feat(logging): [1/4] structured-logging foundation — channels + context processors + auth subscriber#475
Merged
Conversation
turegjorup
force-pushed
the
feature/structured-logging-foundation
branch
2 times, most recently
from
June 3, 2026 08:11
c6652e7 to
7661b7d
Compare
turegjorup
force-pushed
the
feature/structured-logging-foundation
branch
4 times, most recently
from
June 3, 2026 08:58
e800c87 to
fbf4f60
Compare
tuj
requested changes
Jun 3, 2026
| HTTP_CLIENT_LOG_LEVEL=error | ||
| ###< Http Client ### | ||
|
|
||
| ###> Logging ### |
Contributor
There was a problem hiding this comment.
These variables should be documented in README.md under Configuration.
|
|
||
| - Contributors must attach context via the PSR-3 context array (not string interpolation) | ||
| and must never swallow exceptions silently; the PHPStan gate fails the build otherwise. | ||
| The conventions are documented in `docs/logging.md`. |
turegjorup
force-pushed
the
feature/structured-logging-foundation
branch
4 times, most recently
from
June 3, 2026 10:58
4e6beca to
0a35565
Compare
…criber Introduce the structured-logging foundation (ADR 011): - Declare per-domain Monolog channels (auth, screen, media, feed, interactive, cache) alongside app_http. - In prod, add one always-on stderr handler per domain channel, each thresholded by a LOG_LEVEL_<CHANNEL> env var that falls back to a global LOG_LEVEL (app.log_level parameter); a single Monolog handler carries one level, so per-channel thresholds need per-channel handlers. - Make the output destination configurable via LOG_PATH (default php://stderr); nested and app_http honour it too. Suits container stderr capture; bare-metal nginx + php-fpm operators can point it at a file. - RequestContextProcessor enriches every record with request id, route, method and identity (user_id XOR screen_id, tenant_id). Field names are the interim PR-1 names; the OTel SemConv rename follows in a later PR. - TraceContextProcessor parses a W3C traceparent header into trace_id / span_id when present. - RequestIdSubscriber adopts the inbound X-Request-Id or mints one and echoes it on the response, with no format validation. - AuthLoggingSubscriber logs login/logout and JWT-failure outcomes on the auth channel (logging only; never token strings or credentials). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
turegjorup
force-pushed
the
feature/structured-logging-foundation
branch
from
June 3, 2026 11:58
0a35565 to
35ed9dc
Compare
…t-id subscriber Apply the "logging must never break the request it annotates" principle consistently: - RequestContextProcessor: widen the try/catch from getActiveTenant() to the whole identity block, so screen_id (getScreen()->getId() on an unhydrated ScreenUser throws an uninitialized-typed-property Error) and user_id are guarded too. Fields set before a throw are kept. - AuthLoggingSubscriber: run each handler's logging through a guard() so a failure (context building or write) drops the line instead of aborting authentication, making the class's "only logs, never alters auth" contract real. Tests: - New RequestIdSubscriberTest: adopt/mint/empty-header/no-overwrite/sub-request. - AuthLoggingSubscriberTest: add onLoginSuccess and onJwtFailure (all four match arms: invalid/expired/not_found/unknown). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ctured-logging-foundation # Conflicts: # CHANGELOG.md # config/services.yaml
…nnel Rename AuthLoggingSubscriber's injected logger $logger -> $authLogger so the constructor self-documents which channel it writes to. The explicit @monolog.logger.auth binding stays (renamed to the matching key) and remains the authoritative, build-verified wiring; a services.yaml comment records why explicit binding is preferred over MonologBundle channel autowiring. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
OTel semconv deprecated the enduser.* attribute group in favour of user.id. Align the ADR's identity description; the field rename itself lands with the processor changes on the semconv branch. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
tuj
approved these changes
Jun 9, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
PR 1 of 4 — Structured logging
First of a four-PR series implementing structured, channel-split, context-rich logging (see
docs/adr/011-structured-logging.md). Base branch:release/3.0.0.What this PR does
auth,screen,media,feed,interactive,cache) alongsideoutbound_http(the renamedapp_httpoutbound-HTTP-client channel, see below).app_http→outbound_http; aNullLoggerdecorates Symfony's nativehttp_clientlogger soLoggingHttpClientis the single, OTel-shaped source of outbound-HTTP logs (no duplicate request logging).level, so per-channel thresholds need per-channel handlers. Each level isLOG_LEVEL_<CHANNEL>falling back to a globalLOG_LEVEL(via theapp.log_levelparameter and the empty-safedefault:env processor).inforeproduces today's output, so this is additive.nested/outbound_http) write toLOG_PATH(defaultphp://stderr). Suits container stderr capture; bare-metal nginx + php-fpm operators can point it at a file (they own rotation/permissions). New.env:LOG_PATH,LOG_LEVEL,LOG_LEVEL_<CHANNEL>(empty = inherit).RequestContextProcessor— enriches every record withrequest_id,route,method, and identity (user_idXORscreen_id,tenant_id). No request-id format validation. Identity-resolution failures (no active tenant, unhydrated screen token, …) are caught so enrichment never breaks the request it is annotating.TraceContextProcessor— parses a W3Ctraceparentheader intotrace_id/span_id.RequestIdSubscriber— adopts the inboundX-Request-Idor mints one, echoes it on the response.AuthLoggingSubscriber— logs login/logout and JWT invalid/expired/not-found on theauthchannel. Logging only — never token strings or credentials, and each handler is guarded so a logging failure can never break authentication.Verification
composer code-analysis— green (PHPStan level 6)composer coding-standards-check+ env-coverage — greencomposer test— green (18 tests across both processors, the request-id subscriber, and the auth subscriber)cache:clear --env=prod— per-channel handlers +LOG_LEVELfallback resolve🤖 Generated with Claude Code