Instrument Dotnet applications with OpenTelemetry
This guide shows how to instrument your .NET application with OpenTelemetry and export the traces to Dynatrace.
- To learn more about how Dynatrace works with OpenTelemetry, see Send data to Dynatrace with OpenTelemetry.
- To learn how to export the metrics to Dynatrace with OpenTelemetry Instrument .NET applications with OpenTelemetry Metrics.
Prerequisites
- Dynatrace version 1.222+
- W3C Trace Context is enabled
- From the Dynatrace menu, go to Settings > Server-side service monitoring > Deep monitoring > Distributed tracing.
- Turn on Send W3C Trace Context HTTP headers.
Overview
To monitor your .NET application with OpenTelemetry you need to
Instrument your application
Send the data to Dynatrace
Configure context propagation
Restart your application and verify the data in Dynatrace
Configure data capturing to meet privacy requirements
Instrument your application
To instrument automatically, add the package reference of the instrumentations for the frameworks you use to your .csproj
file (see Automatic instrumentation for .NET in OpenTelemetry docs).
Replace <USED_FRAMEWORK>
in the code snippets below with the framework that you use. See GitHub to find your OpenTelemetry.Instrumentation.<USED_FRAMEWORK>
.
<PackageReference Include="OpenTelemetry.Instrumentation.<USED_FRAMEWORK>" Version="<Version>" />
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.4.0" />
To instrument manually, make sure to call the ActivitySource initialized before. You can give it a name and add custom attributes.
using var activity = MyActivitySource.StartActivity("my-span");
activity?.SetTag("my-key-1","my-value-1"); //TODO Add attributes
Send the data to Dynatrace
To send data to Dynatrace, add the following package references to your .csproj file and add the code snippet below:
<PackageReference Include="OpenTelemetry" Version="1.4.0" />
<PackageReference Include="OpenTelemetry.Api" Version="1.4.0" />
using OpenTelemetry;
using OpenTelemetry.Trace;
using OpenTelemetry.Exporter;
private const string activitySource = "Dynatrace.DotNetApp.Sample";
public static readonly ActivitySource MyActivitySource = new ActivitySource(activitySource);
List<KeyValuePair<string, object>> dt_metadata = new List<KeyValuePair<string, object>>();
foreach (string name in new string[] {"dt_metadata_e617c525669e072eebe3d0f08212e8f2.properties", "/var/lib/dynatrace/enrichment/dt_metadata.properties"}) {
try {
foreach (string line in System.IO.File.ReadAllLines(name.StartsWith("/var") ? name : System.IO.File.ReadAllText(name))) {
var keyvalue = line.Split("=");
dt_metadata.Add( new KeyValuePair<string, object>(keyvalue[0], keyvalue[1]));
}
}
catch { }
}
Action<ResourceBuilder> configureResource = r => r
.AddService(serviceName: "dotnet-quickstart") //TODO Replace with the name of your application
.AddAttributes(dt_metadata);
AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);
Sdk.CreateTracerProviderBuilder()
.ConfigureResource(configureResource)
.SetSampler(new AlwaysOnSampler())
.AddSource(MyActivitySource.Name)
.Add<USED_FRAMEWORK>Instrumentation() // TODO Add <instrumentations>, e.g. .AddHttpClientInstrumentation()
// TODO Add <exporter> in case of no OneAgent (see below)
.Build();
using OpenTelemetry;
using OpenTelemetry.Trace;
using OpenTelemetry.Exporter;
private const string activitySource = "Dynatrace.DotNetApp.Sample";
public static readonly ActivitySource MyActivitySource = new ActivitySource(activitySource);
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
List<KeyValuePair<string, object>> dt_metadata = new List<KeyValuePair<string, object>>();
foreach (string name in new string[] {"dt_metadata_e617c525669e072eebe3d0f08212e8f2.properties", "/var/lib/dynatrace/enrichment/dt_metadata.properties"}) {
try {
foreach (string line in System.IO.File.ReadAllLines(System.IO.File.ReadAllText(name))) {
var keyvalue = line.Split("=");
dt_metadata.Add( new KeyValuePair<string, object>(keyvalue[0], keyvalue[1]));
}
}
catch { }
}
Action<ResourceBuilder> configureResource = r => r
.AddService(serviceName: "dotnet-quickstart") //TODO Replace with the name of your application
.AddAttributes(dt_metadata);
AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);
var token = "Api-Token <TOKEN>"; //TODO Replace <TOKEN> with your API token as mentioned in the next step
services.AddOpenTelemetry()
.ConfigureResource(configureResource)
.WithTracing(builder =>
{
builder
.SetSampler(new AlwaysOnSampler())
.AddSource(MyActivitySource.Name)
.Add<USED_FRAMEWORK>Instrumentation() // TODO Add <instrumentations>, e.g. .AddHttpClientInstrumentation()
// TODO Add <exporter> in case of no OneAgent (see below)
;
});
}
If you have the OneAgent installed, it will automatically ingest OpenTelemetry data.
Add the following package reference to your .csproj file and replace <exporter>
in the code snippet above with the code below:
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.4.0" />
var token = "Api-Token <TOKEN>"; //TODO Replace <TOKEN> with the authentication token created in section 3 below
//TODO Replace <exporter> in the code above with the following code:
.AddOtlpExporter(otlpOptions =>
{
otlpOptions.Endpoint = new Uri("<URL>"); //TODO Replace <URL> with the URL as determined in section 3 below
otlpOptions.Protocol = OpenTelemetry.Exporter.OtlpExportProtocol.HttpProtobuf;
otlpOptions.Headers = $"Authorization={token}";
})
Lastly, you need to define the correct endpoint and token, to make sure your data arrives where it should be.
- To set the endpoint:
- Use your Environment ID to set the endpoint to which your app will send traces as follows:
- Dynatrace SaaS
https://{your-environment-id}.live.dynatrace.com/api/v2/otlp/v1/traces
- Dynatrace Managed
https://{your-domain}/e/{your-environment-id}/api/v2/otlp/v1/traces
- Dynatrace ActiveGate
https://{your-activegate-endpoint}/e/{your-environment-id}/api/v2/otlp/v1/traces
- You may need to include the port to your ActiveGate endpoint. For example:
https://{your-activegate-endpoint}:9999/e/{your-environment-id}/api/v2/otlp/v1/traces
- If you are running a containerized ActiveGate, you need to use the FQDN of it. For example:
https://{your-activegate-service-name}.dynatrace.svc.cluster.local/e/{your-environment-id}/api/v2/otlp/v1/traces
- You may need to include the port to your ActiveGate endpoint. For example:
- Dynatrace SaaS
- Replace
<URL>
in the code snippet above with your endpoint.
- Use your Environment ID to set the endpoint to which your app will send traces as follows:
- To create an authentication token
- In the Dynatrace menu, go to Access tokens and select Generate new token.
- Provide a Token name.
- In the Search scopes box, search for
Ingest OpenTelemetry traces
and select the checkbox. - Select Generate token.
- Select Copy to copy the token to your clipboard.
- Save the token in a safe place; you can't display it again.
- Replace
<TOKEN>
in the code snippet above with your token.
Configure context propagation optional
The default HttpClient in .NET automatically propagates the context on all outgoing requests, without any extra code. The snippets below are examples on how manual context propagation will work in case that there is no built-in support. For example, if you use manual instrumentation or a framework that is not supported by OpenTelemetry, you need to configure context propagation manually.
In that case, if your application receives a request or calls other applications in a way that is not covered by any existing instrumentation, you need to extract the parent context and create the new span as a child of it to make sure the spans are linked together.
- Whenever receiving an incoming request, you need to extract the parent context and create the new span as a child of it.
using OpenTelemetry.Context.Propagation;
private CompositeTextMapPropagator textMapPropagator = new CompositeTextMapPropagator(new TextMapPropagator[]
{
new TraceContextPropagator(),
new BaggagePropagator(),
});
private static readonly Func<HttpRequest, string, IEnumerable<string>> _httpRequestHeaderValuesGetter
= (request, name) => request.Headers[name];
var parentContext = textMapPropagator.Extract(default, HttpContext.Request, _httpRequestHeaderValuesGetter);
using var activity = MyActivitySource.StartActivity("my-span", ActivityKind.Consumer, parentContext.ActivityContext);
- If your application calls another service, you need to ensure that you propagate the context, adding it to your outgoing request.
using OpenTelemetry.Context.Propagation;
private CompositeTextMapPropagator textMapPropagator = new CompositeTextMapPropagator(new TextMapPropagator[]
{
new TraceContextPropagator(),
new BaggagePropagator(),
});
private static Action<HttpRequestMessage, string, string> _headerValueSetter => (request, name, value) =>
{
request.Headers.Remove(name);
request.Headers.Add(name, value);
};
private static readonly HttpClient client = new();
var request = new HttpRequestMessage
{
RequestUri = new Uri(url),
Method = new HttpMethod("GET"),
};
textMapPropagator.Inject(new PropagationContext(activity!.Context, Baggage.Current), request, _headerValueSetter);
await client.SendAsync(request);
Verify that the traces are ingested into Dynatrace
A few minutes after restarting your app, look for your spans:
- In the Dynatrace menu, go to Distributed traces and select the Ingested traces tab.
- If you use OneAgent, go to Distributed traces and select the PurePaths tab.
- Your spans will be part of an existing PurePath, if the root of your call is already monitored by OneAgent.
If your application does not receive any traffic, there will be no traces.
Configure data capture to meet privacy requirements optional
While Dynatrace automatically captures all OpenTelemetry resource and span attributes, only attribute values specified in the allowlist are stored and displayed in the Dynatrace web UI. This prevents accidental storage of personal data, so you can meet your privacy requirements and control the amount of monitoring data that's stored.
To view your custom span attributes, you need to allow them in the Dynatrace web UI first:
- Span attributes In the Dynatrace menu, go to Settings and select Server-side service monitoring > Span attributes.
- Resource attributes In the Dynatrace menu, go to Settings and select Server-side service monitoring > Resource attributes.