hight performance logging and tracing c#

High-performance logging e tracing in .NET con EventSource e DiagnosticSource

Introduzione

Il logging è spesso il primo strumento diagnostico di ogni applicazione .NET, ma non tutti i logger sono uguali.
In scenari ad alte prestazioni — microservizi, pipeline di messaggi, telemetria distribuita o analisi in tempo reale — l’uso di ILogger tradizionali può introdurre overhead non trascurabili, soprattutto con log voluminosi.

Per questi casi, il runtime .NET offre strumenti nativi a basso livello: EventSource e DiagnosticSource.
Entrambi consentono telemetria efficiente, instrumentation interna e integrazione diretta con sistemi di tracciamento come EventPipe, ETW, OpenTelemetry, o Application Insights — senza sacrificare performance.

EventSource: logging low-level, zero-allocation

Cos’è

EventSource è una classe base in System.Diagnostics.Tracing pensata per scrivere eventi di telemetria in modo ad altissime prestazioni.
Nasce in Windows con ETW (Event Tracing for Windows), ma in .NET Core e successivi è stata estesa anche per Linux/macOS tramite EventPipe.

L’obiettivo:
– emettere eventi strutturati, leggibili da strumenti esterni, senza passare da formattazioni di stringhe o allocazioni di oggetti temporanei.


Esempio base

[EventSource(Name = "SampleApp-Processor")]
public sealed class ProcessorEventSource : EventSource
{
    public static readonly ProcessorEventSource Log = new();

    [Event(1, Level = EventLevel.Informational)]
    public void ProcessingStarted(string jobId)
        => WriteEvent(1, jobId);

    [Event(2, Level = EventLevel.Error)]
    public void ProcessingFailed(string jobId, string reason)
        => WriteEvent(2, jobId, reason);
}

Uso:

ProcessorEventSource.Log.ProcessingStarted("JOB-423");
try
{
    // ... elaborazione ...
}
catch (Exception ex)
{
    ProcessorEventSource.Log.ProcessingFailed("JOB-423", ex.Message);
}

Caratteristica chiave: EventSource usa buffer binari a basso overhead, con formattazione differita e supporto nativo in tool come dotnet-trace, PerfView o OpenTelemetry .NET Auto-Instrumentation.

Analisi con EventPipe

Puoi leggere questi eventi in tempo reale con:

dotnet trace collect --process-id 12345

Oppure visualizzarli con strumenti avanzati:

  • PerfView (Windows)
  • dotnet-trace + dotnet-analyze (cross-platform)
  • OpenTelemetry Collector (via exporter EventPipe)

DiagnosticSource: tracing contestuale e propagazione

Mentre EventSource si occupa di log binari performanti, DiagnosticSource fornisce un modo dinamico e contestuale per emettere eventi di tracing che includono payload arbitrari.

È la base di System.Net.Http instrumentation, Entity Framework telemetry, ASP.NET Core Diagnostics, e viene usato internamente da OpenTelemetry .NET SDK.


Esempio pratico

using System.Diagnostics;

var source = new DiagnosticListener("PaymentProcessor");

if (source.IsEnabled("Payment.Started"))
{
    source.Write("Payment.Started", new { OrderId = 1001, Amount = 49.90 });
}

// Simulazione logica pagamento
Thread.Sleep(200);

if (source.IsEnabled("Payment.Completed"))
{
    source.Write("Payment.Completed", new { OrderId = 1001, DurationMs = 200 });
}

Un listener può intercettare questi eventi in runtime:

DiagnosticListener.AllListeners.Subscribe(listener =>
{
    if (listener.Name == "PaymentProcessor")
    {
        listener.Subscribe(evt =>
        {
            Console.WriteLine($"Evento: {evt.Key} => {JsonSerializer.Serialize(evt.Value)}");
        });
    }
});

EventSource vs DiagnosticSource

CaratteristicaEventSourceDiagnosticSource
ScopoLogging binario ad alte prestazioniTracing dinamico e contestuale
OverheadMinimo (buffer nativo)Moderato (JSON/payload)
Tipo di eventoStatico, predefinitoDinamico, runtime
Strumenti compatibiliETW, EventPipe, PerfViewOpenTelemetry, App Insights
Uso idealeHigh-throughput loggingTracing applicativo e correlation context

Best practice: usa EventSource per logging strutturato performante e DiagnosticSource per instrumentation dinamica e propagazione di contesto (trace/span id).

Tracing distribuito con OpenTelemetry

Entrambi gli strumenti si integrano perfettamente con OpenTelemetry:

using var tracerProvider = Sdk.CreateTracerProviderBuilder()
    .AddSource("PaymentProcessor", "SampleApp-Processor")
    .AddConsoleExporter()
    .Build();

AddSource() consente di collegare sia EventSource che DiagnosticSource al pipeline di telemetria unificato.
Il risultato: osservabilità end-to-end, dal tuo codice all’export verso Grafana Tempo, Jaeger o Azure Monitor.

Benchmark e performance

  • EventSource: overhead di circa 20–30 ns/evento (zero allocazioni).
  • DiagnosticSource: ~300–500 ns/evento, ma con maggiore flessibilità.
  • ILogger: ~1–2 µs/evento (dipende dai formatter e sink).

Su microservizi high-throughput, EventSource può ridurre del 90% il costo di log rispetto a ILogger.

Pattern d’uso consigliati

  1. Usa EventSource per log interni o metriche ad alta frequenza.
  2. Espandi DiagnosticSource solo per eventi business/trace distribuiti.
  3. Integra con OpenTelemetry e ActivitySource per correlation ID automatico.
  4. Evita conversioni string-heavy: serializza in binary o JSON lightweight.
  5. Centralizza i listener in un singleton per ridurre le subscription dinamiche.

EventSource e DiagnosticSource rappresentano due pilastri poco conosciuti ma fondamentali della telemetria .NET moderna.
Usati correttamente, consentono un’osservabilità di livello enterprise con overhead minimo — un passo essenziale per costruire sistemi resilienti, tracciabili e performanti.

In un mondo dove ogni millisecondo conta, imparare a misurare con precisione è il primo passo per ottimizzare davvero.

Qual è la differenza tra DiagnosticSource e ActivitySource?
ActivitySource è una specializzazione di DiagnosticSource introdotta con .NET 5, ottimizzata per tracing distribuito compatibile con OpenTelemetry.

Posso combinare ILogger e EventSource?
Sì: molti provider di ILogger (Serilog, ETWLogger, EventPipeLogger) permettono di scrivere verso EventSource mantenendo compatibilità con l’ecosistema Microsoft.Extensions.Logging.

DiagnosticSource è thread-safe?
Sì, l’emissione di eventi è thread-safe, ma i payload devono essere immutabili per garantire coerenza.

Posso usare DiagnosticSource in Blazor o MAUI?
Sì, ma gli strumenti di tracing esterni sono più limitati: consigliato solo per telemetria interna.

Dove trovo gli eventi generati?

  • dotnet-trace collect
  • Visual Studio Diagnostic Tools
  • OpenTelemetry Collector / Jaeger
  • PerfView per Windows

Link di approfondimento


Pubblicato

in

da

Tag: