wp-media / wp-rocket

Performance optimization plugin for WordPress
https://wp-rocket.me
GNU General Public License v2.0
698 stars 217 forks source link

Allow logging WP Rocket's cache clearing #5511

Closed vmanthos closed 1 year ago

vmanthos commented 2 years ago

Is your feature request related to a problem? Please describe.

Quite often, T2s and QA have to log cache clearing, e.g. to tell if the cache was cleared (or not) and what triggered it.

This can be used to troubleshoot cases of excessive cache clearing, for example:

or cases where the cache should be cleared but it isn't:

With the new Preload, this is even more important as excessive cache clearing could be involved in preload running non-stop.

Describe the solution you'd like

Currently, to log cache clearing we have to add error_log() statements with current_filter() to the cache clearing functions in order to tell when and what triggered cached clearing. This can be time-consuming and usually requires a T2.

We can build cache-clearing logging in WP Rocket using Monolog. We should probably allow the generation of a cache-clearing specific log file, e.g. by defining a constant WP_ROCKET_DEBUG_CACHE_CLEARING, instead of using /wp-content/wp-rocket-config/wp-rocket-debug.log.html. This will make reading the file easier.

Describe alternatives you've considered

Currently, we can create a helper plugin that will log when a function runs but not what triggers it. This can be useful in some cases but doesn't guarantee that manual debugging won't be necessary.

CrochetFeve0251 commented 1 year ago

Scope a solution

To solve that issue we will have to add the Logger class into the clearing cache process: Inside the PurgeActionsSubscriber, we will add two new callback that will be registered on the before_rocket_clean_domain and before_rocket_clean_post events with the following content:

public function log_clear_domain() {
   $this->logger->info("clear_domain", [
  'type' => 'cache-clearing',
  'date' => date('Y-m-d H:i:s', current_time( 'timestamp', true )),
  'callstack' => debug_backtrace(),
]);
}

public function log_clear_post($post, $purge_url, $lang) {
$this->logger->info("clear_domain", [
  'type' => 'cache-clearing',
  'date' => date('Y-m-d H:i:s', current_time( 'timestamp', true )),
  'callstack' => debug_backtrace(),
  'url' => get_permalink($post->ID),
  'post_type' => $post->post_type,
  'purged_urls' => $purge_url
]);
}

Concerning the logger we will inject it from the service provider and subscriber will take PSR\Log\LoggerInterface as dependency.

Then we will have to modify the Logger class to handle the specific file on WP_ROCKET_DEBUG_CACHE_CLEARING.

For that we will have to create to create a specific handler that we will add only when the constant is defined in the Logger class:

    public static function get_logger() {
        $logger_name = static::LOGGER_NAME;
        $log_level   = Monologger::DEBUG;
               $handlers = [];
        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 );

               $handlers [] = $handler;

             if ( rocket_get_constant( 'WP_ROCKET_DEBUG_CACHE_CLEARING' , false)  ) {
                 $handler   = new CacheCleanStreamHandler( static::get_cache_clean_log_file_path(), $log_level );
             $handler->setFormatter( $formatter );
                 $handlers [] = $handler;
             } 

        /**
         * 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, $handlers, [ $trace_processor ] );

        // Store the logger.
        Registry::addLogger( $logger );

        return $logger;
    }

The the new stream handler we will just have to extend it from the last one and override the handling method:

class CacheCleanStreamHandler  extends Stream_Handler  {
 public function isHandling(array $record): bool {
    $is_handling = parent::isHandling($record);
    if(! $is_handling) {
     return false;
   }

   if(! key_exists( 'context', $record ) || ! key_exists('type' $record['context'])) {
     return true;
   }
   return cache-clearing' === $record['context']['type'];
  }
}

Estimate the effort

Effort S

piotrbak commented 1 year ago

Closing it after dicussion with @alfonso100

Support developed their own tool for that.

Apfelbiss commented 1 year ago

Support developed their own tool for that.

What do you mean by that? Can this be found anywhere online? I couldn't find it within the wp-rocket-helpers directory.