feat(metrics): add HTTP client metrics#29
Merged
jstojiljkovic merged 9 commits intoMay 11, 2026
Conversation
|
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
jstojiljkovic
requested changes
May 10, 2026
Adds automatic metrics for outgoing HTTP requests via a decorator of
Symfony's HttpClientInterface, sibling of the existing TraceableHttpClient
on the trace side. Off by default, gated on metrics.http_client.enabled
which requires metrics.enabled.
Three instruments emitted on every outgoing request:
- http.client.request.duration (Histogram, 's', Stable)
- http.client.request.body.size (Histogram, 'By', Development)
- http.client.response.body.size (Histogram, 'By', Development)
Attributes follow OTel HTTP client semantic conventions:
- http.request.method, server.address, server.port (required)
- url.scheme (conditional, when present in URL)
- http.response.status_code (on response)
- error.type (on transport failure)
Request body size is extracted from explicit Content-Length header or
string body. Response body size comes from the response Content-Length
header or the fully-consumed body. Transport failures (connection
refused, timeouts, etc.) record error.type with duration; 4xx/5xx
responses are recorded with status_code (no synthesized error.type).
Implementation:
- MeteredHttpClient decorator: lazy MeterInterface cached locally, same
re-entrance guard as TraceableHttpClient, same excluded_hosts list,
OTLP endpoint auto-excluded to prevent instrumentation loops.
- MeteredResponse wrapper: mirrors TracedResponse. Records metrics on
first access to getStatusCode / getHeaders / getContent (Symfony
responses are lazy). __destruct falls back to a best-effort finalize.
- HttpClientMetricsPass: compiler pass mirroring HttpClientTracingPass,
decorates 'http_client' and all 'http_client.client' tagged services
at priority -8 (outer wrapper of the tracing decorator at -16) so the
recorded duration reflects the full outgoing call as the app sees it.
New nested config block:
metrics:
http_client:
enabled: false
excluded_hosts: []
Validation rejects metrics.http_client.enabled without metrics.enabled.
Unit test for MeteredHttpClient (13 cases): - duration emission with required OTel attributes - response body size from Content-Length header - request body size from explicit Content-Length header - request body size from string body when no header - no body metric when nothing measurable - 4xx status code recorded without synthesized error.type - transport failure (thrown exception) adds error.type - excluded host skips emission entirely - OTLP endpoint (OTEL_EXPORTER_OTLP_ENDPOINT) auto-excluded - request() returns a MeteredResponse instance - withOptions() returns a new instance preserving the decorator - stream() unwraps MeteredResponse objects before delegating - reset() clears the cached meter BundleBootTest additions: - http_client metrics disabled by default when metrics are enabled - http_client metrics enabled sets container parameters for the pass - validation rejects metrics.http_client.enabled without metrics.enabled
- Adds an HTTP Client table to the Metrics section listing the three instruments emitted, their stability status and attribute set. - Updates the main config YAML block with metrics.http_client.enabled and metrics.http_client.excluded_hosts, mentioning the OTLP auto- exclusion. - Notes that connection-pool metrics (http.client.open_connections, http.client.connection.duration, http.client.active_requests) are out of scope because Symfony HttpClient does not expose the pool.
Use FQCN per OTel semconv with a parent-class fallback for anonymous classes, mirroring the messenger middleware fix.
…est.duration The OTel SDK defaults are tuned for milliseconds and collapse all sub-5s requests into the first bucket, making p95/p99 unusable.
Swallow exceptions from the body-size and duration histograms, and move inFlight reset into a finally so a broken metric provider cannot replace the original transport exception.
Replaces the inline anonymous-class fallback in MeteredHttpClient with a reusable Util\ErrorTypeResolver, so the HTTP server subscriber and the messenger middleware can share the same canonical implementation.
Adds MeteredResponseTest covering every public response method (header finalisation with/without Content-Length, content/toArray success and transport-failure paths, cancel, getInfo, getInnerResponse, double finalisation idempotency, destructor fallback). Adds HttpClientMetricsPassTest covering decoration of the default and tagged HTTP clients, the disabled and missing-parameter early-returns, and the empty-excluded-hosts fallback. Adds MeteredHttpClient cases for the inFlight re-entrance short-circuit and reset() clearing the flag.
Without the SplObjectStorage map, chunks yielded by the inner client are keyed on the bare response, so RetryableHttpClient (or any decorator above this one) cannot look up the wrapper it handed in and Symfony's AsyncResponse throws "UnexpectedValueException: Object not found". Mirrors the fix applied to TraceableHttpClient in tracewayapp#35.
srekcud
force-pushed
the
feat/metrics-http-client
branch
from
May 10, 2026 13:38
9d0bd1b to
11ac2e9
Compare
jstojiljkovic
self-requested a review
May 11, 2026 09:37
jstojiljkovic
approved these changes
May 11, 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.
Adds OpenTelemetry metrics for outgoing requests through the Symfony HttpClient, sibling of
TraceableHttpClienton the trace side. Off by default.Instruments
http.client.request.duration(Histogram,s) — semconv Stablehttp.client.request.body.size(Histogram,By) — Developmenthttp.client.response.body.size(Histogram,By) — DevelopmentAttributes:
http.request.method,server.address,server.port,url.scheme,http.response.status_codeon response,error.typeon transport failure (4xx/5xx are notsynthesised as errors — alert on
status_code >= 400instead).Config
Behaviour
finalises metrics lazily when the caller reads status/headers/content.
exporter using this client doesn't recurse.
Notes
for anonymous classes, duration histogram declares second-based buckets, every recording
path is wrapped in a swallowing try/catch (request-body, response, failure) so a broken
meter provider cannot mask the original transport exception. inFlight is reset in a finally.
duplicated by the parallel http-server and doctrine PRs; whichever merges first lands it on
master, the others rebase clean.