LoggingMiddleware
in package
implements
MiddlewareInterface
PSR-3 logging middleware - logs one line per handler invocation (per dispatch attempt), with a level derived from the outcome (info for success, warning for 4xx, error for 5xx or a thrown exception), known- sensitive headers redacted by default, and request/response bodies excluded unless explicitly opted in. Exceptions are logged then re-thrown completely unmodified. No special coupling with RetryMiddleware - see logRetriesTo() for an optional adapter onto its existing onRetry hook.
Tags
Table of Contents
Interfaces
- MiddlewareInterface
- Client middleware interface - a PSR-15-style interceptor for Client's outbound dispatch. Not a literal PSR-15 implementation: PSR-15's real interfaces are typed to ServerRequestInterface (server-side only) and cannot apply to a Client's outbound RequestInterface flow, so this mirrors PSR-15's shape with the request type swapped.
Properties
- $includeBody : bool
- Whether to include (truncated) request/response body content in the log context
- $logger : LoggerInterface
- The PSR-3 logger to write to
- $maxBodyLength : int
- Maximum body length included in the log context when includeBody is true
- $redactedHeaders : array<string|int, mixed>
- Header names redacted from the logged context (case-insensitive match)
Methods
- __construct() : mixed
- Constructor
- addRedactedHeader() : LoggingMiddleware
- Add a header name to the redacted list
- getIncludeBody() : bool
- Get whether body content is included in the log context
- getMaxBodyLength() : int
- Get the maximum body length included in the log context
- getRedactedHeaders() : array<string|int, mixed>
- Get the redacted header list
- logRetriesTo() : callable
- Adapt a PSR-3 logger into a callable matching RetryMiddleware::setOnRetry()'s signature, producing a structured "retrying" warning log per attempt with the failure reason (exception class+message, or response status) and computed delay
- process() : ResponseInterface
- Process the request, logging one line for this invocation once the outcome is known
- setIncludeBody() : LoggingMiddleware
- Set whether to include (truncated) request/response body content in the log context
- setMaxBodyLength() : LoggingMiddleware
- Set the maximum body length included in the log context when includeBody is true
- setRedactedHeaders() : LoggingMiddleware
- Set the redacted header list, replacing the defaults
- bodyForLog() : string
- Build the (truncated) logged representation of a request/response body without materializing the whole underlying stream into memory - critical for large, file-backed bodies (e.g. multi-megabyte uploads), where only ~maxBodyLength bytes ever need to exist as a PHP string.
- buildContext() : array<string|int, mixed>
- Build the structured log context for a request/response/exception outcome
- levelForStatus() : string
- Determine the PSR-3 log level for a response status code
- redact() : array<string|int, mixed>
- Redact configured header names from a PSR-7 getHeaders()-shaped array
- truncate() : string
- Truncate body content to the configured maximum length
Properties
$includeBody
Whether to include (truncated) request/response body content in the log context
protected
bool
$includeBody
= false
$logger
The PSR-3 logger to write to
protected
LoggerInterface
$logger
$maxBodyLength
Maximum body length included in the log context when includeBody is true
protected
int
$maxBodyLength
= 1000
$redactedHeaders
Header names redacted from the logged context (case-insensitive match)
protected
array<string|int, mixed>
$redactedHeaders
= ['Authorization', 'Cookie', 'Set-Cookie', 'X-Api-Key', 'Proxy-Authorization']
Methods
__construct()
Constructor
public
__construct(LoggerInterface $logger) : mixed
Parameters
- $logger : LoggerInterface
addRedactedHeader()
Add a header name to the redacted list
public
addRedactedHeader(string $header) : LoggingMiddleware
Parameters
- $header : string
Return values
LoggingMiddlewaregetIncludeBody()
Get whether body content is included in the log context
public
getIncludeBody() : bool
Return values
boolgetMaxBodyLength()
Get the maximum body length included in the log context
public
getMaxBodyLength() : int
Return values
intgetRedactedHeaders()
Get the redacted header list
public
getRedactedHeaders() : array<string|int, mixed>
Return values
array<string|int, mixed>logRetriesTo()
Adapt a PSR-3 logger into a callable matching RetryMiddleware::setOnRetry()'s signature, producing a structured "retrying" warning log per attempt with the failure reason (exception class+message, or response status) and computed delay
public
static logRetriesTo(LoggerInterface $logger) : callable
Parameters
- $logger : LoggerInterface
Return values
callableprocess()
Process the request, logging one line for this invocation once the outcome is known
public
process(RequestInterface $request, RequestHandlerInterface $handler) : ResponseInterface
Parameters
- $request : RequestInterface
- $handler : RequestHandlerInterface
Return values
ResponseInterfacesetIncludeBody()
Set whether to include (truncated) request/response body content in the log context
public
setIncludeBody(bool $include) : LoggingMiddleware
Parameters
- $include : bool
Return values
LoggingMiddlewaresetMaxBodyLength()
Set the maximum body length included in the log context when includeBody is true
public
setMaxBodyLength(int $length) : LoggingMiddleware
Parameters
- $length : int
Return values
LoggingMiddlewaresetRedactedHeaders()
Set the redacted header list, replacing the defaults
public
setRedactedHeaders(array<string|int, mixed> $headers) : LoggingMiddleware
Parameters
- $headers : array<string|int, mixed>
Return values
LoggingMiddlewarebodyForLog()
Build the (truncated) logged representation of a request/response body without materializing the whole underlying stream into memory - critical for large, file-backed bodies (e.g. multi-megabyte uploads), where only ~maxBodyLength bytes ever need to exist as a PHP string.
protected
bodyForLog(StreamInterface $body) : string
For a Pop\Http\Body, reads at most maxBodyLength + 1 raw bytes directly off the underlying stream resource (bypassing render()'s encoding/chunk-split transforms, which are irrelevant - and potentially misleading - for a debug log), saving and restoring the stream's read position exactly as Body::getContent() does, so other consumers of the same Body object are unaffected. Non-seekable streams are skipped gracefully with a placeholder rather than letting a rewind()/fseek() PHP warning leak out of a logging call. Any other StreamInterface implementation falls back to the previous whole-body-then-truncate behavior, since there is no generic PSR-7 API for a position-preserving bounded read.
Parameters
- $body : StreamInterface
Return values
stringbuildContext()
Build the structured log context for a request/response/exception outcome
protected
buildContext(RequestInterface $request, ResponseInterface|null $response, Throwable|null $exception, float $duration) : array<string|int, mixed>
Parameters
- $request : RequestInterface
- $response : ResponseInterface|null
- $exception : Throwable|null
- $duration : float
Return values
array<string|int, mixed>levelForStatus()
Determine the PSR-3 log level for a response status code
protected
levelForStatus(int $code) : string
Parameters
- $code : int
Return values
stringredact()
Redact configured header names from a PSR-7 getHeaders()-shaped array
protected
redact(array<string|int, mixed> $headers) : array<string|int, mixed>
Parameters
- $headers : array<string|int, mixed>
Return values
array<string|int, mixed>truncate()
Truncate body content to the configured maximum length
protected
truncate(string $content) : string
A negative maxBodyLength is clamped to 0 - mb_substr()/substr() treat a negative length as "omit N characters from the end," not "take N characters," so without clamping, a negative value would silently produce unbounded (merely trailing-trimmed) output that still looks truncated because of the appended '...'.
Uses mb_strlen()/mb_substr() when available for multi-byte-safe truncation, falling back to byte-based strlen()/substr() otherwise - ext-mbstring is only suggested, not required, by this package, so an unconditional mb_* call would fatal on a build without it. The byte-based fallback can split a multi-byte character mid-sequence; that's an accepted, honest degradation rather than a fatal error.
Parameters
- $content : string