Documentation
DocumentationDiscussions

OpenTelemetry .NET SDK

ASP.NET Core and other .NET applications that use Microsoft.Extensions.Logging without Serilog can use the OpenTelemetry .NET SDK to send data. Refer to the OpenTelemetry documentation for more details.

Terminology Translation

OpenTelemetry, .NET and Seq use different names for things. The following table translates between the schemes.

OpenTelemetry.NETSeqDescription
SpanActivitySpanAn event that has a start timestamp, a duration and zero or more child spans / activities.
TracerActivitySource-Entity responsible for creating spans / activities.
AttributeTagPropertyValue attached to an event, such as a log event or a span / activity.
Trace (noun)-TraceA set of related spans.
Log (noun)Log eventLog eventA timestamped value including a human-readable message and attached structured data. Seq uses 'log event' to differentiate from the log data structure in which log events are stored.
Span event-Log eventA lightweight log event within a span. Seq promotes these to regular log events.

Installing the OpenTelemetry .NET SDK

At the terminal, or in the Visual Studio Package Manager Console, type:

dotnet add package OpenTelemetry.Extensions.Hosting
dotnet add package OpenTelemetry.Exporter.OpenTelemetryProtocol
dotnet add package OpenTelemetry.Exporter.Console
dotnet add package OpenTelemetry.Instrumentation.AspNetCore
dotnet add package OpenTelemetry.Instrumentation.Http
dotnet add package --prerelease OpenTelemetry.Instrumentation.SqlClient

The purpose of these dependencies is explained in the following table.

DependencyPurpose
OpenTelemetry.Extensions.HostingProvides a way to register the OpenTelemetry .NET SDK with .NET's app startup and lifetime management.
OpenTelemetry.Exporter.OpenTelemetryProtocolIs the connector that sends telemetry data, via the OpenTelemetry Protocol (OTLP) to a server.
OpenTelemetry.Exporter.ConsoleIs the connector that sends telemetry data to the console. This is optional.
OpenTelemetry.Instrumentation.AspNetCoreAllows the OpenTelemetry client to track ASP.NET Core activities. This is only useful for ASP.NET Core applications.
OpenTelemetry.Instrumentation.HttpAllows the OpenTelemetry client to track information about outgoing HTTP requests made through System.Net.HttpClient and System.Net.HttpWebRequest. This is only useful for applications that make HTTP requests.
OpenTelemetry.Instrumentation.SqlClientAdds instrumentation to the .NET client libraries for Microsoft SQL Server and Azure SQL Database. This is only useful for applications that connect to these databases.

Next, configure the OpenTelemetry (OTEL) client.

ASP.NET Core configuration example:

using System.Diagnostics;
using OpenTelemetry.Resources;
using OpenTelemetry.Trace;
using OpenTelemetry.Exporter;

ActivitySource tracingSource = new("Example.Source");

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddOpenTelemetry()
  .ConfigureResource(r => r.AddService("My Service"))
  .WithTracing(tracing =>
    {
      tracing.AddSource("Example.Source");
      tracing.AddAspNetCoreInstrumentation();
      tracing.AddHttpClientInstrumentation();
      tracing.AddSqlClientInstrumentation();
      tracing.AddConsoleExporter();
      tracing.AddOtlpExporter(opt =>
      {
        opt.Endpoint = new Uri("http://localhost:5341/ingest/otlp/v1/traces");
        opt.Protocol = OtlpExportProtocol.HttpProtobuf;
        opt.Headers = "X-Seq-ApiKey=abcde12345";
      });
    });

var app = builder.Build();

Console application configuration example:

using System.Diagnostics;
using OpenTelemetry;
using OpenTelemetry.Exporter;
using OpenTelemetry.Resources;
using OpenTelemetry.Trace;

ActivitySource tracingSource = new("Example.Source");

using var tracerProvider = Sdk.CreateTracerProviderBuilder()
    .ConfigureResource(r => r.AddService("My Service"))
    .AddSource("Example.Source")    
    .AddHttpClientInstrumentation()
    .AddSqlClientInstrumentation();
    .AddConsoleExporter()
    .AddOtlpExporter(opt =>
    {
        opt.Endpoint = new Uri("http://localhost:5341/ingest/otlp/v1/traces");
        opt.Protocol = OtlpExportProtocol.HttpProtobuf;
        opt.Headers = "X-Seq-ApiKey=abcde12345";
    })    
    .Build();

Let us consider this one step at a time.

ActivitySource tracingSource = new("Example.Source");

This creates a new System.Diagnostic.ActivitySource. It can be used to generate traces.

builder.Services.AddOpenTelemetry()

This begins the OpenTelemetry configuration for an ASP.NET Core application.

.ConfigureResource(r => r.AddService(builder.Environment.ApplicationName))

In OpenTelemetry terminology a resource is something that emits telemetry. It is conventional to ensure that all telemetry is emitted by a resource with a service name.

.WithTracing(tracing =>

Configures the OpenTelemetry SDK to emit trace data.

tracing.AddSource("Example.Source");

Tells the OTEL SDK to listen to the ActivitySource with the name Example.Source. This must exactly match the name of the ActivitySource used to create activities (spans).

tracing.AddAspNetCoreInstrumentation();
tracing.AddHttpClientInstrumentation();
tracing.AddSqlClientInstrumentation();

Register the OTEL SDK to collect trace data from ASP.NET Core, HttpClient and the SQL Server client.

tracing.AddConsoleExporter();

Send trace telemetry to the console.

tracing.AddOtlpExporter(opt =>
{
  opt.Endpoint = new Uri("http://localhost:5341/ingest/otlp/v1/traces");
  opt.Protocol = OtlpExportProtocol.HttpProtobuf;
  opt.Headers = "X-Seq-ApiKey=abcde12345";
});

Send trace telemetry to an OTLP server. Protocol can be OtlpExportProtocol.HttpProtobuf or OtlpExportProtocol.Grpc as required.

🚧

For gRPC to work:

  1. the connection to Seq must be served using HTTPS (TLS)
  2. all intermediate network infrastructure must fully supports HTTP 2.0
  3. Seq must be configured to use the Kestrel web server (not the default on Windows)

For these reasons it is best to start with the HttpProtobuf protocol.

For the HttpProtobuf protocol the correct endpoint is https://seq.example.com/ingest/otlp/v1/traces (replacing https://seq.example.com with your domain, protocol and port).

For the Grpc protocol the correct endpoint is https://seq.example.com (replacing https://seq.example.com with your domain, protocol and port).

If you are using an API Key for ingestion, add an X-Seq-ApiKey header containing the API key. The expected format of the opt.Headers string is a comma-separated list of <key>=<value> pairs.

Generating Traces

A trace is generated by starting activities on an ActivitySource that has been registered with the OpenTelemetry SDK.

The StartActivity method will return null if there are no registered listeners for the ActivitySource, so accessing the Activity must be protected using the null-coalescing operator (?.). If your activities / spans are not appearing check that StartActivity is returning a non-null value.

Activities are given a name, which may include placeholders for interpolating attached data. Key/value data can be attached using the SetTag method.

Activities (spans) created within the scope of other activities will establish a parent-child relationship.

using (Activity activity = tracingSource.StartActivity("Hello {Name}")) 
{
    activity?.SetTag("Name", "world");
      
    using (Activity child = tracingSource.StartActivity("Child activity"))     {
        //
    } 
}
Seq trace visualization showing the hierarchy of recorded spans

Seq trace visualization showing the hierarchy of recorded spans