PluginProbe
Pushly / trunk
Pushly vtrunk
2.4.0 2.3.0 trunk 1.0 1.0.0 1.1.0 1.1.1 1.1.2 1.1.3 2.0.0 2.1.0 2.1.1 2.1.10 2.1.11 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 2.1.7 2.1.8 2.1.9 2.2.0 2.2.1
pushly / src / php / Admin / LogWriter.php

LogWriter.php in Pushly trunk, at src/php/Admin/LogWriter.php

115 lines 3.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Pushly\Admin;
4
5 if ( ! defined( 'ABSPATH' ) ) {
6 exit;
7 }
8
9 class LogWriter {
10
11 private const ALLOWED_SEVERITIES = [ 'debug', 'info', 'warning', 'error' ];
12
13 /** @var array<int, array{timestamp: string, severity: string, event_type: string, message: string, post_id: ?int, context: ?string}> */
14 private array $buffer = [];
15
16 private LogStore $store;
17
18 private bool $enabled;
19
20 /**
21 * @param LogStore $store The database storage layer for log entries.
22 * @param bool $enabled Whether debug logging is active.
23 */
24 public function __construct( LogStore $store, bool $enabled ) {
25 $this->store = $store;
26 $this->enabled = $enabled;
27 }
28
29 /**
30 * Registers the shutdown hook to flush buffered entries.
31 */
32 public function register_hooks(): void {
33 add_action( 'shutdown', [ $this, 'flush' ] );
34 }
35
36 /**
37 * Adds a log entry to the in-memory buffer.
38 * No-ops when debug logging is disabled or severity is invalid.
39 *
40 * @param string $severity One of: debug, info, warning, error.
41 * @param string $event_type Machine-readable event identifier.
42 * @param string $message Human-readable message.
43 * @param int|null $post_id Associated post ID, if applicable.
44 * @param array|null $context Additional structured data.
45 */
46 public function log( string $severity, string $event_type, string $message, ?int $post_id = null, ?array $context = null ): void {
47 if ( ! $this->enabled ) {
48 return;
49 }
50
51 if ( ! in_array( $severity, self::ALLOWED_SEVERITIES, true ) ) {
52 return;
53 }
54
55 $this->buffer[] = [
56 'timestamp' => gmdate( 'Y-m-d H:i:s' ),
57 'severity' => $severity,
58 'event_type' => $event_type,
59 'message' => $message,
60 'post_id' => $post_id,
61 'context' => $context !== null ? wp_json_encode( $context ) : null,
62 ];
63 }
64
65 /**
66 * Writes a single entry directly to the store, bypassing the buffer.
67 * Used for environment snapshots that must persist immediately.
68 *
69 * @param string $severity One of: debug, info, warning, error.
70 * @param string $event_type Machine-readable event identifier.
71 * @param string $message Human-readable message.
72 * @param int|null $post_id Associated post ID, if applicable.
73 * @param array|null $context Additional structured data.
74 */
75 public function log_immediate( string $severity, string $event_type, string $message, ?int $post_id = null, ?array $context = null ): void {
76 $entry = [
77 'timestamp' => gmdate( 'Y-m-d H:i:s' ),
78 'severity' => $severity,
79 'event_type' => $event_type,
80 'message' => $message,
81 'post_id' => $post_id,
82 'context' => $context !== null ? wp_json_encode( $context ) : null,
83 ];
84
85 $this->store->insert_batch( [ $entry ] );
86 }
87
88 /**
89 * Flushes all buffered entries to the LogStore in a single batch.
90 * Called on the WordPress shutdown action.
91 * Catches all exceptions silently to ensure logging never interrupts the request.
92 * Clears the buffer regardless of success or failure.
93 */
94 public function flush(): void {
95 if ( empty( $this->buffer ) ) {
96 return;
97 }
98
99 try {
100 $this->store->insert_batch( $this->buffer );
101 } catch ( \Exception $e ) {
102 // Intentionally silent — logging must never cause a visible error.
103 }
104
105 $this->buffer = [];
106 }
107
108 /**
109 * Returns whether debug logging is enabled.
110 */
111 public function is_enabled(): bool {
112 return $this->enabled;
113 }
114 }
115