# woo-postnl/5.9.12/src/Rest_API/SDK/Logger_Adapter.php

PostNL for WooCommerce, version 5.9.12. 172 lines.

- Page: https://pluginprobe.com/plugins/woo-postnl/5.9.12/code/src/Rest_API/SDK/Logger_Adapter.php
- Raw: https://pluginprobe.com/plugins/woo-postnl/5.9.12/raw/src/Rest_API/SDK/Logger_Adapter.php
- Modified: 2026-07-20T20:49:46+00:00

Line numbers below start at 1. Link to a line or a range by appending a fragment to the
page URL, for example `https://pluginprobe.com/plugins/woo-postnl/5.9.12/code/src/Rest_API/SDK/Logger_Adapter.php#L10-L20`.

```php
<?php
/**
 * Class Rest_API\SDK\Logger_Adapter file.
 *
 * @package PostNLWooCommerce\Rest_API\SDK
 */

declare( strict_types = 1 );

namespace PostNLWooCommerce\Rest_API\SDK;

use PostNLWooCommerce\Logger;
use Psr\Log\AbstractLogger;
use Psr\Log\LogLevel;

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Class Logger_Adapter
 *
 * Bridges the V4 SDK's PSR-3 logging to the plugin's WooCommerce logger.
 * Messages are tagged [postnl-v4] so support can filter the shared WC log on
 * the originating API version; legacy entries carry no tag.
 *
 * The SDK redacts label binary, PII, and credentials via its
 * RedactionRegistry::forProduction() (on by default) before a message ever
 * reaches this adapter, so the adapter writes what it receives verbatim — it
 * deliberately does not re-run the legacy Logger::check_pdf_content() scan.
 * The V4 label binary travels under the label, mergedLabel and labelSignature
 * keys, all three of which the SDK registry omits; the legacy Labels[].Content
 * shape never reaches this adapter.
 *
 * PSR-3 context is forwarded to WC_Logger alongside the source, per WooCommerce
 * logging standards: the file handler strips source, JSON-encodes whatever
 * remains and appends it as a "CONTEXT:" segment, which the admin Logs screen
 * parses back out and renders as structured data. Forwarding therefore keeps
 * the adapter correct under either SDK log format. Under the default
 * LogFormat::Human the SDK folds the payload into the message and hands over an
 * empty context, so nothing is appended; under LogFormat::Structured the
 * message is a bare event label and the payload arrives as context, where
 * WooCommerce renders it.
 *
 * @since   5.9.9
 * @package PostNLWooCommerce\Rest_API\SDK
 */
class Logger_Adapter extends AbstractLogger {

	/**
	 * Tag prefixed to every V4 message so support can filter the shared WC log.
	 */
	private const TAG = '[postnl-v4]';

	/**
	 * WC log source (channel) shared with the legacy plugin logger.
	 */
	private const SOURCE = 'PostNLWooCommerce';

	/**
	 * The eight valid PSR-3 levels, which are identical to WooCommerce's
	 * WC_Log_Levels (both follow RFC 5424). Any value outside this set falls
	 * back to notice rather than reaching WC_Logger, which rejects unknown
	 * levels.
	 *
	 * PSR-3 allows an unknown level to raise Psr\Log\InvalidArgumentException.
	 * Coercing to notice is a deliberate departure: this adapter is a sink for
	 * SDK output, and a logging call must never break the operation that
	 * emitted it.
	 */
	private const VALID_LEVELS = array(
		LogLevel::EMERGENCY,
		LogLevel::ALERT,
		LogLevel::CRITICAL,
		LogLevel::ERROR,
		LogLevel::WARNING,
		LogLevel::NOTICE,
		LogLevel::INFO,
		LogLevel::DEBUG,
	);

	/**
	 * Plugin logger, consulted only for the merchant "enable logging" gate so
	 * the V4 path honours the same setting as the legacy path.
	 *
	 * @var Logger
	 */
	private $logger;

	/**
	 * Logger_Adapter constructor.
	 *
	 * @param Logger $logger Plugin logger providing the enable-logging gate.
	 */
	public function __construct( Logger $logger ) {
		$this->logger = $logger;
	}

	/**
	 * Write a PSR-3 log record to the WooCommerce logger.
	 *
	 * Best-effort: any failure while formatting or writing (e.g. a throwing
	 * Stringable message, or wc_get_logger() being unavailable) is swallowed so
	 * logging can never break the SDK operation that emitted the line.
	 *
	 * @param mixed              $level   PSR-3 level (one of LogLevel::*).
	 * @param string|\Stringable $message Message, optionally with {placeholders}.
	 * @param array              $context Context values for placeholder interpolation.
	 * @return void
	 */
	public function log( $level, string|\Stringable $message, array $context = array() ): void {
		// Respect the same merchant "enable logging" toggle the legacy path uses.
		if ( ! $this->logger->is_enabled() ) {
			return;
		}

		try {
			$level   = $this->normalize_level( $level );
			$message = self::TAG . ' ' . $this->interpolate( (string) $message, $context );

			// Source is merged last so incoming context can never redirect the
			// entry away from the plugin's shared WC log channel.
			$wc_context = array_merge( $context, array( 'source' => self::SOURCE ) );

			$wc_logger = wc_get_logger();
			if ( $wc_logger ) {
				$wc_logger->log( $level, $message, $wc_context );
			}
		} catch ( \Throwable $e ) {
			// Swallowed deliberately — logging is best-effort and must not propagate.
			return;
		}
	}

	/**
	 * Coerce an arbitrary level into a value WC_Logger accepts.
	 *
	 * @param mixed $level Incoming PSR-3 level.
	 * @return string A valid WC log level; notice when unrecognised.
	 */
	private function normalize_level( $level ): string {
		$level = is_string( $level ) ? strtolower( $level ) : '';

		return in_array( $level, self::VALID_LEVELS, true ) ? $level : LogLevel::NOTICE;
	}

	/**
	 * Interpolate {placeholder} tokens from context, per the PSR-3 spec.
	 *
	 * Only context values that can be cast to a string are substituted; other
	 * values (arrays, non-stringable objects) leave their placeholder intact.
	 *
	 * @param string $message Raw message.
	 * @param array  $context Context values.
	 * @return string
	 */
	private function interpolate( string $message, array $context ): string {
		if ( empty( $context ) || false === strpos( $message, '{' ) ) {
			return $message;
		}

		$replacements = array();
		foreach ( $context as $key => $value ) {
			if ( is_scalar( $value ) || $value instanceof \Stringable ) {
				$replacements[ '{' . $key . '}' ] = (string) $value;
			}
		}

		return strtr( $message, $replacements );
	}
}

```
