336 lines
9.1 KiB
PHP
336 lines
9.1 KiB
PHP
<?php
|
|
/**
|
|
* Logger Module
|
|
*
|
|
* Sistema de logging centralizado con soporte para archivos y base de datos
|
|
*
|
|
* @package WP_Debug
|
|
* @since 1.0.0
|
|
*/
|
|
|
|
// Exit if accessed directly
|
|
if (!defined('ABSPATH')) {
|
|
exit;
|
|
}
|
|
|
|
/**
|
|
* WP_Debug_Logger class
|
|
*/
|
|
class WP_Debug_Logger {
|
|
|
|
/**
|
|
* Log levels
|
|
*/
|
|
const LEVEL_INFO = 'INFO';
|
|
const LEVEL_WARNING = 'WARNING';
|
|
const LEVEL_ERROR = 'ERROR';
|
|
const LEVEL_DEBUG = 'DEBUG';
|
|
|
|
/**
|
|
* Log file handle
|
|
*
|
|
* @var resource
|
|
*/
|
|
private static $file_handle = null;
|
|
|
|
/**
|
|
* Initialize logger
|
|
*/
|
|
public static function init() {
|
|
// Hook into shutdown to close file handle
|
|
add_action('shutdown', array(__CLASS__, 'close_file_handle'));
|
|
}
|
|
|
|
/**
|
|
* Log a message
|
|
*
|
|
* @param string $message Message to log
|
|
* @param string $level Log level
|
|
* @param array $context Additional context
|
|
* @return bool True on success
|
|
*/
|
|
public static function log($message, $level = self::LEVEL_INFO, $context = array()) {
|
|
// Check if logging is enabled
|
|
if (!get_option('wp_debug_enabled', true)) {
|
|
return false;
|
|
}
|
|
|
|
// Validate level
|
|
if (!in_array($level, array(self::LEVEL_INFO, self::LEVEL_WARNING, self::LEVEL_ERROR, self::LEVEL_DEBUG))) {
|
|
$level = self::LEVEL_INFO;
|
|
}
|
|
|
|
// Get current log level setting
|
|
$min_level = get_option('wp_debug_log_level', self::LEVEL_INFO);
|
|
if (!self::should_log($level, $min_level)) {
|
|
return false;
|
|
}
|
|
|
|
// Prepare log entry
|
|
$log_entry = self::prepare_log_entry($message, $level, $context);
|
|
|
|
// Log to file
|
|
$log_to_file = get_option('wp_debug_log_to_file', true);
|
|
if ($log_to_file) {
|
|
self::log_to_file($log_entry);
|
|
}
|
|
|
|
// Log to database
|
|
$log_to_db = get_option('wp_debug_log_to_db', true);
|
|
if ($log_to_db) {
|
|
self::log_to_database($log_entry);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Check if message should be logged based on level
|
|
*
|
|
* @param string $level Message level
|
|
* @param string $min_level Minimum level to log
|
|
* @return bool True if should log
|
|
*/
|
|
private static function should_log($level, $min_level) {
|
|
$levels = array(
|
|
self::LEVEL_DEBUG => 0,
|
|
self::LEVEL_INFO => 1,
|
|
self::LEVEL_WARNING => 2,
|
|
self::LEVEL_ERROR => 3,
|
|
);
|
|
|
|
return $levels[$level] >= $levels[$min_level];
|
|
}
|
|
|
|
/**
|
|
* Prepare log entry with all context
|
|
*
|
|
* @param string $message Message
|
|
* @param string $level Level
|
|
* @param array $context Context
|
|
* @return array Log entry
|
|
*/
|
|
private static function prepare_log_entry($message, $level, $context) {
|
|
$user = wp_get_current_user();
|
|
|
|
return array(
|
|
'timestamp' => current_time('mysql'),
|
|
'level' => $level,
|
|
'message' => $message,
|
|
'context' => !empty($context) ? json_encode($context) : null,
|
|
'user_id' => $user->ID,
|
|
'url' => isset($_SERVER['REQUEST_URI']) ? esc_url_raw($_SERVER['REQUEST_URI']) : '',
|
|
'ip_address' => self::get_client_ip(),
|
|
'backtrace' => self::get_backtrace(),
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Log to file
|
|
*
|
|
* @param array $log_entry Log entry
|
|
* @return bool True on success
|
|
*/
|
|
private static function log_to_file($log_entry) {
|
|
$logs_dir = WP_CONTENT_DIR . '/logs/wp-debug';
|
|
$log_file = $logs_dir . '/debug-' . date('Y-m-d') . '.log';
|
|
|
|
// Ensure directory exists
|
|
if (!file_exists($logs_dir)) {
|
|
wp_mkdir_p($logs_dir);
|
|
}
|
|
|
|
// Prepare log line
|
|
$log_line = sprintf(
|
|
"[%s] [%s] %s | User: %s | URL: %s | IP: %s\n",
|
|
$log_entry['timestamp'],
|
|
$log_entry['level'],
|
|
$log_entry['message'],
|
|
$log_entry['user_id'],
|
|
$log_entry['url'],
|
|
$log_entry['ip_address']
|
|
);
|
|
|
|
// Add context if exists
|
|
if ($log_entry['context']) {
|
|
$log_line .= sprintf(" Context: %s\n", $log_entry['context']);
|
|
}
|
|
|
|
// Add backtrace if exists
|
|
if ($log_entry['backtrace']) {
|
|
$log_line .= sprintf(" Backtrace: %s\n", $log_entry['backtrace']);
|
|
}
|
|
|
|
// Write to file
|
|
return file_put_contents($log_file, $log_line, FILE_APPEND | LOCK_EX) !== false;
|
|
}
|
|
|
|
/**
|
|
* Log to database
|
|
*
|
|
* @param array $log_entry Log entry
|
|
* @return bool True on success
|
|
*/
|
|
private static function log_to_database($log_entry) {
|
|
global $wpdb;
|
|
|
|
$table = $wpdb->prefix . 'wp_debug_logs';
|
|
|
|
return $wpdb->insert(
|
|
$table,
|
|
array(
|
|
'timestamp' => $log_entry['timestamp'],
|
|
'level' => $log_entry['level'],
|
|
'message' => $log_entry['message'],
|
|
'context' => $log_entry['context'],
|
|
'user_id' => $log_entry['user_id'],
|
|
'url' => $log_entry['url'],
|
|
'ip_address' => $log_entry['ip_address'],
|
|
),
|
|
array('%s', '%s', '%s', '%s', '%d', '%s', '%s')
|
|
) !== false;
|
|
}
|
|
|
|
/**
|
|
* Get client IP address
|
|
*
|
|
* @return string IP address
|
|
*/
|
|
private static function get_client_ip() {
|
|
$ip = '';
|
|
|
|
if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
|
|
$ip = $_SERVER['HTTP_CLIENT_IP'];
|
|
} elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
|
|
$ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
|
|
} elseif (!empty($_SERVER['REMOTE_ADDR'])) {
|
|
$ip = $_SERVER['REMOTE_ADDR'];
|
|
}
|
|
|
|
return sanitize_text_field($ip);
|
|
}
|
|
|
|
/**
|
|
* Get backtrace
|
|
*
|
|
* @return string Backtrace string
|
|
*/
|
|
private static function get_backtrace() {
|
|
$backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 5);
|
|
$trace_string = '';
|
|
|
|
foreach ($backtrace as $index => $trace) {
|
|
if (isset($trace['file']) && isset($trace['line'])) {
|
|
$trace_string .= sprintf(
|
|
"%s:%s",
|
|
basename($trace['file']),
|
|
$trace['line']
|
|
);
|
|
if ($index < count($backtrace) - 1) {
|
|
$trace_string .= ' -> ';
|
|
}
|
|
}
|
|
}
|
|
|
|
return $trace_string;
|
|
}
|
|
|
|
/**
|
|
* Get logs from database
|
|
*
|
|
* @param array $args Query arguments
|
|
* @return array Logs
|
|
*/
|
|
public static function get_logs($args = array()) {
|
|
global $wpdb;
|
|
|
|
$defaults = array(
|
|
'level' => null,
|
|
'limit' => 100,
|
|
'offset' => 0,
|
|
'orderby' => 'timestamp',
|
|
'order' => 'DESC',
|
|
);
|
|
|
|
$args = wp_parse_args($args, $defaults);
|
|
|
|
$table = $wpdb->prefix . 'wp_debug_logs';
|
|
$where = '1=1';
|
|
|
|
if ($args['level']) {
|
|
$where .= $wpdb->prepare(' AND level = %s', $args['level']);
|
|
}
|
|
|
|
$query = $wpdb->prepare(
|
|
"SELECT * FROM {$table} WHERE {$where} ORDER BY {$args['orderby']} {$args['order']} LIMIT %d OFFSET %d",
|
|
$args['limit'],
|
|
$args['offset']
|
|
);
|
|
|
|
return $wpdb->get_results($query);
|
|
}
|
|
|
|
/**
|
|
* Clear old logs
|
|
*
|
|
* @param int $days Days to keep
|
|
* @return bool True on success
|
|
*/
|
|
public static function clear_old_logs($days = null) {
|
|
if ($days === null) {
|
|
$days = get_option('wp_debug_log_retention_days', 7);
|
|
}
|
|
|
|
// Clear database logs
|
|
global $wpdb;
|
|
$table = $wpdb->prefix . 'wp_debug_logs';
|
|
$date = date('Y-m-d H:i:s', strtotime("-{$days} days"));
|
|
|
|
$wpdb->query($wpdb->prepare("DELETE FROM {$table} WHERE timestamp < %s", $date));
|
|
|
|
// Clear file logs
|
|
$logs_dir = WP_CONTENT_DIR . '/logs/wp-debug';
|
|
if (file_exists($logs_dir)) {
|
|
$files = glob($logs_dir . '/debug-*.log');
|
|
$cutoff = strtotime("-{$days} days");
|
|
|
|
foreach ($files as $file) {
|
|
if (filemtime($file) < $cutoff) {
|
|
unlink($file);
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Close file handle on shutdown
|
|
*/
|
|
public static function close_file_handle() {
|
|
if (self::$file_handle) {
|
|
fclose(self::$file_handle);
|
|
self::$file_handle = null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Convenience methods for different log levels
|
|
*/
|
|
public static function info($message, $context = array()) {
|
|
return self::log($message, self::LEVEL_INFO, $context);
|
|
}
|
|
|
|
public static function warning($message, $context = array()) {
|
|
return self::log($message, self::LEVEL_WARNING, $context);
|
|
}
|
|
|
|
public static function error($message, $context = array()) {
|
|
return self::log($message, self::LEVEL_ERROR, $context);
|
|
}
|
|
|
|
public static function debug($message, $context = array()) {
|
|
return self::log($message, self::LEVEL_DEBUG, $context);
|
|
}
|
|
}
|