Documentation
DocumentationDiscussions
Documentation

OpenTelemetry .NET SDK

.NET applications can use the OpenTelemetry .NET SDK to send logs and traces to Seq. Refer to the OpenTelemetry documentation for more details.

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