文件操作 - Logger.php
返回文件管理
返回主菜单
删除本文件
文件: /storage/v12552/stanleysteamer/public_html/wp-content/plugins/wp-rocket/inc/Logger/Logger.php
编辑文件内容
<?php namespace WP_Rocket\Logger; use WP_Rocket\Dependencies\Monolog\Logger as Monologger; use WP_Rocket\Dependencies\Monolog\Registry; use WP_Rocket\Dependencies\Monolog\Processor\IntrospectionProcessor; use WP_Rocket\Dependencies\Monolog\Handler\StreamHandler as MonoStreamHandler; use WP_Rocket\Dependencies\Monolog\Formatter\LineFormatter; use WP_Rocket\Logger\{HTMLFormatter, StreamHandler}; /** * Class used to log events. * * @since 3.1.4 * @since 3.2 Changed namespace from \WP_Rocket to \WP_Rocket\Logger. * @author Grégory Viguier */ class Logger { /** * Logger name. * * @var string */ const LOGGER_NAME = 'wp_rocket'; /** * Name of the logs file. * * @var string */ const LOG_FILE_NAME = 'wp-rocket-debug.log.html'; /** * A unique ID given to the current thread. * * @var string */ private static $thread_id; /** * Cached debug enabled status for the current request. * * @var bool|null */ private static $debug_enabled_cache = null; /** ----------------------------------------------------------------------------------------- */ /** LOG ===================================================================================== */ /** ----------------------------------------------------------------------------------------- */ /** * Adds a log record at the DEBUG level. * * @since 3.1.4 * * @param string $message The log message. * @param array $context The log context. * @return bool|null Whether the record has been processed. */ public static function debug( $message, array $context = [] ) { return static::debug_enabled() ? static::get_logger()->debug( $message, $context ) : null; } /** * Adds a log record at the INFO level. * * @since 3.1.4 * * @param string $message The log message. * @param array $context The log context. * @return bool|null Whether the record has been processed. */ public static function info( $message, array $context = [] ) { return static::debug_enabled() ? static::get_logger()->info( $message, $context ) : null; } /** * Adds a log record at the NOTICE level. * * @since 3.1.4 * * @param string $message The log message. * @param array $context The log context. * @return bool|null Whether the record has been processed. */ public static function notice( $message, array $context = [] ) { return static::debug_enabled() ? static::get_logger()->notice( $message, $context ) : null; } /** * Adds a log record at the WARNING level. * * @since 3.1.4 * * @param string $message The log message. * @param array $context The log context. * @return bool|null Whether the record has been processed. */ public static function warning( $message, array $context = [] ) { return static::debug_enabled() ? static::get_logger()->warning( $message, $context ) : null; } /** * Adds a log record at the ERROR level. * * @since 3.1.4 * * @param string $message The log message. * @param array $context The log context. * @return bool|null Whether the record has been processed. */ public static function error( $message, array $context = [] ) { return static::debug_enabled() ? static::get_logger()->error( $message, $context ) : null; } /** * Adds a log record at the CRITICAL level. * * @since 3.1.4 * * @param string $message The log message. * @param array $context The log context. * @return bool|null Whether the record has been processed. */ public static function critical( $message, array $context = [] ) { return static::debug_enabled() ? static::get_logger()->critical( $message, $context ) : null; } /** * Adds a log record at the ALERT level. * * @since 3.1.4 * * @param string $message The log message. * @param array $context The log context. * @return bool|null Whether the record has been processed. */ public static function alert( $message, array $context = [] ) { return static::debug_enabled() ? static::get_logger()->alert( $message, $context ) : null; } /** * Adds a log record at the EMERGENCY level. * * @since 3.1.4 * * @param string $message The log message. * @param array $context The log context. * @return bool|null Whether the record has been processed. */ public static function emergency( $message, array $context = [] ) { return static::debug_enabled() ? static::get_logger()->emergency( $message, $context ) : null; } /** * Get the logger instance. * * @since 3.1.4 * * @return Logger A Logger instance. */ public static function get_logger() { $logger_name = static::LOGGER_NAME; $log_level = Monologger::DEBUG; if ( Registry::hasLogger( $logger_name ) ) { return Registry::$logger_name(); } /** * File handler. * HTML formatter is used. */ $handler = new StreamHandler( static::get_log_file_path(), $log_level ); $formatter = new HtmlFormatter(); $handler->setFormatter( $formatter ); /** * Thanks to the processors, add data to each log: * - `debug_backtrace()` (exclude this class and Abstract_Buffer). */ $trace_processor = new IntrospectionProcessor( $log_level, [ get_called_class(), 'Abstract_Buffer' ] ); // Create the logger. $logger = new Monologger( $logger_name, [ $handler ], [ $trace_processor ] ); // Store the logger. Registry::addLogger( $logger ); return $logger; } /** ----------------------------------------------------------------------------------------- */ /** LOG FILE ================================================================================ */ /** ----------------------------------------------------------------------------------------- */ /** * Get the path to the log file. * * @since 3.1.4 * * @return string */ public static function get_log_file_path() { if ( defined( 'WP_ROCKET_DEBUG_LOG_FILE' ) && WP_ROCKET_DEBUG_LOG_FILE && is_string( WP_ROCKET_DEBUG_LOG_FILE ) ) { // Make sure the file uses a ".log" extension. return preg_replace( '/\.[^.]*$/', '', WP_ROCKET_DEBUG_LOG_FILE ) . '.log'; } if ( defined( 'WP_ROCKET_DEBUG_INTERVAL' ) ) { // Adds an optional logs rotator depending on a constant value - WP_ROCKET_DEBUG_INTERVAL (interval by minutes). $rotator = str_pad( round( ( strtotime( 'now' ) - strtotime( 'today midnight' ) ) / 60 / WP_ROCKET_DEBUG_INTERVAL ), 4, '0', STR_PAD_LEFT ); return WP_CONTENT_DIR . '/wp-rocket-config/' . $rotator . '-' . static::LOG_FILE_NAME; } else { return WP_CONTENT_DIR . '/wp-rocket-config/' . static::LOG_FILE_NAME; } } /** * Get the log file contents. * * @since 3.1.4 * * @return string|object The file contents on success. A WP_Error object on failure. */ public static function get_log_file_contents() { $filesystem = \rocket_direct_filesystem(); $file_path = static::get_log_file_path(); if ( ! $filesystem->exists( $file_path ) ) { return new \WP_Error( 'no_file', __( 'The log file does not exist.', 'rocket' ) ); } $contents = $filesystem->get_contents( $file_path ); if ( false === $contents ) { return new \WP_Error( 'file_not_read', __( 'The log file could not be read.', 'rocket' ) ); } return $contents; } /** * Get the log file size and number of entries. * * @since 3.1.4 * * @return array|object An array of statistics on success. A WP_Error object on failure. */ public static function get_log_file_stats() { $formatter = static::get_stream_formatter(); if ( ! $formatter ) { return new \WP_Error( 'no_stream_formatter', __( 'The logs are not saved into a file.', 'rocket' ) ); } $filesystem = \rocket_direct_filesystem(); $file_path = static::get_log_file_path(); if ( ! $filesystem->exists( $file_path ) ) { return new \WP_Error( 'no_file', __( 'The log file does not exist.', 'rocket' ) ); } $contents = $filesystem->get_contents( $file_path ); if ( false === $contents ) { return new \WP_Error( 'file_not_read', __( 'The log file could not be read.', 'rocket' ) ); } if ( $formatter instanceof HtmlFormatter ) { $entries = preg_split( '@<h1 @', $contents ); } elseif ( $formatter instanceof LineFormatter ) { $entries = preg_split( '@^\[\d{4,}-\d{2,}-\d{2,} \d{2,}:\d{2,}:\d{2,}] @m', $contents ); } else { $entries = 0; } $entries = $entries ? number_format_i18n( count( $entries ) ) : '0'; $bytes = $filesystem->size( $file_path ); $raw_size = $bytes; $decimals = $bytes > pow( 1024, 3 ) ? 1 : 0; $bytes = @size_format( $bytes, $decimals ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged $bytes = str_replace( ' ', ' ', $bytes ); // Non-breaking space character. return compact( 'entries', 'bytes', 'raw_size' ); } /** * Get the log file extension related to the formatter in use. This can be used when the file is downloaded. * * @since 3.1.4 * * @return string The corresponding file extension with the heading dot. */ public static function get_log_file_extension() { $formatter = static::get_stream_formatter(); if ( ! $formatter ) { return '.log'; } if ( $formatter instanceof HtmlFormatter ) { return '.html'; } if ( $formatter instanceof LineFormatter ) { return '.txt'; } return '.log'; } /** * Delete the log file. * * @since 3.1.4 * * @return bool True on success. False on failure. */ public static function delete_log_file() { $filesystem = \rocket_direct_filesystem(); $file_path = static::get_log_file_path(); if ( ! $filesystem->exists( $file_path ) ) { return true; } $filesystem->put_contents( $file_path, '' ); $filesystem->delete( $file_path, false, 'f' ); return ! $filesystem->exists( $file_path ); } /** * Get the handler used for the log file. * * @since 3.2 * * @return object|bool The formatter object on success. False on failure. */ public static function get_stream_handler() { $handlers = static::get_logger()->getHandlers(); if ( ! $handlers ) { return false; } foreach ( $handlers as $_handler ) { if ( $_handler instanceof MonoStreamHandler ) { $handler = $_handler; break; } } if ( empty( $handler ) ) { return false; } return $handler; } /** * Get the formatter used for the log file. * * @since 3.1.4 * * @return object|bool The formatter object on success. False on failure. */ public static function get_stream_formatter() { $handler = static::get_stream_handler(); if ( empty( $handler ) ) { return false; } return $handler->getFormatter(); } /** ----------------------------------------------------------------------------------------- */ /** CONSTANT ================================================================================ */ /** ----------------------------------------------------------------------------------------- */ /** * Tell if debug is enabled. * * Supports multiple formats for WP_ROCKET_DEBUG: * - Boolean: true enables logging for all URLs (backward compatible) * Example: define( 'WP_ROCKET_DEBUG', true ); * * - String: Logs only the specified URL * Example: define( 'WP_ROCKET_DEBUG', 'https://example.com/page' ); * Example: define( 'WP_ROCKET_DEBUG', '/page' ); // Relative path * * - Array: Logs URLs matching any pattern in the array * Example: define( 'WP_ROCKET_DEBUG', [ '/page-1/', '/page-2/', '/page-3/' ] ); * * URL matching behavior: * - Trailing slashes are ignored: '/page' matches '/page/' * - Query strings are ignored: '/page' matches '/page?param=value' * - Case-insensitive: '/Page' matches '/page' * - Homepage can be matched with '/' or full site URL * - Works with both relative and absolute URLs * * @since 3.1.4 * * @return bool */ public static function debug_enabled() { if ( null !== self::$debug_enabled_cache ) { return self::$debug_enabled_cache; } $debug_enabled = false; $debug_config = defined( 'WP_ROCKET_DEBUG' ) ? constant( 'WP_ROCKET_DEBUG' ) : false; if ( true === $debug_config ) { $debug_enabled = true; } elseif ( is_string( $debug_config ) || is_array( $debug_config ) ) { // URL-based filtering. $current_url = self::get_current_request_url(); $debug_enabled = self::url_matches_debug_patterns( $current_url, $debug_config ); } // Cache the result. self::$debug_enabled_cache = $debug_enabled; /** * Fires before checking if debug is enabled for the logger. * * @param boolean $debug_status Returns if debug is enabled. * * @since 3.20.4 */ do_action( 'rocket_before_debug_status_check', $debug_enabled ); return $debug_enabled; } /** * Normalize a URL for comparison. * * Strips trailing slashes, query strings, fragments, and converts to lowercase. * Handles both relative and absolute URLs. * * @param string $url URL to normalize. * @return array Array with 'path' and 'host' keys. */ private static function normalize_url( $url ) { if ( empty( $url ) || ! is_string( $url ) ) { return [ 'path' => '', 'host' => '', ]; } $parsed = parse_url( $url ); // phpcs:ignore WordPress.WP.AlternativeFunctions.parse_url_parse_url if ( ! $parsed ) { return [ 'path' => '', 'host' => '', ]; } // Extract path, defaulting to '/' for homepage. $path = isset( $parsed['path'] ) ? $parsed['path'] : '/'; // Remove trailing slash (except for root). $path = '/' === $path ? $path : rtrim( $path, '/' ); $path = strtolower( $path ); // Extract and normalize host. $host = isset( $parsed['host'] ) ? strtolower( $parsed['host'] ) : ''; return [ 'path' => $path, 'host' => $host, ]; } /** * Get the current request URL. * * Builds URL from REQUEST_URI and home_url(), normalizes it. * * @return array Normalized current request URL with 'path' and 'host'. */ private static function get_current_request_url() { // Sanitize REQUEST_URI without WordPress functions. $request_uri = isset( $_SERVER['REQUEST_URI'] ) ? strip_tags( stripslashes( $_SERVER['REQUEST_URI'] ) ) // phpcs:ignore WordPress.WP.AlternativeFunctions.strip_tags_strip_tags, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.ValidatedSanitizedInput.MissingUnslash : '/'; // Get host from server variables (portable, no WordPress dependency). $host = ''; if ( isset( $_SERVER['HTTP_HOST'] ) ) { $host = strtolower( stripslashes( $_SERVER['HTTP_HOST'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.ValidatedSanitizedInput.MissingUnslash } elseif ( isset( $_SERVER['SERVER_NAME'] ) ) { $host = strtolower( stripslashes( $_SERVER['SERVER_NAME'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.ValidatedSanitizedInput.MissingUnslash } // Normalize the request URI. $normalized = self::normalize_url( $request_uri ); return [ 'path' => $normalized['path'], 'host' => $host, ]; } /** * Check if current URL matches debug patterns. * * @param array $current_url Current request URL with 'path' and 'host'. * @param string|array $patterns Debug pattern(s) to match against. * @return bool True if URL matches any pattern, false otherwise. */ private static function url_matches_debug_patterns( $current_url, $patterns ) { // Handle empty patterns. if ( empty( $patterns ) ) { return false; } // Convert single string to array for uniform processing. if ( ! is_array( $patterns ) ) { $patterns = [ $patterns ]; } // Check each pattern. foreach ( $patterns as $pattern ) { if ( ! is_string( $pattern ) || '' === $pattern ) { continue; } $normalized_pattern = self::normalize_url( $pattern ); // If pattern has a host (absolute URL), validate domain matches. if ( ! empty( $normalized_pattern['host'] ) ) { // Domain must match current site's domain. if ( $normalized_pattern['host'] !== $current_url['host'] ) { continue; } } // Compare paths. if ( $current_url['path'] === $normalized_pattern['path'] ) { return true; } } return false; } /** * Enable debug mode by adding a constant in the `wp-config.php` file. * * @since 3.1.4 */ public static function enable_debug() { static::define_debug( true ); } /** * Disable debug mode by removing the constant in the `wp-config.php` file. * * @since 3.1.4 */ public static function disable_debug() { static::define_debug( false ); } /** * Enable or disable debug mode by adding or removing a constant in the `wp-config.php` file. * * @since 3.1.4 * * @param bool $enable True to enable debug, false to disable. */ public static function define_debug( $enable ) { if ( $enable && static::debug_enabled() ) { // Debug is already enabled. return; } if ( ! $enable && ! static::debug_enabled() ) { // Debug is already disabled. return; } // Get the path to the file. $file_path = \rocket_find_wpconfig_path(); if ( ! $file_path ) { // Couldn't get the path to the file. return; } // Get the content of the file. $filesystem = \rocket_direct_filesystem(); $content = $filesystem->get_contents( $file_path ); if ( false === $content ) { // Couldn't get the content of the file. return; } // Remove previous value. $placeholder = '## WP_ROCKET_DEBUG placeholder ##'; $content = preg_replace( '@^[\t ]*define\s*\(\s*["\']WP_ROCKET_DEBUG["\'].*$@miU', $placeholder, $content ); $content = preg_replace( "@\n$placeholder@", '', $content ); if ( $enable ) { // Add the constant. $define = "define( 'WP_ROCKET_DEBUG', true ); // Added by WP Rocket.\r\n"; $content = preg_replace( '@<\?php\s*@i', "<?php\n$define", $content, 1 ); } // Save the file. $chmod = rocket_get_filesystem_perms( 'file' ); $filesystem->put_contents( $file_path, $content, $chmod ); } /** ----------------------------------------------------------------------------------------- */ /** TOOLS =================================================================================== */ /** ----------------------------------------------------------------------------------------- */ /** * Get the thread identifier. * * @since 3.3 * * @return string */ public static function get_thread_id() { if ( ! isset( self::$thread_id ) ) { self::$thread_id = uniqid( '', true ); } return self::$thread_id; } /** * Remove cookies related to WP auth. * * @since 3.1.4 * * @param array $cookies An array of cookies. * @return array */ public static function remove_auth_cookies( $cookies = [] ) { if ( ! $cookies || ! is_array( $cookies ) ) { $cookies = $_COOKIE; } unset( $cookies['wordpress_test_cookie'] ); if ( ! $cookies ) { return []; } $pattern = strtolower( '@^WordPress(?:user|pass|_sec|_logged_in)?_@' ); // Trolling PHPCS. foreach ( $cookies as $cookie_name => $value ) { if ( preg_match( $pattern, $cookie_name ) ) { $cookies[ $cookie_name ] = 'Value removed by WP Rocket.'; } } return $cookies; } /** * Automatically deletes the log file if it exceeds a maximum file size. * * The maximum file size before deletion is controlled by the 'rocket_debug_log_auto_delete_max_file_size' filter, * with a default of 30,000,000 bytes (approximately 30MB). * * @param bool $debug_enabled Whether debug is enabled and log file auto-delete should proceed. * @return void */ public static function maybe_delete_log_file( $debug_enabled ): void { // Bail out if debug is not enabled. if ( ! $debug_enabled ) { return; } // Bail out if debug.log file does not exist. if ( ! rocket_direct_filesystem()->exists( self::get_log_file_path() ) ) { return; } // Bail out if transient cache is still valid. if ( get_transient( 'wp_rocket_log_file_size_check' ) ) { return; } // Do transient cache for one hour. set_transient( 'wp_rocket_log_file_size_check', true, HOUR_IN_SECONDS ); /** * Filters the maximum file size (in bytes) before the log file is automatically deleted. * * @param int $max_file_size The maximum file size in bytes. Default is 30,000,000 (30MB). * * @since 3.20.4 */ $max_file_size = wpm_apply_filters_typed( 'integer', 'rocket_debug_log_auto_delete_max_file_size', 30000000 ); $log_file_stats = self::get_log_file_stats(); // Bail out if there is an error getting the log file stats. if ( is_wp_error( $log_file_stats ) ) { return; } $log_file_size = $log_file_stats['raw_size']; if ( $log_file_size < $max_file_size ) { return; } self::delete_log_file(); } }
修改文件时间
将文件时间修改为当前时间的前一年
删除文件