.NET OpenTelemetry instrumentation
This tutorial demonstrates how to instrument .NET applications to capture logs, metrics and traces using OpenTelemetry, and send them to Coralogix.
The OpenTelemetry .NET instrumentation for metrics, traces, and logs is currently stable. You can find the OpenTelemetry .NET SDK documentation here.
Note
The examples provided here do not represent a concrete standard for instrumenting .NET applications but serve as guides for basic implementation. You should review your codebase and determine the best approach to manual or auto instrumentation for your specific project.
Prerequisites
- Coralogix domain associated with your account.
- Coralogix Send-Your-Data API key.
- .NET SDK 6.0 or later installed.
Auto-Instrumentation
The OpenTelemetry .NET Auto-Instrumentation enables you to send traces and metrics from .NET applications to Coralogix without modifying your source code.
Required Configuration
# Coralogix endpoint and authentication
export OTEL_EXPORTER_OTLP_ENDPOINT="https://ingress.<CORALOGIX_REGION>.coralogix.com:443"
export OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer <your-api-key>"
export OTEL_EXPORTER_OTLP_PROTOCOL="grpc"
# Service identification
export OTEL_SERVICE_NAME="my-dotnet-service"
export OTEL_RESOURCE_ATTRIBUTES="application.name=my-app,cx.application.name=my-app,cx.subsystem.name=my-subsystem"
Replace <CORALOGIX_REGION> with your Coralogix domain: eu1, eu2, us1, us2, ap1, or ap2.
For complete setup instructions including profiler configuration, see the OpenTelemetry .NET Auto-Instrumentation GitHub repository and Zero Code Configuration methods and settings.
Manual Instrumentation
Before instrumenting logs, traces, or metrics, we will set up Coralogix details and a shared OpenTelemetry Resource. You'll reuse these across all signals.
1. Create a new .NET console application
2. Set the environment variables required for Coralogix
export CORALOGIX_API_KEY="<your-coralogix-api-key>"
export CORALOGIX_APP_NAME="my-app"
export CORALOGIX_SUBSYSTEM_NAME="my-subsystem"
export CORALOGIX_DOMAIN="EU1"
Domain Options: EU1, EU2, US1, US2, AP1, AP2
3. Install the necessary NuGet packages
dotnet add package OpenTelemetry --version 1.9.0
dotnet add package OpenTelemetry.Exporter.OpenTelemetryProtocol --version 1.9.0
dotnet add package OpenTelemetry.Extensions.Hosting --version 1.9.0
dotnet add package Microsoft.Extensions.Logging --version 8.0.0
We will add signal-specific packages later in the corresponding sections.
4. Set up Coralogix configuration and shared Resource
using System.Diagnostics;
using Microsoft.Extensions.Logging;
using OpenTelemetry;
using OpenTelemetry.Logs;
using OpenTelemetry.Resources;
using OpenTelemetry.Trace;
namespace CoralogixOtelExample;
class Program
{
private static readonly ActivitySource ActivitySource = new("CoralogixOtelExample", "1.0.0");
static void Main(string[] args)
{
try
{
var config = LoadConfiguration();
var resourceBuilder = BuildResourceAttributes(config);
Console.WriteLine("OpenTelemetry configured successfully");
}
catch (InvalidOperationException ex)
{
Console.Error.WriteLine($"Configuration Error: {ex.Message}");
Environment.Exit(1);
}
}
private static CoralogixConfig LoadConfiguration()
{
var domain = Environment.GetEnvironmentVariable("CORALOGIX_DOMAIN") ?? "EU1";
var apiKey = Environment.GetEnvironmentVariable("CORALOGIX_API_KEY");
var applicationName = Environment.GetEnvironmentVariable("CORALOGIX_APP_NAME") ?? "dotnet-otel-example";
var subsystemName = Environment.GetEnvironmentVariable("CORALOGIX_SUBSYSTEM_NAME") ?? "example-subsystem";
if (string.IsNullOrWhiteSpace(apiKey))
{
throw new InvalidOperationException(
"CORALOGIX_API_KEY is required. Set it via environment variable.");
}
return new CoralogixConfig
{
Domain = domain,
ApiKey = apiKey,
ApplicationName = applicationName,
SubsystemName = subsystemName
};
}
private static ResourceBuilder BuildResourceAttributes(CoralogixConfig config)
{
return ResourceBuilder.CreateDefault()
.AddService(
serviceName: config.SubsystemName,
serviceNamespace: config.ApplicationName,
serviceVersion: "1.0.0")
.AddAttributes(new Dictionary<string, object>
{
["cx.application.name"] = config.ApplicationName,
["cx.subsystem.name"] = config.SubsystemName
});
}
}
record CoralogixConfig
{
public required string Domain { get; init; }
public required string ApiKey { get; init; }
public required string ApplicationName { get; init; }
public required string SubsystemName { get; init; }
}
5. Running the example
Logs
For logging, we will initialize a LoggerProvider that integrates with the standard Microsoft.Extensions.Logging framework. This allows you to use familiar logging patterns while automatically exporting to Coralogix.
1. Configure the Logger Provider
// Add this method to the Program class
private static ILoggerFactory ConfigureLogging(ResourceBuilder resourceBuilder, CoralogixConfig config)
{
return LoggerFactory.Create(builder =>
{
builder.SetMinimumLevel(LogLevel.Information);
builder.AddOpenTelemetry(options =>
{
options.SetResourceBuilder(resourceBuilder);
options.AddOtlpExporter(otlpOptions => ConfigureOtlpExporter(otlpOptions, config));
options.IncludeFormattedMessage = true;
options.IncludeScopes = true;
});
});
}
private static void ConfigureOtlpExporter(OpenTelemetry.Exporter.OtlpExporterOptions options, CoralogixConfig config)
{
options.Endpoint = new Uri($"https://ingress.{config.Domain}.coralogix.com:443");
options.Protocol = OpenTelemetry.Exporter.OtlpExportProtocol.Grpc;
options.Headers = $"Authorization=Bearer {config.ApiKey}";
}
2. Use the logger to emit logs
static void Main(string[] args)
{
try
{
var config = LoadConfiguration();
var resourceBuilder = BuildResourceAttributes(config);
using var loggerFactory = ConfigureLogging(resourceBuilder, config);
var logger = loggerFactory.CreateLogger<Program>();
// Emit logs
logger.LogInformation("Hello from Coralogix .NET OpenTelemetry!");
logger.LogWarning("This is a warning message");
logger.LogError("This is an error message");
// Structured logging
logger.LogInformation("User {UserId} performed {Action}", "user-123", "login");
// Give time for logs to be exported
Thread.Sleep(5000);
}
catch (InvalidOperationException ex)
{
Console.Error.WriteLine($"Configuration Error: {ex.Message}");
Environment.Exit(1);
}
}
After a successful run, you can view your logs in the Coralogix Logs dashboard.
Traces
For tracing, we will initialize an OpenTelemetry TracerProvider that provides ActivitySource to create spans. In this example, we'll manually instrument a simple operation.
1. Configure the Trace Provider
// Add this method to the Program class
private static TracerProvider ConfigureTracing(ResourceBuilder resourceBuilder, CoralogixConfig config)
{
return Sdk.CreateTracerProviderBuilder()
.SetResourceBuilder(resourceBuilder)
.AddSource(ActivitySource.Name)
.AddOtlpExporter(options => ConfigureOtlpExporter(options, config))
.Build();
}
2. Create traces with spans
static void Main(string[] args)
{
try
{
var config = LoadConfiguration();
var resourceBuilder = BuildResourceAttributes(config);
using var tracerProvider = ConfigureTracing(resourceBuilder, config);
using var loggerFactory = ConfigureLogging(resourceBuilder, config);
var logger = loggerFactory.CreateLogger<Program>();
// Create a trace span
using (var activity = ActivitySource.StartActivity("SampleOperation"))
{
activity?.SetTag("user.id", "user-123");
activity?.SetTag("operation.type", "example");
logger.LogInformation("Processing sample operation");
activity?.SetStatus(ActivityStatusCode.Ok);
}
// Give time for telemetry to export
Thread.Sleep(1000);
}
catch (InvalidOperationException ex)
{
Console.Error.WriteLine($"Configuration Error: {ex.Message}");
Environment.Exit(1);
}
}
You can view your traces in the Coralogix Tracing dashboard.
Metrics
For producing metrics, we will set up a MeterProvider, which lets us use a Meter to define and record metrics. Read more about supported metrics in the official documentation.
1. Install the necessary NuGet package for metrics
2. Configure the Meter Provider
using OpenTelemetry.Metrics;
// Add this method to the Program class
private static MeterProvider ConfigureMetrics(ResourceBuilder resourceBuilder, CoralogixConfig config)
{
return Sdk.CreateMeterProviderBuilder()
.SetResourceBuilder(resourceBuilder)
.AddMeter("CoralogixOtelExample")
.AddRuntimeInstrumentation()
.AddOtlpExporter(options => ConfigureOtlpExporter(options, config))
.Build();
}
3. Create and record metrics
using System.Diagnostics.Metrics;
// Add to the Program class
private static readonly Meter Meter = new("CoralogixOtelExample", "1.0.0");
static void Main(string[] args)
{
try
{
var config = LoadConfiguration();
var resourceBuilder = BuildResourceAttributes(config);
using var meterProvider = ConfigureMetrics(resourceBuilder, config);
// Create a counter metric
var requestCounter = Meter.CreateCounter<long>("sample.counter", "requests", "Sample counter metric");
// Record some metrics
for (int i = 0; i < 10; i++)
{
requestCounter.Add(1, new KeyValuePair<string, object?>("status", "success"));
}
// Give time for metrics to export
Thread.Sleep(1000);
}
catch (InvalidOperationException ex)
{
Console.Error.WriteLine($"Configuration Error: {ex.Message}");
Environment.Exit(1);
}
}
You can view your metrics in the Coralogix Metrics dashboard.
Exceptions
For exceptions in logs, pass the exception object to the logger instead of formatting the exception into the log message. OpenTelemetry .NET exports the log following the OpenTelemetry semantic conventions for exceptions in logs.
OpenTelemetry .NET API support
If you are already using Microsoft.Extensions.Logging, log the exception with one of the exception overloads.
1. Log exceptions with Microsoft.Extensions.Logging
static void Main(string[] args)
{
try
{
var config = LoadConfiguration();
var resourceBuilder = BuildResourceAttributes(config);
using var loggerFactory = ConfigureLogging(resourceBuilder, config);
var logger = loggerFactory.CreateLogger<Program>();
try
{
throw new InvalidOperationException("database connection failed");
}
catch (Exception ex)
{
logger.LogError(ex, "Could not process item {ItemId}", 42);
}
Thread.Sleep(1000);
}
catch (InvalidOperationException ex)
{
Console.Error.WriteLine($"Configuration Error: {ex.Message}");
Environment.Exit(1);
}
}
If you use LoggerMessage source generation, keep the first Exception parameter separate from the message template.
Note
OpenTelemetry.Exporter.OpenTelemetryProtocol exports exception logs using the exception semantic conventions starting in 1.8.0.
2. Send exception logs manually with the Logs Bridge API
using OpenTelemetry;
using OpenTelemetry.Logs;
using var loggerProvider = Sdk.CreateLoggerProviderBuilder()
.AddConsoleExporter()
.Build();
var logger = loggerProvider.GetLogger("ManualExceptions");
try
{
throw new InvalidOperationException("database connection failed");
}
catch (Exception ex)
{
LogRecordAttributeList attributes = default;
attributes.RecordException(ex);
logger.EmitLog(
new LogRecordData
{
Body = "Could not process item 42",
Severity = LogRecordSeverity.Error,
SeverityText = "Error",
},
attributes);
}
Note
The Logs Bridge API is experimental. Sdk.CreateLoggerProviderBuilder, LoggerProvider.GetLogger, LogRecordData, and LogRecordAttributeList.RecordException are available in pre-release builds starting in OpenTelemetry.Api 1.6.0-alpha.1.
NLog bridge
If your application uses NLog directly, you can capture exceptions with the NLog bridge in OpenTelemetry .NET Auto-Instrumentation.
1. Enable the NLog bridge
2. Log the exception with NLog
using NLog;
var logger = LogManager.GetCurrentClassLogger();
try
{
throw new InvalidOperationException("database connection failed");
}
catch (Exception ex)
{
logger.Error(ex, "Could not process item {0}", 42);
}
Note
NLog bridge support is available in OpenTelemetry .NET Auto-Instrumentation 1.14.0 and later. Supported NLog versions are >= 5.0.0 and < 7.0.0. If NLog is configured as a Microsoft.Extensions.Logging provider, use the Microsoft.Extensions.Logging path instead of enabling the NLog bridge to avoid duplicate logs.
log4net bridge
If your application uses log4net directly, you can capture exceptions with the log4net bridge in OpenTelemetry .NET Auto-Instrumentation.
1. Enable the log4net bridge
2. Log the exception with log4net
using log4net;
var logger = LogManager.GetLogger(typeof(Program));
try
{
throw new InvalidOperationException("database connection failed");
}
catch (Exception ex)
{
logger.Error("Could not process item 42", ex);
}
Note
log4net bridge support is available in OpenTelemetry .NET Auto-Instrumentation 1.10.0 and later. Supported log4net versions are >= 2.0.13 and < 4.0.0. The bridge is added only when log4net is configured with at least one appender.
Support
Need help?
Our world-class customer success team is available 24/7 to walk you through your setup and answer any questions that may come up.
Feel free to reach out to us via our in-app chat or by sending us an email at [email protected].


