PluginProbe
404 Solution / trunk
404 Solution vtrunk
4.3.5 4.3.4 4.3.3 4.3.2 4.3.1 4.3.0 4.2.0 4.1.19 4.1.18 4.1.17 4.1.16 4.1.15 4.1.13 4.1.12 4.1.11 4.1.10 4.1.9 4.1.8 4.1.7 4.1.6 4.1.5 4.1.4 4.1.3 trunk 2.30.0 All 109 releases
404-solution / includes / logs / LoggingMessageWriter.php

LoggingMessageWriter.php in 404 Solution trunk, at includes/logs/LoggingMessageWriter.php

181 lines 6.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3
4 if (!defined('ABSPATH')) {
5 exit;
6 }
7
8 // allow-no-test-found: covered by tests/LoggingTest.php through public ABJ_404_Solution_Logging message entry points.
9
10 /**
11 * Formats and writes severity-tagged debug log messages.
12 *
13 * Owns the DEBUG/INFO/WARN/ERROR line shapes and the stored-debug-message
14 * buffer used when debug mode is disabled. It deliberately receives its time,
15 * debug-mode, and file-write dependencies as callables so the public
16 * ABJ_404_Solution_Logging facade remains the only production entry point.
17 */
18 class ABJ_404_Solution_LoggingMessageWriter {
19
20 /** @var callable */
21 private $timestampProvider;
22 /** @var callable */
23 private $debugModeProvider;
24 /** @var callable */
25 private $lineWriter;
26 /** @var array<int, string> */
27 private $storedDebugMessages;
28 /** @var callable|null */
29 private $operationTracer;
30
31 /**
32 * @param callable $timestampProvider Returns the formatted current timestamp.
33 * @param callable $debugModeProvider Returns true when debug mode is enabled.
34 * @param callable $lineWriter Receives one fully-formatted log line.
35 * @param array<int, string> $storedDebugMessages Shared debug buffer from the facade.
36 * @param callable(string,array<string,mixed>,callable):mixed|null $operationTracer
37 */
38 public function __construct(
39 callable $timestampProvider,
40 callable $debugModeProvider,
41 callable $lineWriter,
42 array &$storedDebugMessages,
43 $operationTracer = null
44 ) {
45 $this->timestampProvider = $timestampProvider;
46 $this->debugModeProvider = $debugModeProvider;
47 $this->lineWriter = $lineWriter;
48 $this->storedDebugMessages =& $storedDebugMessages;
49 $this->operationTracer = is_callable($operationTracer) ? $operationTracer : null;
50 }
51
52 /**
53 * Write immediately when debug mode is enabled; otherwise buffer for the
54 * next error log entry.
55 *
56 * @param string $message
57 * @param \Throwable|null $e
58 * @return void
59 */
60 public function debugMessage(string $message, $e = null): void {
61 $this->traceOperation('message', function () use ($message, $e): void {
62 $this->writeDebugMessage($message, $e);
63 }, array('level' => 'debug'));
64 }
65
66 /** @param \Throwable|null $e */
67 private function writeDebugMessage(string $message, $e): void {
68 $stacktrace = "";
69 if ($e != null) {
70 $stacktrace = ", Stacktrace: " . $e->getTraceAsString();
71 }
72
73 $line = $this->timestampPrefix('DEBUG') . $message . $stacktrace;
74 $debugEnabled = (bool)$this->traceOperation(
75 'debug_state_resolution',
76 fn() => call_user_func($this->debugModeProvider)
77 );
78 if ($debugEnabled) {
79 $this->writeLine($line);
80 return;
81 }
82
83 $this->traceOperation('buffer_append', function () use ($line): void {
84 $this->storedDebugMessages[] = $line;
85 });
86 }
87
88 /**
89 * @param string $message
90 * @return void
91 */
92 public function infoMessage(string $message): void {
93 $this->traceOperation('message', function () use ($message): void {
94 $this->writeLine($this->timestampPrefix('INFO') . $message);
95 }, array('level' => 'info'));
96 }
97
98 /**
99 * @param string $message
100 * @return void
101 */
102 public function warn(string $message): void {
103 $this->traceOperation('message', function () use ($message): void {
104 $this->writeLine($this->timestampPrefix('WARN') . $message);
105 }, array('level' => 'warn'));
106 }
107
108 /**
109 * Flush any debug messages buffered while debug mode was disabled, then
110 * write the ERROR line with request/plugin context.
111 *
112 * @param string $message
113 * @param \Exception|null $e
114 * @return void
115 */
116 public function errorMessage(string $message, $e = null): void {
117 $this->traceOperation('message', function () use ($message, $e): void {
118 $this->writeErrorMessage($message, $e);
119 }, array('level' => 'error'));
120 }
121
122 /** @param \Exception|null $e */
123 private function writeErrorMessage(string $message, $e): void {
124 if ($e == null) {
125 $e = new Exception;
126 }
127 $stacktrace = $e->getTraceAsString();
128
129 $savedDebugMessages = implode("\n", $this->storedDebugMessages);
130 $this->storedDebugMessages = array();
131
132 $referrer = '';
133 if (array_key_exists('HTTP_REFERER', $_SERVER) && !empty($_SERVER['HTTP_REFERER'])) {
134 $referrer = $_SERVER['HTTP_REFERER'];
135 }
136 $requestedURL = '';
137 if (array_key_exists('REQUEST_URI', $_SERVER) && !empty($_SERVER['REQUEST_URI'])) {
138 $requestedURL = $_SERVER['REQUEST_URI'];
139 }
140
141 $this->writeLine($this->timestampPrefix('ERROR') . $message . ", PHP version: " . PHP_VERSION .
142 ", WP ver: " . get_bloginfo('version') . ", Plugin ver: " . ABJ404_VERSION .
143 ", Referrer: " . $referrer . ", Requested URL: " . $requestedURL .
144 ", \nStored debug messages: \n" . $savedDebugMessages . ", \nTrace: " . $stacktrace);
145 }
146
147 /**
148 * @param string $level
149 * @return string
150 */
151 private function timestampPrefix(string $level): string {
152 $timestamp = $this->traceOperation(
153 'timestamp_resolution',
154 fn() => call_user_func($this->timestampProvider)
155 );
156 return (string)$timestamp . ' (' . $level . '): ';
157 }
158
159 /**
160 * @param string $line
161 * @return void
162 */
163 private function writeLine(string $line): void {
164 $this->traceOperation('line_writer_dispatch', function () use ($line): void {
165 call_user_func($this->lineWriter, $line);
166 });
167 }
168
169 /**
170 * @template T
171 * @param callable():T $work
172 * @param array<string,mixed> $fields
173 * @return T
174 */
175 private function traceOperation(string $operation, callable $work, array $fields = array()) {
176 return is_callable($this->operationTracer)
177 ? call_user_func($this->operationTracer, $operation, $fields, $work)
178 : $work();
179 }
180 }
181