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); } }