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 / frontend / FrontendPipelineTelemetry.php

FrontendPipelineTelemetry.php in 404 Solution trunk, at includes/frontend/FrontendPipelineTelemetry.php

131 lines 5.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 if (!defined('ABSPATH')) {
4 exit;
5 }
6
7 /**
8 * Pipeline-execution diagnostic emission adapter. Owns three concerns:
9 * 1. Benchmark response headers (`abj404_benchmark_emit_headers`) for paths
10 * that bypass WordPress's normal send_headers.
11 * 2. Redirect-lookup timing samples
12 * (`abj404_benchmark_record_redirect_lookup`) recorded in microsecond
13 * precision so the benchmark wp_load can build a histogram.
14 * 3. Verbose debug-log line at the top of process404, only emitted when
15 * the logger is in debug mode.
16 *
17 * All three call into global functions that may or may not be defined
18 * (benchmark wp_load is optional, debug logging is opt-in). The adapter
19 * absorbs those defined/!defined checks.
20 */
21 class ABJ_404_Solution_FrontendPipelineTelemetry {
22
23 /** @var ABJ_404_Solution_Logging */
24 private $logger;
25
26 /** @var ABJ_404_Solution_Functions */
27 private $f;
28
29 /**
30 * @param ABJ_404_Solution_Logging $logger
31 * @param ABJ_404_Solution_Functions $functions
32 */
33 function __construct($logger, $functions) {
34 $this->logger = $logger;
35 $this->f = $functions;
36 }
37
38 /**
39 * Emit benchmark header immediately for paths that may not reach
40 * WordPress send_headers (early redirect, exit, etc).
41 * @return void
42 */
43 function emitBenchmarkHeadersIfEnabled(): void {
44 if (function_exists('abj404_benchmark_emit_headers')) {
45 abj404_benchmark_emit_headers();
46 }
47 }
48
49 /**
50 * @param float $startTime
51 * @return void
52 */
53 function recordRedirectLookupTiming($startTime): void {
54 if (!function_exists('abj404_benchmark_record_redirect_lookup')) {
55 return;
56 }
57 $elapsedMs = (abj_clock()->nowFloat() - (float)$startTime) * 1000.0;
58 abj404_benchmark_record_redirect_lookup($elapsedMs);
59 }
60
61 /**
62 * Verbose debug-line emit at the top of process404. No-op when not in
63 * debug mode (the line is expensive: parse_url, json_encode, MD5).
64 *
65 * @param array<string, mixed> $options
66 * @param string $requestedURL
67 * @param array<string, mixed> $redirect
68 * @return void
69 */
70 function logAReallyLongDebugMessage(array $options, string $requestedURL, array $redirect): void {
71 if (!$this->logger->isDebug()) {
72 return;
73 }
74
75 $optAutoRedirects = isset($options['auto_redirects']) && is_scalar($options['auto_redirects']) ? (string)$options['auto_redirects'] : '';
76 $optAutoScore = isset($options['auto_score']) && is_scalar($options['auto_score']) ? (string)$options['auto_score'] : '';
77 $optTemplatePriority = isset($options['template_redirect_priority']) && is_scalar($options['template_redirect_priority']) ? (string)$options['template_redirect_priority'] : '';
78 $optAutoCats = isset($options['auto_cats']) && is_scalar($options['auto_cats']) ? (string)$options['auto_cats'] : '';
79 $optAutoTags = isset($options['auto_tags']) && is_scalar($options['auto_tags']) ? (string)$options['auto_tags'] : '';
80 $optDest404 = isset($options['dest404page']) && is_scalar($options['dest404page']) ? (string)$options['dest404page'] : '';
81 $debugOptionsMsg = esc_html('auto_redirects: ' . $optAutoRedirects . ', auto_score: ' .
82 $optAutoScore . ', template_redirect_priority: ' . $optTemplatePriority .
83 ', auto_cats: ' . $optAutoCats . ', auto_tags: ' .
84 $optAutoTags . ', dest404page: ' . $optDest404);
85
86 $remoteAddressRaw = isset($_SERVER['REMOTE_ADDR']) && is_string($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '';
87 $remoteAddress = esc_sql($remoteAddressRaw);
88 if (!is_string($remoteAddress)) {
89 $remoteAddress = '';
90 }
91 if (!array_key_exists('log_raw_ips', $options) || $options['log_raw_ips'] != '1') {
92 $remoteAddress = $this->f->md5lastOctet($remoteAddress);
93 }
94
95 $httpUserAgent = '';
96 if (array_key_exists('HTTP_USER_AGENT', $_SERVER) && is_string($_SERVER['HTTP_USER_AGENT'])) {
97 $httpUserAgent = $_SERVER['HTTP_USER_AGENT'];
98 }
99
100 $requestUriStr = isset($_SERVER['REQUEST_URI']) && is_string($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '';
101 $debugServerMsg = esc_html('HTTP_USER_AGENT: ' . $httpUserAgent . ', REMOTE_ADDR: ' .
102 $remoteAddress . ', REQUEST_URI: ' . abj_service('sanitizer')->normalizeUrlString($requestUriStr));
103 $isSingle = $this->callWpFunction('is_single', array(), false);
104 $isPage = $this->callWpFunction('is_page', array(), false);
105 $isFeed = $this->callWpFunction('is_feed', array(), false);
106 $isTrackback = $this->callWpFunction('is_trackback', array(), false);
107 $isPreview = $this->callWpFunction('is_preview', array(), false);
108 $redirectJson = json_encode($redirect);
109 $this->logger->debugMessage('Processing 404 for URL: ' . $requestedURL . ' | Redirect: ' .
110 wp_kses_post(is_string($redirectJson) ? $redirectJson : '{}') . ' | is_single(): ' . $isSingle . ' | ' . 'is_page(): ' . $isPage .
111 ' | is_feed(): ' . $isFeed . ' | is_trackback(): ' . $isTrackback . ' | is_preview(): ' .
112 $isPreview . ' | options: ' . $debugOptionsMsg . ', ' . $debugServerMsg);
113 }
114
115 /**
116 * Safe wrapper for WordPress template/global functions. Returns $default
117 * when the function does not exist (used during unit testing without WP loaded).
118 *
119 * @param string $name
120 * @param array<int, mixed> $args
121 * @param mixed $default
122 * @return mixed
123 */
124 private function callWpFunction(string $name, array $args = array(), $default = null) {
125 if (!function_exists($name)) {
126 return $default;
127 }
128 return call_user_func_array($name, $args);
129 }
130 }
131