This guide shows you how to add OpenTelemetry to your .NET application by installing NuGet packages and configuring the SDK in your code. Use this approach when you need more control over instrumentation than zero-code provides, or when zero-code instrumentation isn't available for your deployment.
Prerequisites
- .NET SDK 5.0 or later
- A SigNoz Cloud account or self-hosted SigNoz instance
Step 1. Install OpenTelemetry packages
Add the core packages to your project:
dotnet add package OpenTelemetry.Extensions.Hosting
dotnet add package OpenTelemetry.Exporter.OpenTelemetryProtocol
dotnet add package OpenTelemetry.Instrumentation.AspNetCore
Add instrumentation packages based on what your application uses:
# HTTP client calls
dotnet add package OpenTelemetry.Instrumentation.Http
# SQL Server
dotnet add package OpenTelemetry.Instrumentation.SqlClient
# Entity Framework Core
dotnet add package OpenTelemetry.Instrumentation.EntityFrameworkCore
# gRPC
dotnet add package OpenTelemetry.Instrumentation.GrpcNetClient
Step 2. Configure OpenTelemetry in Program.cs
Program.cs
using OpenTelemetry.Exporter;
using OpenTelemetry.Resources;
using OpenTelemetry.Trace;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddOpenTelemetry()
.ConfigureResource(resource => resource
.AddService(serviceName: "my-dotnet-service")
.AddAttributes(new Dictionary<string, object>
{
["deployment.environment"] = builder.Environment.EnvironmentName,
["service.version"] = "1.0.0"
}))
.WithTracing(tracing => tracing
.AddAspNetCoreInstrumentation()
.AddHttpClientInstrumentation()
.AddOtlpExporter(options =>
{
options.Endpoint = new Uri("https://ingest.<region>.signoz.cloud:443");
options.Protocol = OtlpExportProtocol.Grpc;
options.Headers = "signoz-ingestion-key=<your-ingestion-key>";
}));
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Run();
Replace:
my-dotnet-service: Your service name<region>: Your SigNoz Cloud region (us,eu, orin). See endpoints.<your-ingestion-key>: Your SigNoz ingestion key
Step 3. Configure using environment variables (optional)
Instead of hardcoding values, use environment variables:
Program.cs
builder.Services.AddOpenTelemetry()
.ConfigureResource(resource => resource
.AddService(serviceName: Environment.GetEnvironmentVariable("OTEL_SERVICE_NAME") ?? "my-service"))
.WithTracing(tracing => tracing
.AddAspNetCoreInstrumentation()
.AddHttpClientInstrumentation()
.AddOtlpExporter());
Then set environment variables when running:
OTEL_SERVICE_NAME=my-dotnet-service \
OTEL_EXPORTER_OTLP_ENDPOINT=https://ingest.<region>.signoz.cloud:443 \
OTEL_EXPORTER_OTLP_HEADERS=signoz-ingestion-key=<your-ingestion-key> \
dotnet run
Step 4. Add database instrumentation (optional)
SQL Server
dotnet add package OpenTelemetry.Instrumentation.SqlClient
Program.cs
builder.Services.AddOpenTelemetry()
.WithTracing(tracing => tracing
.AddAspNetCoreInstrumentation()
.AddHttpClientInstrumentation()
.AddSqlClientInstrumentation(options =>
{
options.SetDbStatementForText = true;
options.RecordException = true;
})
.AddOtlpExporter(/* ... */));
Entity Framework Core
dotnet add package OpenTelemetry.Instrumentation.EntityFrameworkCore
Program.cs
builder.Services.AddOpenTelemetry()
.WithTracing(tracing => tracing
.AddAspNetCoreInstrumentation()
.AddHttpClientInstrumentation()
.AddEntityFrameworkCoreInstrumentation()
.AddOtlpExporter(/* ... */));
Redis (StackExchange.Redis)
dotnet add package OpenTelemetry.Instrumentation.StackExchangeRedis
Program.cs
var redis = ConnectionMultiplexer.Connect("localhost:6379");
builder.Services.AddOpenTelemetry()
.WithTracing(tracing => tracing
.AddAspNetCoreInstrumentation()
.AddRedisInstrumentation(redis)
.AddOtlpExporter(/* ... */));
Validate
- Generate traffic by making requests to your application.
- Open SigNoz and go to Services.
- Look for your service name in the list.
- Click on your service to view traces, latency, and error rates.
Troubleshooting
How do I debug locally?
Add the console exporter to see traces in your terminal:
dotnet add package OpenTelemetry.Exporter.Console
.WithTracing(tracing => tracing
.AddAspNetCoreInstrumentation()
.AddConsoleExporter()
.AddOtlpExporter(/* ... */));
Traces not appearing?
- Check the endpoint URL format (include protocol and port)
- Verify the ingestion key has no extra spaces
- Ensure your firewall allows outbound traffic to the endpoint
Missing spans for certain libraries?
- Verify you've added the correct instrumentation package
- Check the package is compatible with your library version
- Some libraries need explicit configuration (like SQL statement capture)
gRPC vs HTTP export
By default, AddOtlpExporter() uses gRPC. If you're behind a proxy that doesn't support HTTP/2:
.AddOtlpExporter(options =>
{
options.Endpoint = new Uri("https://ingest.<region>.signoz.cloud:443/v1/traces");
options.Protocol = OtlpExportProtocol.HttpProtobuf;
options.Headers = "signoz-ingestion-key=<your-key>";
});
Next steps
- Manual Instrumentation to create custom spans for business operations
- Setup alerts for your traces to get notified on errors and latency