Files
2meet-data-optimizer/includes/class-tmdo-logger.php
T
wpdev 76c01e44df refactor: 全部 128 個生產檔加入 declare(strict_types=1)(PR-H)
對齊 A v3.2.0。型別強制會把隱式轉換變成 TypeError,所以一次全檔加入
並跑完整測試(unit 451 / integration 398 全綠,無迴歸)。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TbG1keQQ7XBa7qMQY16KCY
2026-07-31 06:13:33 +08:00

163 lines
5.1 KiB
PHP

<?php
/**
* Error logging for WP Data Optimizer.
*
* @package WP_Data_Optimizer
*/
declare(strict_types=1);
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Lightweight error logger — writes to wpdo_errors table and PHP error_log.
*/
class TMDO_Logger {
/**
* Log an INFO-level event (event-based signature, used by v2.0.0 engine code).
*
* Writes only to PHP error_log; does NOT touch wpdo_errors (which is reserved
* for actual errors). Mode changes / cache flushes / audit prunes call here
* many times per request — keeping them out of the DB error table.
*
* @param string $event Event name (e.g. 'bridge_mode_changed').
* @param array $context Structured context.
* @return void
*/
public static function info( string $event, array $context = array() ): void {
error_log( sprintf( '[WPDO][INFO][%s] %s', $event, $context ? wp_json_encode( $context, JSON_UNESCAPED_UNICODE ) : '' ) );
}
/**
* Log a NOTICE-level event — alias of info() for callers that want a stronger
* level intent. Same routing (error_log only).
*
* @param string $event Event name.
* @param array $context Structured context.
* @return void
*/
public static function notice( string $event, array $context = array() ): void {
self::info( $event, $context );
}
/**
* Log a WARNING-level event — error_log + wpdo_errors row (for admin visibility).
*
* Use for "anomaly worth attention but not breaking" (e.g. invalid filter
* return, unexpected fallback path). The event_name becomes the `hook` column;
* message gets a `[WARN]` prefix to distinguish from hard errors.
*
* @param string $event Event name (e.g. 'wpdo_route_decision_invalid_return').
* @param array $context Structured context.
* @return void
*/
public static function warning( string $event, array $context = array() ): void {
error_log( sprintf( '[WPDO][WARN][%s] %s', $event, $context ? wp_json_encode( $context, JSON_UNESCAPED_UNICODE ) : '' ) );
self::error( 'engine', $event, '[WARN] ' . $event, $context );
}
/**
* Log a DEBUG-level event — only when WP_DEBUG is true; routes to error_log.
*
* @param string $event Event name.
* @param array $context Structured context.
* @return void
*/
public static function debug( string $event, array $context = array() ): void {
if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
self::info( $event, $context );
}
}
/**
* Log a module error.
*
* @param string $module Module name (e.g. 'reviews', 'hot_hp_listing').
* @param string $hook The hook or method where the error occurred.
* @param string $message Human-readable error message.
* @param array $context Optional extra context (will be JSON-encoded).
* @param string $zone Optional zone identifier (hot/warm/cold/archive).
*/
public static function error( string $module, string $hook, string $message, array $context = array(), string $zone = '' ): void {
global $wpdb;
error_log( sprintf( '[WPDO][%s][%s] %s', $module, $hook, $message ) );
$table = TMDO_DB::table( 'wpdo_errors' );
$wpdb->insert(
$table,
array(
'module' => sanitize_key( $module ),
'zone' => sanitize_key( $zone ),
'hook' => substr( sanitize_text_field( $hook ), 0, 255 ),
'message' => $message,
'context' => $context ? wp_json_encode( $context, JSON_UNESCAPED_UNICODE ) : null,
'created_at' => current_time( 'mysql', true ),
),
array( '%s', '%s', '%s', '%s', '%s', '%s' )
);
}
/**
* Return recent errors, optionally filtered by module.
*
* @param string $module Module name (empty = all modules).
* @param int $limit Max rows to return.
* @return array
*/
public static function get_recent( string $module = '', int $limit = 100 ): array {
global $wpdb;
$table = TMDO_DB::table( 'wpdo_errors' );
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Table name from TMDO_DB::table().
if ( $module ) {
return $wpdb->get_results(
$wpdb->prepare(
"SELECT * FROM `{$table}` WHERE module = %s ORDER BY id DESC LIMIT %d",
$module,
$limit
),
ARRAY_A
) ?: array();
}
return $wpdb->get_results(
$wpdb->prepare( "SELECT * FROM `{$table}` ORDER BY id DESC LIMIT %d", $limit ),
ARRAY_A
) ?: array();
// phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
}
/**
* Delete errors older than a given number of days.
*
* @param int $days Keep errors from the last N days.
* @return int Number of rows deleted.
*/
public static function purge( int $days = 30 ): int {
global $wpdb;
$table = TMDO_DB::table( 'wpdo_errors' );
$now = current_time( 'mysql', true );
if ( TMDO_IS_SQLITE ) {
$sql = $wpdb->prepare(
"DELETE FROM `{$table}` WHERE created_at < datetime(%s, %s)", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
$now,
"-{$days} days"
);
} else {
$sql = $wpdb->prepare(
"DELETE FROM `{$table}` WHERE created_at < DATE_SUB(%s, INTERVAL %d DAY)", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
$now,
$days
);
}
return (int) $wpdb->query( $sql ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
}
}