PluginProbe
PostNL for WooCommerce / 5.9.12
PostNL for WooCommerce v5.9.12
5.9.12 5.9.11 5.9.10 5.9.9 5.9.8 5.9.7 5.9.6 trunk 2.5.0 2.5.1 2.5.2 2.5.3 2.5.4 2.5.5 3.1.4 3.1.5 3.1.6 3.1.7 4.0.0 4.0.1 4.0.2 4.3.2 4.3.3 4.4.0 4.4.1 All 72 releases
woo-postnl / src / Rest_API / SDK / Logger_Adapter.php

Logger_Adapter.php in PostNL for WooCommerce 5.9.12, at src/Rest_API/SDK/Logger_Adapter.php

172 lines 5.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Class Rest_API\SDK\Logger_Adapter file.
4 *
5 * @package PostNLWooCommerce\Rest_API\SDK
6 */
7
8 declare( strict_types = 1 );
9
10 namespace PostNLWooCommerce\Rest_API\SDK;
11
12 use PostNLWooCommerce\Logger;
13 use Psr\Log\AbstractLogger;
14 use Psr\Log\LogLevel;
15
16 if ( ! defined( 'ABSPATH' ) ) {
17 exit;
18 }
19
20 /**
21 * Class Logger_Adapter
22 *
23 * Bridges the V4 SDK's PSR-3 logging to the plugin's WooCommerce logger.
24 * Messages are tagged [postnl-v4] so support can filter the shared WC log on
25 * the originating API version; legacy entries carry no tag.
26 *
27 * The SDK redacts label binary, PII, and credentials via its
28 * RedactionRegistry::forProduction() (on by default) before a message ever
29 * reaches this adapter, so the adapter writes what it receives verbatim — it
30 * deliberately does not re-run the legacy Logger::check_pdf_content() scan.
31 * The V4 label binary travels under the label, mergedLabel and labelSignature
32 * keys, all three of which the SDK registry omits; the legacy Labels[].Content
33 * shape never reaches this adapter.
34 *
35 * PSR-3 context is forwarded to WC_Logger alongside the source, per WooCommerce
36 * logging standards: the file handler strips source, JSON-encodes whatever
37 * remains and appends it as a "CONTEXT:" segment, which the admin Logs screen
38 * parses back out and renders as structured data. Forwarding therefore keeps
39 * the adapter correct under either SDK log format. Under the default
40 * LogFormat::Human the SDK folds the payload into the message and hands over an
41 * empty context, so nothing is appended; under LogFormat::Structured the
42 * message is a bare event label and the payload arrives as context, where
43 * WooCommerce renders it.
44 *
45 * @since 5.9.9
46 * @package PostNLWooCommerce\Rest_API\SDK
47 */
48 class Logger_Adapter extends AbstractLogger {
49
50 /**
51 * Tag prefixed to every V4 message so support can filter the shared WC log.
52 */
53 private const TAG = '[postnl-v4]';
54
55 /**
56 * WC log source (channel) shared with the legacy plugin logger.
57 */
58 private const SOURCE = 'PostNLWooCommerce';
59
60 /**
61 * The eight valid PSR-3 levels, which are identical to WooCommerce's
62 * WC_Log_Levels (both follow RFC 5424). Any value outside this set falls
63 * back to notice rather than reaching WC_Logger, which rejects unknown
64 * levels.
65 *
66 * PSR-3 allows an unknown level to raise Psr\Log\InvalidArgumentException.
67 * Coercing to notice is a deliberate departure: this adapter is a sink for
68 * SDK output, and a logging call must never break the operation that
69 * emitted it.
70 */
71 private const VALID_LEVELS = array(
72 LogLevel::EMERGENCY,
73 LogLevel::ALERT,
74 LogLevel::CRITICAL,
75 LogLevel::ERROR,
76 LogLevel::WARNING,
77 LogLevel::NOTICE,
78 LogLevel::INFO,
79 LogLevel::DEBUG,
80 );
81
82 /**
83 * Plugin logger, consulted only for the merchant "enable logging" gate so
84 * the V4 path honours the same setting as the legacy path.
85 *
86 * @var Logger
87 */
88 private $logger;
89
90 /**
91 * Logger_Adapter constructor.
92 *
93 * @param Logger $logger Plugin logger providing the enable-logging gate.
94 */
95 public function __construct( Logger $logger ) {
96 $this->logger = $logger;
97 }
98
99 /**
100 * Write a PSR-3 log record to the WooCommerce logger.
101 *
102 * Best-effort: any failure while formatting or writing (e.g. a throwing
103 * Stringable message, or wc_get_logger() being unavailable) is swallowed so
104 * logging can never break the SDK operation that emitted the line.
105 *
106 * @param mixed $level PSR-3 level (one of LogLevel::*).
107 * @param string|\Stringable $message Message, optionally with {placeholders}.
108 * @param array $context Context values for placeholder interpolation.
109 * @return void
110 */
111 public function log( $level, string|\Stringable $message, array $context = array() ): void {
112 // Respect the same merchant "enable logging" toggle the legacy path uses.
113 if ( ! $this->logger->is_enabled() ) {
114 return;
115 }
116
117 try {
118 $level = $this->normalize_level( $level );
119 $message = self::TAG . ' ' . $this->interpolate( (string) $message, $context );
120
121 // Source is merged last so incoming context can never redirect the
122 // entry away from the plugin's shared WC log channel.
123 $wc_context = array_merge( $context, array( 'source' => self::SOURCE ) );
124
125 $wc_logger = wc_get_logger();
126 if ( $wc_logger ) {
127 $wc_logger->log( $level, $message, $wc_context );
128 }
129 } catch ( \Throwable $e ) {
130 // Swallowed deliberately — logging is best-effort and must not propagate.
131 return;
132 }
133 }
134
135 /**
136 * Coerce an arbitrary level into a value WC_Logger accepts.
137 *
138 * @param mixed $level Incoming PSR-3 level.
139 * @return string A valid WC log level; notice when unrecognised.
140 */
141 private function normalize_level( $level ): string {
142 $level = is_string( $level ) ? strtolower( $level ) : '';
143
144 return in_array( $level, self::VALID_LEVELS, true ) ? $level : LogLevel::NOTICE;
145 }
146
147 /**
148 * Interpolate {placeholder} tokens from context, per the PSR-3 spec.
149 *
150 * Only context values that can be cast to a string are substituted; other
151 * values (arrays, non-stringable objects) leave their placeholder intact.
152 *
153 * @param string $message Raw message.
154 * @param array $context Context values.
155 * @return string
156 */
157 private function interpolate( string $message, array $context ): string {
158 if ( empty( $context ) || false === strpos( $message, '{' ) ) {
159 return $message;
160 }
161
162 $replacements = array();
163 foreach ( $context as $key => $value ) {
164 if ( is_scalar( $value ) || $value instanceof \Stringable ) {
165 $replacements[ '{' . $key . '}' ] = (string) $value;
166 }
167 }
168
169 return strtr( $message, $replacements );
170 }
171 }
172