Skip to content

Feat/metrics foundation#27

Merged
jstojiljkovic merged 8 commits into
tracewayapp:masterfrom
srekcud:feat/metrics-foundation
May 3, 2026
Merged

Feat/metrics foundation#27
jstojiljkovic merged 8 commits into
tracewayapp:masterfrom
srekcud:feat/metrics-foundation

Conversation

@srekcud

@srekcud srekcud commented Apr 23, 2026

Copy link
Copy Markdown
Contributor

Refs #21 for the design discussion.

Adds the metrics pillar to the bundle alongside the existing tracing and logs
instrumentation. Off by default, existing tracing/logs behavior unchanged.

What's in

  • MeterRegistry / MeterRegistryInterface: lazy-cached access to counters, histograms and
    up/down counters. Public accessor users inject for their own business metrics. Caches
    instruments (otherwise aggregation breaks) and clears on ResetInterface.
  • OpenTelemetryMetricsMiddleware: sibling of OpenTelemetryMiddleware, wired on the same bus
    via prepend(). Emits on consume side only. Owns its lazy MeterInterface, same shape as
    ConsoleSubscriber uses for the tracer today.
  • Nested config from day one under metrics: to align with the planned v2 rework. Validation
    rejects metrics.messenger.enabled without metrics.enabled.
  • Tests cover container wiring (functional), MeterRegistry unit, middleware unit with
    InMemoryExporter via the existing OTelTestTrait extended with a meter provider.

Semconv version pinned

OTel messaging metrics semconv as of 2026-04-23. Every messaging metric and
messaging-specific attribute is Development; only error.type is Stable.

Names:

  • messaging.process.duration (Histogram, s)
  • messaging.client.consumed.messages (Counter, {message})

Attributes (required / conditional per spec, plus messaging.operation.type for trace
correlation):

  • messaging.system = symfony_messenger
  • messaging.operation.name = process
  • messaging.operation.type = process
  • messaging.destination.name = ReceivedStamp::getTransportName() when available
  • error.type on failure = short exception class

Name resolution is centralized in a private method so OTEL_SEMCONV_STABILITY_OPT_IN
dual-emit is a one-place change when messaging metrics stabilize.

Closes #21

srekcud added 4 commits April 23, 2026 10:26
Introduces metrics support alongside the existing tracing and logs
instrumentation, aligned with OpenTelemetry's three-pillar model.

- MeterRegistry / MeterRegistryInterface: lazy-cached access to counters,
  histograms and up/down counters bound to a single meter, mirroring the
  Tracing helper pattern. User-land manual instrumentation entry point.
- New nested config: metrics.enabled (master switch, off by default) and
  metrics.meter_name. Nested from day one to align with the planned v2
  config rework; flat keys for other subsystems will migrate separately.

Refs tracewayapp#21
Sibling of OpenTelemetryMiddleware, wired on the same Messenger bus via
OpenTelemetryExtension::prepend() when metrics.messenger.enabled. Dispatch
side is out of scope for this first metrics drop; emission triggers on
ReceivedStamp or ConsumedByWorkerStamp presence.

Metrics (OTel messaging metrics semconv, Development status as of 2026-04):
- messaging.process.duration             (Histogram, unit 's')
- messaging.client.consumed.messages     (Counter,  unit '{message}')

Attributes on every emission:
- messaging.system                  (required)    = 'symfony_messenger'
- messaging.operation.name          (required)    = 'process'
- messaging.operation.type          (trace-compat) = 'process'
- messaging.destination.name        (conditional) = ReceivedStamp::getTransportName()
- error.type                        (conditional, on failure) = short exception class

Mirrors the tracer subscriber pattern: each subscriber owns its Meter
lazy-resolved from Globals::meterProvider(), caches instruments locally,
implements ResetInterface to clear everything between worker iterations.
No cross-subscriber dependency. Same shape as ConsoleSubscriber uses for
the tracer today, so upcoming HTTP/Doctrine/Cache metric subscribers
can follow the same form.

Metric name resolution is centralized in a private method, ready for
OTEL_SEMCONV_STABILITY_OPT_IN dual-emit once messaging metrics stabilize.

Config validation rejects metrics.messenger.enabled without metrics.enabled.

Refs tracewayapp#21
Extends OTelTestTrait with an in-memory MeterProvider + ExportingReader so
any test can assert on emitted metrics via collectMetrics().

Coverage:
- MeterRegistry: instrument caching per name, emitted values/attributes,
  reset behavior.
- OpenTelemetryMetricsMiddleware: counter + histogram emission on consume,
  semantic convention attributes (including required messaging.operation.name),
  error.type on failure, queue exclusion, dispatch-side passthrough, reset
  clears cached meter and instruments, ConsumedByWorkerStamp without
  ReceivedStamp omits destination attribute.
- BundleBootTest: metrics services correctly wired based on nested config,
  validation rejects metrics.messenger.enabled without metrics.enabled.

Refs tracewayapp#21
Adds a Metrics section to the README covering:
- nested config tree (metrics.enabled, metrics.meter_name,
  metrics.messenger.enabled, metrics.messenger.excluded_queues)
- note that the nested shape is a forward-compatible choice for the
  planned v2 config rework, not an inconsistency with the 1.x flat keys
- emitted instruments table with OTel semantic convention attributes
  (messaging.process.duration + messaging.client.consumed.messages)
- explicit note on semconv status: all messaging metrics and attributes
  are currently Development in the spec; only error.type is Stable
- scope note: excluded_queues is consume-side only (ReceivedStamp), and
  dispatch-side metrics are out of scope for this first drop
- MeterRegistryInterface injection example for custom business metrics
- OTEL_METRICS_EXPORTER and OTEL_EXPORTER_OTLP_METRICS_ENDPOINT env vars

Refs tracewayapp#21
@codecov-commenter

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

On PHP 8.1, iterator_to_array() requires a strict Traversable and throws
TypeError when given an array. The OTel SDK types dataPoints as iterable,
which can return either an array or a Traversable depending on the
aggregator. The spread operator [...$x] accepts both and works on PHP 8.1+.

Caught by CI on the PHP 8.1 matrix row.
Comment thread src/Messenger/OpenTelemetryMetricsMiddleware.php
Comment thread src/Messenger/OpenTelemetryMetricsMiddleware.php
Comment thread src/Messenger/OpenTelemetryMetricsMiddleware.php
srekcud added 3 commits April 30, 2026 10:08
Use FQCN per OTel semconv with a parent-class fallback for anonymous
classes, whose name embeds a filesystem path that leaks code locations
and explodes label cardinality.
…s.duration

The OTel SDK defaults are tuned for milliseconds and collapse all
sub-5s jobs into the first bucket, making p95/p99 unusable. The new
boundaries mirror the HTTP semconv recommendation for second-based
durations.
Wrap record() in a try/finally with a swallowing catch so that a broken
metric provider, exporter or instrument cannot replace the original
business exception with a metric error.
@srekcud

srekcud commented Apr 30, 2026

Copy link
Copy Markdown
Contributor Author

Hello @jstojiljkovic thanx for the review

I have added your proposal with some test on top.

@jstojiljkovic
jstojiljkovic self-requested a review May 3, 2026 18:12
@jstojiljkovic
jstojiljkovic merged commit d6b97d1 into tracewayapp:master May 3, 2026
5 checks passed
@jstojiljkovic

Copy link
Copy Markdown
Collaborator

@srekcud

Hello @jstojiljkovic thanx for the review

I have added your proposal with some test on top.

Merged, will prepare for the release next week, thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Metrics support: in-tree or sibling bundle?

3 participants