Open Telemetry Exporter with .NET6

2022, Feb 20

You can export traces and metrics from Open Telemetry by implementing custom exporters, and in this article, I would like to show how to create a Custom Console Exporter as an example.

Observability

Looking at how Open Telemetry Logs are displayed on Console, it feels that a lot of information is displayed in separate chunks, which makes it very challenging for us to use the Console display as a way of troubleshooting applications. To be able to do that you need to visualize traces on a tool like Zipkin1 or Azure Monitor.

In my case, when working with Docker Containers, I wanted to visualize the Logs concisely, so I decided to create a Custom Console Exporter to allow me to do that. This becomes very important when you need to troubleshoot applications by viewing the Logs, so you can validate the output and take decisions about it.

Custom Exporter

The code below shows the implementation of an Exporter, by inheriting from BaseExporter<Activity>, and overriding the implementation of Export method.

using System.Diagnostics;
using System.Text.Json;
using OpenTelemetry;

namespace OpenTelemetryExporter
{
    class MyConsoleExporter : BaseExporter<Activity>
    {
        private readonly string _name;

        public MyConsoleExporter(string name = "MyConsoleExporter")
        {
            this._name = name;
        }

        public override ExportResult Export(in Batch<Activity> batch)
        {
            // SuppressInstrumentationScope should be used to prevent exporter
            // code from generating telemetry and causing live-loop.
            using var scope = SuppressInstrumentationScope.Begin();

            foreach (var activity in batch)
            {
                ConsoleWriteData($"{activity.StartTimeUtc:o}");
                ConsoleWriteData($"{activity.Status}");
                ConsoleWriteData("NAME", activity.DisplayName);
                ConsoleWriteData("TRACE-ID", activity.TraceId.ToString());
                ConsoleWriteData("BAGGAGE", JsonSerializer.Serialize(activity.Baggage));

                foreach (var ev in activity.Events)
                {
                    ConsoleWriteData("EVENT", $"{ev.Name}:{JsonSerializer.Serialize(ev.Tags)}");
                }

                Console.WriteLine();
            }

            return ExportResult.Success;
        }

        private void ConsoleWriteData(string title, string? content = null)
        {
            Console.Write(!string.IsNullOrEmpty(content) ? $"[{title} \"{content}\"]" : $"[{title}]");
        }
    }
}

Adding the Exporter

The exporter needs to be hooked up to the TraceProviderBuilder, and to do that the following extension was created, the AddMyConsoleExporter extension:

using OpenTelemetry;
using OpenTelemetry.Trace;

namespace OpenTelemetryExporter
{
    static class LoggerExtensions
    {
        public static TracerProviderBuilder AddMyConsoleExporter(this TracerProviderBuilder builder)
        {
            if (builder == null)
            {
                throw new ArgumentNullException(nameof(builder));
            }

            return builder.AddProcessor(new BatchActivityExportProcessor(new MyConsoleExporter()));
        }
    }
}

The extension is then added to the providerBuilder, and it will allow for Traces to be logged on Console:

builder.Services.AddOpenTelemetryTracing(providerBuilder =>
{
    providerBuilder
        .AddMyConsoleExporter() // this is a custom exporter
        .AddSource(serviceName)
        .SetResourceBuilder(
            ResourceBuilder.CreateDefault()
                .AddService(serviceName: serviceName, serviceVersion: serviceVersion))
        .AddAspNetCoreInstrumentation()
        .AddAzureMonitorTraceExporter(o =>
        {
            o.ConnectionString = builder.Configuration.GetSection("AzureMonitorTrace").Value;
        });
});

For more details about the AzureMonitorTraceExporter, check this article

Download this example on PlayGoKids repository.

Visualize the Console

If you run the docker in your Visual Studio, you'll be able to see the details printed as part of the docker Logs:

Docker Console

If I run the solution as a regular Console, I get the same output available on Docker Logs:

Console

Compare this Console with the one from this article to see the differences.


  1. Zipkin link