tail($uploads_log_file); } // Fallback $local_log_file = AFFILILABS_LOG_PATH . $log_file_name; if (file_exists($local_log_file)) { return $this->tail($local_log_file); } return esc_html__('No logs found.', 'affililabs'); } public function get_general_log() { if (!defined('WP_CONTENT_DIR')) { return esc_html__('WP_CONTENT_DIR is not defined. Cannot locate general log file.', 'affililabs'); } $general_log_file = WP_CONTENT_DIR . '/debug.log'; if (file_exists($general_log_file)) { return $this->tail($general_log_file); } return esc_html__('No general WordPress/PHP logs found.', 'affililabs'); } /** * Reads the last N lines of a file efficiently. * * @param string $filepath Path to the file. * @param int $lines Number of lines to retrieve. * @return string|false The last N lines or false on failure. */ private function tail($filepath, $lines = 1000) { if (!file_exists($filepath) || !is_readable($filepath)) { return false; } $f = fopen($filepath, 'rb'); if ($f === false) { return false; } $chunk = 4096; fseek($f, 0, SEEK_END); $pos = ftell($f); // Failsafe: Prevent memory exhaustion on files with no newlines // E.g., Stop reading after 10MB backward $max_bytes_to_read = 100 * 1024 * 1024; $bytes_read = 0; $line_count = 0; $chunks = []; // Read backward in chunks until we have enough lines while ($pos > 0 && $line_count <= $lines && $bytes_read < $max_bytes_to_read) { $read_size = min($pos, $chunk); $pos -= $read_size; fseek($f, $pos); $data = fread($f, $read_size); $bytes_read += $read_size; // Count both \n and \r to handle Unix, Windows, and Mac line endings safely $line_count += substr_count($data, "\n"); if (strpos($data, "\n") === false) { $line_count += substr_count($data, "\r"); } $chunks[] = $data; } fclose($f); // Reassemble the read chunks efficiently $output = implode('', array_reverse($chunks)); // Normalize line endings to \n for clean splitting $output = str_replace("\r\n", "\n", $output); $output = str_replace("\r", "\n", $output); $output_lines = explode("\n", $output); if (end($output_lines) === '') { array_pop($output_lines); } if (count($output_lines) > $lines) { $output_lines = array_slice($output_lines, -$lines); } return implode("\n", $output_lines); } }