OpenTelemetry Logs for .NET
This document covers OpenTelemetry Logs for .NET, focusing on Microsoft.Extensions.Logging.
Prerequisites
Make sure your exporter is configured before you start instrumenting code. Follow Getting started with OpenTelemetry .NET or set up Direct OTLP Configuration first.
If you are not familiar with logs terminology like structured logging or log-trace correlation, read the introduction to OpenTelemetry Logs first.
Overview
OpenTelemetry provides two approaches for collecting logs in .NET:
- Logger provider (recommended): Integrate with
Microsoft.Extensions.Loggingto capture logs and correlate them with traces. - Logs API: Use the native OpenTelemetry Logs API directly for maximum control.
The logger provider approach is recommended because it preserves your existing logging patterns while adding trace context automatically.
ASP.NET Core logging
Use AddOpenTelemetry() to configure logging in ASP.NET Core:
using OpenTelemetry.Exporter;
using OpenTelemetry.Logs;
using OpenTelemetry.Resources;
var dsn = Environment.GetEnvironmentVariable("UPTRACE_DSN");
if (string.IsNullOrEmpty(dsn))
{
throw new Exception("UPTRACE_DSN environment variable is required");
}
var serviceName = "myservice";
var serviceVersion = "1.0.0";
var builder = WebApplication.CreateBuilder(args);
builder.Logging.AddOpenTelemetry(logging =>
{
logging.SetResourceBuilder(
ResourceBuilder.CreateDefault()
.AddService(serviceName: serviceName, serviceVersion: serviceVersion)
.AddAttributes(new Dictionary<string, object>
{
["deployment.environment"] = builder.Environment.EnvironmentName
}));
logging.IncludeFormattedMessage = true;
logging.IncludeScopes = true;
logging.AddOtlpExporter(options =>
{
options.Endpoint = new Uri("https://api.uptrace.dev/v1/logs");
options.Headers = $"uptrace-dsn={dsn}";
options.Protocol = OtlpExportProtocol.HttpProtobuf;
});
});
var app = builder.Build();
app.MapGet("/", (ILogger<Program> logger) =>
{
logger.LogInformation("Request handled by {Service}", serviceName);
return "ok";
});
app.Run();
Install the required packages:
dotnet add package OpenTelemetry.Extensions.Hosting
dotnet add package OpenTelemetry.Exporter.OpenTelemetryProtocol
dotnet add package OpenTelemetry.Logs
Console application logging
For console apps or background services, use LoggerFactory directly:
using Microsoft.Extensions.Logging;
using OpenTelemetry.Exporter;
using OpenTelemetry.Logs;
using OpenTelemetry.Resources;
var dsn = Environment.GetEnvironmentVariable("UPTRACE_DSN");
if (string.IsNullOrEmpty(dsn))
{
throw new Exception("UPTRACE_DSN environment variable is required");
}
var serviceName = "myservice";
var serviceVersion = "1.0.0";
using var loggerFactory = LoggerFactory.Create(builder =>
{
builder.AddOpenTelemetry(logging =>
{
logging.SetResourceBuilder(
ResourceBuilder.CreateDefault()
.AddService(serviceName: serviceName, serviceVersion: serviceVersion));
logging.IncludeFormattedMessage = true;
logging.IncludeScopes = true;
logging.AddOtlpExporter(options =>
{
options.Endpoint = new Uri("https://api.uptrace.dev/v1/logs");
options.Headers = $"uptrace-dsn={dsn}";
options.Protocol = OtlpExportProtocol.HttpProtobuf;
});
});
});
var logger = loggerFactory.CreateLogger("Example");
logger.LogInformation("Background job started");
Log-trace correlation
When you log inside an active span, OpenTelemetry includes trace context automatically:
using System.Diagnostics;
using Microsoft.Extensions.Logging;
using OpenTelemetry;
using OpenTelemetry.Exporter;
using OpenTelemetry.Logs;
using OpenTelemetry.Trace;
var dsn = Environment.GetEnvironmentVariable("UPTRACE_DSN");
if (string.IsNullOrEmpty(dsn))
{
throw new Exception("UPTRACE_DSN environment variable is required");
}
var activitySource = new ActivitySource("app_or_package_name");
using var tracerProvider = Sdk.CreateTracerProviderBuilder()
.AddSource(activitySource.Name)
.AddOtlpExporter(options =>
{
options.Endpoint = new Uri("https://api.uptrace.dev/v1/traces");
options.Headers = $"uptrace-dsn={dsn}";
options.Protocol = OtlpExportProtocol.HttpProtobuf;
})
.Build();
using var loggerFactory = LoggerFactory.Create(builder =>
{
builder.AddOpenTelemetry(logging =>
{
logging.AddOtlpExporter(options =>
{
options.Endpoint = new Uri("https://api.uptrace.dev/v1/logs");
options.Headers = $"uptrace-dsn={dsn}";
options.Protocol = OtlpExportProtocol.HttpProtobuf;
});
});
});
var logger = loggerFactory.CreateLogger("Example");
using var activity = activitySource.StartActivity("process-request");
logger.LogInformation("Processing request for {UserId}", "12345");
In Uptrace, the log record is linked to the trace and span where it was emitted.
Best practices
Use structured logs
Prefer structured fields over string concatenation:
logger.LogInformation("Order processed {OrderId} {Status}", 42, "ok");
Record exceptions
Attach exceptions so that stack traces are preserved:
try
{
throw new InvalidOperationException("Something went wrong");
}
catch (Exception ex)
{
logger.LogError(ex, "Processing failed");
}