Enhancing .NET Aspire Observability with Seq

.NET Aspire is a toolkit for development and deployment of microservice applications. It is a local orchestrator with support for deployment to production-grade orchestrators like Azure Container Apps or Kubernetes.

One great thing about Aspire is the built-in local development dashboard.

Aspire's built-in local development dashboard

It shows the resources that are included in the solution as well as the observability signals generated by the resources. There are pages for structured logs, traces and metrics with basic search and analysis features. With no additional effort developers get a dashboard that provides a light-weight alternative to the typical observability tools used in production.

Depending upon the system and the developer that may be enough, and a vast improvement over console logging or text files. However, some developers will prefer a more powerful diagnostic experience, even during development.

Thanks to the modular design of Aspire, and the magic of OpenTelemetry, it is easy to enhance Aspire by connecting its telemetry to Seq.

Seq provides a free Individual license that can be used in this way. This post will show how to send Aspire telemetry to Seq, and how to manage a Seq container instance directly in the Aspire dashboard.

Part 1: Sending Aspire Logs and Traces to Seq

Aspire has OpenTelemetry support built-in, which means it can be configured to send telemetry to any OpenTelemetry compatible server, including Seq.

Let's start by creating an Aspire project, if you don't already have one. You will need to have Docker Desktop or Docker Engine, then install the Aspire workload:

dotnet workload update
dotnet workload install aspire

Now create a new Aspire solution:

dotnet new aspire-starter --output AspireWithSeq

The aspire-starter template includes an API service project, a web UI project, and the .NET Aspire projects. You may now launch the solution by running the AppHost project:

cd AspireWithSeq\AspireWithSeq.AppHost
dotnet run
The .NET Aspire Resources dashboard
The aspire-starter example web application

The next step is to get Seq running. On Windows, download and run the installer. On Mac or Linux the following Docker command will do the job:

docker run -e ACCEPT_EULA=Y -p 5341:80 datalust/seq

The ACCEPT_EULA=Y environment variable indicates that you have read and accepted the Seq End User Licence Agreement.

You should now be able to access Seq in your browser - if not, consult the docs for your platform.

A freshly installed Seq Events page

At this stage we have a working Aspire solution, and a Seq instance, but they are not connected. To get Aspire's telemetry into Seq we must configure the application's OTLP exporter to send to Seq.

Find and open the file AspireWithSeq.ServiceDefaults/Extensions.cs. This is where Aspire's OpenTelemetry client is configured. Add a method AddExportToSeq:

static IHostApplicationBuilder AddExportToSeq(this IHostApplicationBuilder builder)
    {
        builder.Services.Configure<OpenTelemetryLoggerOptions>(logging => logging.AddOtlpExporter(opt =>
        {
            opt.Endpoint = new Uri("http://localhost:5341/ingest/otlp/v1/logs");
            opt.Protocol = OtlpExportProtocol.HttpProtobuf;
        }));
        builder.Services.ConfigureOpenTelemetryTracerProvider(tracing => tracing
            .AddSource("MyApp.Source")
            .AddOtlpExporter(opt =>
                {
                    opt.Endpoint = new Uri("http://localhost:5341/ingest/otlp/v1/traces");
                    opt.Protocol = OtlpExportProtocol.HttpProtobuf;
                }
            ));

        return builder;
    }

Replace "MyApp.Source" with the name of your ActivitySource. If, for example, we wished to trace the creation of the weather forecast, we can add a span to the /weatherforecast endpoint in AspireWithSeq.ApiService/Program.cs like this:

ActivitySource source = new("MyApp.Source");

app.MapGet("/weatherforecast", () =>
{
    var min = -20;
    var max = 55;
    
    using var activity = source.StartActivity("Calculating weather with temperature between {Min} and {Max}");
    
    activity?.SetTag("Min", min);
    activity?.SetTag("Max", max);

    var summary = summaries[Random.Shared.Next(summaries.Length)];

    activity?.SetTag("Summary", summary);
    
    var forecast = Enumerable.Range(1, 5).Select(index =>
        new WeatherForecast
        (
            DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
            Random.Shared.Next(min, max),
            summary
        ))
        .ToArray();
    
    return forecast;
});

Notice that when we explicitly create traces using the StartActivity method in this way we are able to add a descriptive name that will be crucial for later communicating what happened in the trace.

💡
The name of the activity source must match what was registered with the OpenTelemetry .NET SDK.

Now add builder.AddExportToSeq(); to the AddOpenTelemetryExporters method:

    private static IHostApplicationBuilder AddOpenTelemetryExporters(this IHostApplicationBuilder builder)
    {
        var useOtlpExporter = !string.IsNullOrWhiteSpace(builder.Configuration["OTEL_EXPORTER_OTLP_ENDPOINT"]);

        if (useOtlpExporter)
        {
            builder.Services.Configure<OpenTelemetryLoggerOptions>(logging => logging.AddOtlpExporter());
            builder.Services.ConfigureOpenTelemetryMeterProvider(metrics => metrics.AddOtlpExporter());
            builder.Services.ConfigureOpenTelemetryTracerProvider(tracing => tracing.AddOtlpExporter());

            builder.AddExportToSeq();
        }

        return builder;
    }

Restart the Aspire solution and log events and traces will now be available in Seq, as well as the Aspire dashboard:

Aspire log events and spans in Seq

Part 2: Including Seq in an Aspire Solution

Aspire is a local development container orchestrator, and Seq can be a local development container. Therefore, the above setup can be improved by including Seq in the Aspire solution. This well cause Seq to start and stop automatically with the rest of the solution.

💡
Aspire is in preview and the API is still changing. The following code is compatible with Aspire 8.0.0-preview.4.24105.20/8.0.100

Add the following to the file AspireWithSeq.AppHost/Program.cs somewhere before builder.Build().Run();:

builder
    .AddResource(new ContainerResource("seq"))
    .WithAnnotation(new EndpointAnnotation(ProtocolType.Tcp, uriScheme: "http", name: "seq", port: 5341, containerPort: 80))
    .WithEnvironment("ACCEPT_EULA", "Y")
    .WithAnnotation(new ContainerImageAnnotation { Image = "datalust/seq", Tag = "latest" });

Now when the Aspire solution is started it will start a Seq container, and include it in the Aspire dashboard:

The Aspire dashboard showing a running datalust/seq container.

Viewing the logs for the seq resource will show Seq's console logs:

The Aspire dashboard "Console logs" tab, showing the terminal output from the running Seq container.

Having the built-in observability tooling is .NET Aspire will be a significant improvement for many developers. To add powerful searching, analysis, dashboarding, and alerts it is easy to enhance the Aspire dashboard experience with Seq.

Liam McLennan

Read more posts by this author.