Chapter 18 · Diagnostics, Logging, Interceptors, Metrics, and Observability
DiagnosticSource, EventCounters, Activity/OpenTelemetry Integration, and Correlation
Connect EF Core DiagnosticSource events and first-class System.Diagnostics.Metrics to application Activity/OpenTelemetry correlation, while treating legacy EventCounters and the beta OpenTelemetry EF instrumentation package as clearly labeled observation paths rather than hidden dependencies.
Learning outcomes
Explain DiagnosticSource/DiagnosticListener as process-wide rich event instrumentation and distinguish it from per-context interceptors and ordinary logging.
Use EF Core 10 System.Diagnostics.Metrics from the Microsoft.EntityFrameworkCore meter and name the currently documented health instruments.
Use legacy EventCounters only as a compatibility/CLI observation path and avoid mixing their semantics with the newer Meter instruments.
Correlate an application Activity/trace identifier with EF command telemetry and understand where OpenTelemetry fits in the .NET diagnostics stack.
Label OpenTelemetry.Instrumentation.EntityFrameworkCore as beta/prerelease and keep query-parameter capture disabled by default.
Build a free local console observation path that demonstrates metrics and correlation without requiring a commercial backend.
1. Logs answer “what happened”; diagnostics add machine-readable event streams
ServiceHub can now emit safe structured logs, but a production
observability pipeline also needs continuous counters and trace
correlation. EF Core exposes several layers.
DiagnosticSource/DiagnosticListener
publishes rich in-process diagnostic events for EF operations.
System.Diagnostics.Metrics publishes continuous
numeric instruments through a named Meter.
EventCounters are the older event-source
counter mechanism and remain useful for tools such as
dotnet-counters. Activity is
.NET's representation of trace/span context. OpenTelemetry
collects .NET logs, metrics, and activities and exports them to
an observability backend.
Mandatory baseline: .NET runtime 10.0.11, SDK 10.0.400, Microsoft.EntityFrameworkCore / Design / Sqlite and dotnet-ef 10.0.11. SQLite is the free local database. EF Core 11 preview APIs are out of scope. OpenTelemetry core/hosting 1.18.0 may be used for an optional console telemetry path; OpenTelemetry.Instrumentation.EntityFrameworkCore 1.18.0-beta.1 remains prerelease/beta and is never required for the mandatory labs.
2. DiagnosticListener is process-wide, not a DbContext-local hook
EF's diagnostic listener can expose events from every context in the process. That makes it useful for libraries, profilers, telemetry adapters, and process-wide diagnostics. It is not intended as a general-purpose logging API, and it is broader than a context-specific interceptor.
public sealed class EfDiagnosticObserver : IObserver<DiagnosticListener>, IObserver<KeyValuePair<string, object?>>{ private IDisposable? _efSubscription; public void OnNext(DiagnosticListener listener) { if (listener.Name == DbLoggerCategory.Name) { _efSubscription = listener.Subscribe(this); } } public void OnNext(KeyValuePair<string, object?> evt) { if (evt.Key.EndsWith("CommandExecuted", StringComparison.Ordinal)) { Console.WriteLine($"EF diagnostic event: {evt.Key}; trace={Activity.Current?.TraceId}"); } } public void OnCompleted() => _efSubscription?.Dispose(); public void OnError(Exception error) => Console.Error.WriteLine(error);}
A real observer should filter aggressively and understand each
payload type rather than reflect every object. The point of this
example is topology: subscribing to
DiagnosticListener.AllListeners can observe
process-wide EF events, while an interceptor is registered on
configured context instances. Register once with
DiagnosticListener.AllListeners.Subscribe(new
EfDiagnosticObserver()), retain the returned subscription for the intended application
lifetime, and dispose it during shutdown.
3. EF Core 10 exposes first-class System.Diagnostics.Metrics
Since EF Core 9, the preferred continuous numeric metrics use
the standard .NET Metrics API. The meter name is
Microsoft.EntityFrameworkCore. The currently
documented instruments include:
| Instrument | Type of question it helps answer |
|---|---|
microsoft.entityframeworkcore.active_dbcontexts
|
Are contexts accumulating/leaking, remembering that pooled idle contexts are included? |
microsoft.entityframeworkcore.queries |
How much EF query activity is the process producing? |
microsoft.entityframeworkcore.savechanges
|
How often is SaveChanges invoked? |
...compiled_query_cache_hits /
...misses
|
Are query shapes stabilizing after warm-up? |
...execution_strategy_operation_failures
|
Are provider execution-strategy operations failing/retrying? |
...optimistic_concurrency_failures |
Are business concurrency conflicts occurring? |
These are application/EF signals. They do not tell you database CPU, lock waits, buffer-cache efficiency, or server connection saturation; those need provider/database telemetry.
4. Export the EF meter with stable OpenTelemetry packages
The mandatory lesson does not require a telemetry backend. A free console exporter is sufficient to prove that EF metrics can be collected. The stable OpenTelemetry hosting line is 1.18.0 as of this chapter review.
dotnet add package OpenTelemetry.Extensions.Hosting --version 1.18.0dotnet add package OpenTelemetry.Exporter.Console --version 1.18.0
builder.Services.AddOpenTelemetry() .WithMetrics(metrics => metrics .AddMeter("Microsoft.EntityFrameworkCore") .AddConsoleExporter());
Run ServiceHub queries and SaveChanges calls, then
inspect the emitted measurements. Do not add high-cardinality
dimensions such as work-order numbers or SQL text to your own
metrics; metrics are for aggregation, not payload storage.
5. EventCounters are the legacy observation path
EF Core still exposes legacy counters, and
dotnet-counters can monitor them without
application code. This is useful on an existing process or when
your tooling has not moved to
System.Diagnostics.Metrics. Do not assume the
names/aggregation semantics are identical to the new meter.
dotnet tool install --global dotnet-countersdotnet-counters monitor --counters Microsoft.EntityFrameworkCore --process-id <PID>
Typical output includes active contexts, queries/sec and total queries, query-cache hit rate, SaveChanges counts, execution-strategy failures, and optimistic concurrency failures. Use it as a live diagnostic view, not a durable time-series store.
6. Correlation begins at the application Activity
An HTTP request, message handler, or scheduled job should
establish a trace context before data access. In .NET,
Activity represents the span context that flows
through asynchronous work. Logs can include
Activity.Current.TraceId; diagnostic events and
instrumentation libraries can attach database spans to the same
trace.
public static class ServiceHubTelemetry{ public static readonly ActivitySource Source = new("ServiceHub.Workflows", "1.0.0");}using var activity = ServiceHubTelemetry.Source.StartActivity("dispatch.rebuild");activity?.SetTag("servicehub.operation", "dispatch.rebuild");await RebuildDispatchQueueAsync(db, cancellationToken);logger.LogInformation( "Dispatch rebuild completed; TraceId={TraceId}", Activity.Current?.TraceId);
Do not tag the activity with customer name, work-order summary, or arbitrary SQL. Stable operation names and low-cardinality tags are enough to find the trace and then inspect authorized evidence.
7. Optional EF spans: the current OpenTelemetry EF package is still beta
OpenTelemetry.Instrumentation.EntityFrameworkCore
consumes EF relational diagnostics and creates trace activities
for outgoing database operations. At review time the latest
package is 1.18.0-beta.1; NuGet labels the
component beta because database semantic conventions remain
experimental. It is compatible with .NET 10 but must be treated
as prerelease.
dotnet add package OpenTelemetry.Instrumentation.EntityFrameworkCore --version 1.18.0-beta.1 --prerelease
builder.Services.AddOpenTelemetry() .WithTracing(tracing => tracing .AddSource("ServiceHub.Workflows") .AddEntityFrameworkCoreInstrumentation() .AddConsoleExporter());
The package documentation explicitly warns that query-parameter telemetry is experimental and disabled by default because parameters can contain sensitive data. Keep it disabled for this course.
The current OpenTelemetry EF instrumentation documents that its Activity duration covers underlying command/query execution to success determination but does not include time spent enumerating all returned rows. Correlate it with request timing and database evidence before interpreting “DB span latency.”
8. Failure case: use TraceId, SQL text, and user IDs as metric labels
A developer exports a metric
ef_query_duration_ms with labels
trace_id, work_order_number, and full
SQL. Every request creates a new time-series. The metrics
backend consumes excessive memory/storage and now contains
sensitive query payloads.
Repair: keep metrics low-cardinality: operation
(dispatch.queue), provider (sqlite),
outcome (success/error), perhaps a
bounded query-template identifier. Put trace IDs on logs/spans
where exemplars or links are appropriate, not as a metric
dimension.
9. Mandatory lab: correlate one request/job, metrics, and EF work
-
Start a ServiceHub operation inside an application-owned
ActivitySourceactivity. -
Enable the stable OpenTelemetry console exporter for the
Microsoft.EntityFrameworkCoremeter or observe legacy counters withdotnet-counters. -
Execute a query and one
SaveChanges; verify query/save metrics move. - Log the current trace ID in the application operation.
- Optionally install the beta EF instrumentation in a disposable branch/lab and observe relational spans under the same trace context.
- Verify no customer values, work-order summaries, SQL parameters, or connection-string secrets appear in exported attributes.
- Record which signals are EF-provided versus application-, provider-, or database-provided.
10. Production judgment
Use the EF meter for process health and query-cache/concurrency/retry signals, DiagnosticSource when a process-wide rich-event consumer is justified, and Activity/OpenTelemetry for cross-component correlation. Keep legacy EventCounters for compatibility/tooling, not as the conceptual center of a new dashboard. Treat the beta EF OpenTelemetry instrumentation as optional and version-pinned. The next lesson goes one level deeper: interceptors can not only observe but modify or suppress EF operations, which makes them powerful—and dangerous.
Check your understanding
- What is the EF Core Meter name for current metrics?
- Why are EventCounters called a legacy path here?
- When is DiagnosticListener preferable to a per-context interceptor?
- Is OpenTelemetry.Instrumentation.EntityFrameworkCore a stable required dependency in this chapter?
- Why should TraceId not be a metric label?
- Does an EF span duration necessarily include reading/materializing every result row?
Review the answers
1. Microsoft.EntityFrameworkCore.
2. EF Core 9 introduced standard System.Diagnostics.Metrics as the preferred current metrics API; EventCounters remain available for older tooling/compatibility.
3. When a process-wide consumer needs rich EF events across contexts and does not need to modify/suppress operations.
4. No. The current package is beta/prerelease and is only an optional observation path.
5. It is essentially unique per trace, creating high-cardinality time-series; keep trace IDs in traces/logs or exemplars.
6. No. The current instrumentation documents that command activity duration does not include full result-set enumeration, so correlate with end-to-end timing.
Authoritative references
Observability and diagnostics are version-, provider-, and topology-sensitive. Re-check these primary sources before standardizing a production telemetry contract.
- Metrics in EF Core — System.Diagnostics.Metrics instruments and legacy EventCounters
- Using diagnostic listeners in EF Core — process-wide EF DiagnosticListener events
- Overview of logging and interception - EF Core — when to use diagnostic listeners, logging, events and interceptors
- .NET observability with OpenTelemetry — ILogger, Meter and ActivitySource relationship to OpenTelemetry
- OpenTelemetry .NET — current traces, metrics and logs guidance
- OpenTelemetry.Instrumentation.EntityFrameworkCore — beta relational EF instrumentation and parameter/privacy warnings