# xspeed/1.0.6/includes/class-activity-log.php

xSpeed Cache: AI-Powered Performance Hub with MCP, Caching &amp; CDN, version 1.0.6. 89 lines.

- Page: https://pluginprobe.com/plugins/xspeed/1.0.6/code/includes/class-activity-log.php
- Raw: https://pluginprobe.com/plugins/xspeed/1.0.6/raw/includes/class-activity-log.php
- Modified: 2026-06-01T17:33:22+00:00

Line numbers below start at 1. Link to a line or a range by appending a fragment to the
page URL, for example `https://pluginprobe.com/plugins/xspeed/1.0.6/code/includes/class-activity-log.php#L10-L20`.

```php
<?php
/**
 * Activity_Log — capped, append-only log of cache lifecycle events.
 *
 * Used by the Health module's dashboard panel + (later phases) by audit
 * surfaces. Storage: one transient `xspeed_activity_log` containing at
 * most MAX_ENTRIES events, ordered newest-first. Transient TTL is long
 * (30 days) so we don't lose history during quiet periods, but the size
 * cap keeps it bounded.
 *
 * Event shape:
 *   [ ts => int, type => string, message => string, severity => 'info' |
 *     'warn' | 'error' | 'success' ]
 *
 * Callers (from Cache.php and elsewhere):
 *   Activity_Log::record('cache_purged', 'Cache purged (settings change)');
 *   Activity_Log::record('cache_enabled_event', 'Cache enabled', 'success');
 *   Activity_Log::record('conflict_detected', 'WP Rocket activated', 'warn');
 *
 * Event type ids are snake_case — sanitize_key (called on the way in)
 * strips dots, so dotted ids would collapse silently.
 *
 * @package XSpeed
 */

declare(strict_types=1);

namespace XSpeed;

defined( 'ABSPATH' ) || exit;

final class Activity_Log {

	public const TRANSIENT_KEY = 'xspeed_activity_log';
	public const TTL           = 2592000; // 30 days
	public const MAX_ENTRIES   = 50;

	public const INFO    = 'info';
	public const WARN    = 'warn';
	public const ERROR   = 'error';
	public const SUCCESS = 'success';

	public static function record( string $type, string $message, string $severity = self::INFO ): void {
		$entries = self::entries();
		array_unshift(
			$entries,
			array(
				'ts'       => time(),
				'type'     => sanitize_key( $type ),
				'message'  => $message, // caller must produce already-safe text.
				'severity' => in_array( $severity, array( self::INFO, self::WARN, self::ERROR, self::SUCCESS ), true ) ? $severity : self::INFO,
			)
		);
		if ( count( $entries ) > self::MAX_ENTRIES ) {
			$entries = array_slice( $entries, 0, self::MAX_ENTRIES );
		}
		set_transient( self::TRANSIENT_KEY, $entries, self::TTL );
	}

	/**
	 * Newest-first entries (up to MAX_ENTRIES). Defensive shape coercion
	 * so a malformed transient never breaks the dashboard.
	 *
	 * @return array<int,array{ts:int,type:string,message:string,severity:string}>
	 */
	public static function entries(): array {
		$raw = get_transient( self::TRANSIENT_KEY );
		if ( ! is_array( $raw ) ) {
			return array();
		}
		$out = array();
		foreach ( $raw as $e ) {
			if ( is_array( $e ) && isset( $e['ts'], $e['type'], $e['message'] ) ) {
				$out[] = array(
					'ts'       => (int) $e['ts'],
					'type'     => (string) $e['type'],
					'message'  => (string) $e['message'],
					'severity' => isset( $e['severity'] ) ? (string) $e['severity'] : self::INFO,
				);
			}
		}
		return $out;
	}

	public static function clear(): void {
		delete_transient( self::TRANSIENT_KEY );
	}
}

```
