222 lines
7.4 KiB
PHP
222 lines
7.4 KiB
PHP
|
|
<?php
|
|||
|
|
/**
|
|||
|
|
* Real-time Execution Logger for WordPress
|
|||
|
|
* Логирует выполнение страницы в реальном времени
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
class RealtimePageLogger {
|
|||
|
|
private static $instance;
|
|||
|
|
private $log_file;
|
|||
|
|
private $request_id;
|
|||
|
|
private $start_time;
|
|||
|
|
|
|||
|
|
public static function get_instance() {
|
|||
|
|
if (null === self::$instance) {
|
|||
|
|
self::$instance = new self();
|
|||
|
|
}
|
|||
|
|
return self::$instance;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private function __construct() {
|
|||
|
|
$this->log_file = WP_CONTENT_DIR . '/realtime-debug.log';
|
|||
|
|
$this->request_id = uniqid('req_', true);
|
|||
|
|
$this->start_time = microtime(true);
|
|||
|
|
|
|||
|
|
// Очищаем файл при инициализации
|
|||
|
|
$this->clean_log_file();
|
|||
|
|
|
|||
|
|
$this->init();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Очистка лог-файла с проверками
|
|||
|
|
*/
|
|||
|
|
private function clean_log_file() {
|
|||
|
|
// Если файл не существует, создаем его
|
|||
|
|
if (!file_exists($this->log_file)) {
|
|||
|
|
if (touch($this->log_file)) {
|
|||
|
|
chmod($this->log_file, 0644);
|
|||
|
|
$this->log_message("Создан новый лог-файл", 'SYSTEM');
|
|||
|
|
} else {
|
|||
|
|
error_log('Не удалось создать файл realtime-debug.log');
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Проверяем доступность для записи
|
|||
|
|
if (!is_writable($this->log_file)) {
|
|||
|
|
error_log('Файл realtime-debug.log недоступен для записи');
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Очищаем файл
|
|||
|
|
if (file_put_contents($this->log_file, '') === false) {
|
|||
|
|
error_log('Не удалось очистить файл realtime-debug.log');
|
|||
|
|
} else {
|
|||
|
|
$this->log_message("Лог-файл очищен", 'SYSTEM');
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private function init() {
|
|||
|
|
// Начинаем логирование как можно раньше
|
|||
|
|
add_action('plugins_loaded', [$this, 'log_plugins_loaded'], 1);
|
|||
|
|
add_action('setup_theme', [$this, 'log_setup_theme'], 1);
|
|||
|
|
add_action('after_setup_theme', [$this, 'log_after_setup_theme'], 1);
|
|||
|
|
add_action('init', [$this, 'log_init'], 1);
|
|||
|
|
|
|||
|
|
// Логируем все основные этапы
|
|||
|
|
add_action('wp_loaded', [$this, 'log_wp_loaded']);
|
|||
|
|
add_action('parse_query', [$this, 'log_parse_query']);
|
|||
|
|
add_action('pre_get_posts', [$this, 'log_pre_get_posts']);
|
|||
|
|
add_action('wp', [$this, 'log_wp']);
|
|||
|
|
|
|||
|
|
// Логируем SQL запросы
|
|||
|
|
add_filter('query', [$this, 'log_sql_query']);
|
|||
|
|
|
|||
|
|
// Логируем шаблоны
|
|||
|
|
add_filter('template_include', [$this, 'log_template_include'], 9999);
|
|||
|
|
|
|||
|
|
// Логируем завершение
|
|||
|
|
add_action('shutdown', [$this, 'log_shutdown'], 9999);
|
|||
|
|
|
|||
|
|
// Логируем хуки в реальном времени
|
|||
|
|
$this->setup_hook_logging();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public function setup_hook_logging() {
|
|||
|
|
$important_hooks = [
|
|||
|
|
'template_redirect',
|
|||
|
|
'get_header',
|
|||
|
|
'wp_head',
|
|||
|
|
'the_post',
|
|||
|
|
'loop_start',
|
|||
|
|
'loop_end',
|
|||
|
|
'get_sidebar',
|
|||
|
|
'get_footer',
|
|||
|
|
'wp_footer',
|
|||
|
|
'admin_bar_menu',
|
|||
|
|
'wp_enqueue_scripts',
|
|||
|
|
'wp_print_styles',
|
|||
|
|
'wp_print_scripts'
|
|||
|
|
];
|
|||
|
|
|
|||
|
|
foreach ($important_hooks as $hook) {
|
|||
|
|
add_action($hook, function() use ($hook) {
|
|||
|
|
$this->log_hook($hook);
|
|||
|
|
}, 1);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private function log_message($message, $level = 'INFO') {
|
|||
|
|
$timestamp = microtime(true);
|
|||
|
|
$elapsed = round(($timestamp - $this->start_time) * 1000, 2);
|
|||
|
|
$memory = memory_get_usage(true);
|
|||
|
|
|
|||
|
|
$log = sprintf(
|
|||
|
|
"[%s] %s | %6.2fms | %8s | %s | %s\n",
|
|||
|
|
date('H:i:s'),
|
|||
|
|
$this->request_id,
|
|||
|
|
$elapsed,
|
|||
|
|
size_format($memory, 0),
|
|||
|
|
$level,
|
|||
|
|
$message
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
file_put_contents($this->log_file, $log, FILE_APPEND);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public function log_plugins_loaded() {
|
|||
|
|
$this->log_message('PLUGINS_LOADED - Плагины загружены');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public function log_setup_theme() {
|
|||
|
|
$this->log_message('SETUP_THEME - Начинается загрузка темы');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public function log_after_setup_theme() {
|
|||
|
|
$this->log_message('AFTER_SETUP_THEME - Тема загружена');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public function log_init() {
|
|||
|
|
$this->log_message('INIT - WordPress инициализирован');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public function log_wp_loaded() {
|
|||
|
|
$this->log_message('WP_LOADED - WordPress полностью загружен');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public function log_parse_query() {
|
|||
|
|
global $wp_query;
|
|||
|
|
$this->log_message(sprintf('PARSE_QUERY - Запрос: %s', $wp_query->query_vars['pagename'] ?? 'main'));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public function log_pre_get_posts($query) {
|
|||
|
|
if ($query->is_main_query()) {
|
|||
|
|
$this->log_message('PRE_GET_POSTS - Основной запрос к posts');
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public function log_wp() {
|
|||
|
|
$this->log_message('WP - Запрос обработан, готовим данные для шаблона');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public function log_sql_query($query) {
|
|||
|
|
$trimmed = trim($query);
|
|||
|
|
if (!empty($trimmed) && !str_starts_with($trimmed, '/*')) {
|
|||
|
|
//$short_query = substr($trimmed, 0, 150);
|
|||
|
|
//if (strlen($trimmed) > 150) {
|
|||
|
|
// $short_query .= '...';
|
|||
|
|
// }
|
|||
|
|
$this->log_message("SQL: {$query}", 'SQL');
|
|||
|
|
}
|
|||
|
|
return $query;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public function log_hook($hook) {
|
|||
|
|
$backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 4);
|
|||
|
|
$caller = $this->get_caller_info($backtrace);
|
|||
|
|
$this->log_message("HOOK: {$hook} → {$caller}", 'HOOK');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public function log_template_include($template) {
|
|||
|
|
$template_name = basename($template);
|
|||
|
|
$this->log_message("TEMPLATE: {$template_name}", 'TEMPLATE');
|
|||
|
|
return $template;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public function log_shutdown() {
|
|||
|
|
$total_time = round((microtime(true) - $this->start_time) * 1000, 2);
|
|||
|
|
$peak_memory = memory_get_peak_usage(true);
|
|||
|
|
|
|||
|
|
$this->log_message("SHUTDOWN - Завершение работы ({$total_time}ms, " .
|
|||
|
|
size_format($peak_memory) . ")", 'FINISH');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private function get_caller_info($backtrace) {
|
|||
|
|
foreach ($backtrace as $trace) {
|
|||
|
|
if (isset($trace['file']) &&
|
|||
|
|
!str_contains($trace['file'], 'wp-includes') &&
|
|||
|
|
!str_contains($trace['file'], 'wp-admin')) {
|
|||
|
|
$file = basename($trace['file']);
|
|||
|
|
$line = $trace['line'] ?? 'unknown';
|
|||
|
|
return "{$file}:{$line}";
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return 'unknown';
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Инициализируем логгер только если включен дебаг
|
|||
|
|
//if (defined('WP_DEBUG') && WP_DEBUG) {
|
|||
|
|
RealtimePageLogger::get_instance();
|
|||
|
|
//}
|
|||
|
|
|
|||
|
|
// Функция для очистки лога
|
|||
|
|
add_action('wp_ajax_clear_realtime_log', 'clear_realtime_debug_log');
|
|||
|
|
function clear_realtime_debug_log() {
|
|||
|
|
if (current_user_can('administrator')) {
|
|||
|
|
file_put_contents(WP_CONTENT_DIR . '/realtime-debug.log', '');
|
|||
|
|
echo "Realtime log cleared!";
|
|||
|
|
}
|
|||
|
|
wp_die();
|
|||
|
|
}
|