Skip to content

OpenTelemetry tracing and metrics for Send, CreateStream, Publish and handlers - #19

Merged
Adiakys merged 5 commits into
mainfrom
feature/tracing
Nov 1, 2026
Merged

Adiakys merged 5 commits into
mainfrom
feature/tracing

Conversation

@Adiakys

@Adiakys Adiakys commented Nov 1, 2026

Copy link
Copy Markdown
Owner

Summary

AnyMediator now emits OpenTelemetry-compatible spans and a duration histogram through .NET's own ActivitySource and Meter: no new dependency, nothing recorded until something subscribes.

builder.Services.AddOpenTelemetry()
    .WithTracing(t => t.AddSource(AnyMediatorDiagnostics.ActivitySourceName))
    .WithMetrics(m => m.AddMeter(AnyMediatorDiagnostics.MeterName));
  • New public class AnyMediatorDiagnostics with ActivitySourceName and MeterName (minor version bump at release: 2.2.0).
  • Spans: send {Request}, create_stream {Request} (open until the stream is read, fails or is abandoned), publish {Notification} with one handle {Notification} child per handler. Attributes: anymediator.operation, anymediator.message.type, anymediator.handler.type. Failures set Error status, error.type and an exception event per the OpenTelemetry conventions.
  • Metric: anymediator.operation.duration histogram (seconds) by operation, message type and error.type.
  • Cost without listeners: two flag checks; traced paths live in separate methods so no closure is allocated. Send unchanged at 93 ns / 96 B; Publish 196 → 207 ns, same allocations.
  • Docs: new "Tracing and metrics" page (setup, span/metric reference, propagation through a notification sink), README section, llms.txt entry.

Tests

  • 128 tests pass (4 new in TelemetryTests, using ActivityListener/MeterListener: send span and duration, failure recording, publish span with per-handler children, stream span lifetime). Ran the suite 4 times to check stability, since listeners are process-wide.
  • Docs site builds.

🤖 Generated with Claude Code

Adiakys and others added 4 commits November 1, 2026 13:13
AnyMediator emits OpenTelemetry-compatible spans and a duration histogram
through .NET's ActivitySource and Meter, with no new dependency.

- AnyMediatorDiagnostics exposes ActivitySourceName and MeterName.
- Spans: "send {Request}", "create_stream {Request}" (open until the
  stream is read, fails or is abandoned), "publish {Notification}" and one
  "handle {Notification}" child per handler, with operation, message type
  and handler type attributes. Failures set Error status, error.type and
  an exception event.
- Histogram anymediator.operation.duration (seconds) by operation,
  message type and error.type.
- With no listener the dispatch path checks two flags; traced paths live in
  separate methods so no closure is allocated. Send stays at 93 ns / 96 B.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
New "Tracing and metrics" guide page with the OpenTelemetry setup, span
and metric reference, cost, and trace propagation through a notification
sink. README section and llms.txt entry.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Span names and attributes are built once per message type (OperationSpan)
instead of on every call; traced operations take their inputs as a state
tuple and a static lambda, so no closure is allocated; the non-generic Run
no longer wraps the generic one; tags are passed at span creation and the
duration is recorded with the same prebuilt array.

TracingBenchmarks measures Send and Publish with and without listeners.
With a listener recording everything: Send 652 ns / 880 B -> 564 ns /
704 B; Publish with 3 handlers 2950 ns / 3936 B -> 2310 ns / 2872 B.
Unchanged with no listener.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Adiakys

Adiakys commented Nov 1, 2026

Copy link
Copy Markdown
Owner Author

Added two commits after measuring the cost with a listener active (new TracingBenchmarks, listener recording every span and measurement):

Not listening Listening, before Listening, now
Send (1 span) 87 ns, 96 B 652 ns, 880 B 564 ns, 704 B
Publish, 3 handlers (4 spans) 307 ns, 632 B 2,950 ns, 3,936 B 2,310 ns, 2,872 B

Span names and attributes are now built once per message type, traced operations use a state tuple with static lambdas (no closures), and the extra async layer is gone. The tracing guide's "Cost" section shows these numbers. Still 128 tests passing.

Tracing and metrics release: OpenTelemetry spans and a duration histogram
through AnyMediatorDiagnostics, with no behavior change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Adiakys
Adiakys merged commit 91caaa4 into main Nov 1, 2026
1 check passed
@Adiakys
Adiakys deleted the feature/tracing branch November 1, 2026 14:34
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.

1 participant