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.

Advanced150–190 minutesmetrics + trace-correlation labEF Core 10.0.11 · .NET 10.0.11SQLite provider 10.0.11 mandatory baselinedotnet-ef 10.0.11 · SDK 10.0.400Observability reviewed: August 2026

Learning outcomes

01

Explain DiagnosticSource/DiagnosticListener as process-wide rich event instrumentation and distinguish it from per-context interceptors and ordinary logging.

02

Use EF Core 10 System.Diagnostics.Metrics from the Microsoft.EntityFrameworkCore meter and name the currently documented health instruments.

03

Use legacy EventCounters only as a compatibility/CLI observation path and avoid mixing their semantics with the newer Meter instruments.

04

Correlate an application Activity/trace identifier with EF command telemetry and understand where OpenTelemetry fits in the .NET diagnostics stack.

05

Label OpenTelemetry.Instrumentation.EntityFrameworkCore as beta/prerelease and keep query-parameter capture disabled by default.

06

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.

Frozen lab baseline

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.

csharp · minimal diagnostic-listener observer
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.

shell · packages for optional local OpenTelemetry metrics
dotnet add package OpenTelemetry.Extensions.Hosting --version 1.18.0dotnet add package OpenTelemetry.Exporter.Console --version 1.18.0
csharp · collect EF Core metrics
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.

shell · legacy live observation
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.

csharp · application-owned job activity
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.

shell · optional prerelease package
dotnet add package OpenTelemetry.Instrumentation.EntityFrameworkCore   --version 1.18.0-beta.1   --prerelease
csharp · optional EF trace instrumentation
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.

Span duration is not whole-query end-to-end time

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

  1. Start a ServiceHub operation inside an application-owned ActivitySource activity.
  2. Enable the stable OpenTelemetry console exporter for the Microsoft.EntityFrameworkCore meter or observe legacy counters with dotnet-counters.
  3. Execute a query and one SaveChanges; verify query/save metrics move.
  4. Log the current trace ID in the application operation.
  5. Optionally install the beta EF instrumentation in a disposable branch/lab and observe relational spans under the same trace context.
  6. Verify no customer values, work-order summaries, SQL parameters, or connection-string secrets appear in exported attributes.
  7. 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

  1. What is the EF Core Meter name for current metrics?
  2. Why are EventCounters called a legacy path here?
  3. When is DiagnosticListener preferable to a per-context interceptor?
  4. Is OpenTelemetry.Instrumentation.EntityFrameworkCore a stable required dependency in this chapter?
  5. Why should TraceId not be a metric label?
  6. 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.

Keep knowledge open

Help the academy stay free and grow.

If these tutorials save you time, a small donation supports new lessons, technical review, diagrams, examples, and long-term maintenance.

ETHEthereum / ERC-20 only
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0

Send only Ethereum or ERC-20 compatible assets to this address.

\n