Instrument your .NET application with OpenTelemetry
This walkthrough shows how to add observability to your .NET application using the OpenTelemetry .NET libraries and tools.
Feature
Supported
Automatic instrumentation
Yes
Traces
Yes
Metrics
Yes
Logs
Yes
Prerequisites
Dynatrace version 1.254+
For tracing, W3C Trace Context is enabled
Go to Settings > Preferences > OneAgent features.
Turn on Send W3C Trace Context HTTP headers.
Get the Dynatrace access details
Determine the API base URL
For details on how to assemble the base OTLP endpoint URL, see Export with OTLP. The URL should end in /api/v2/otlp.
Get API access token
The access token for ingesting traces, logs, and metrics can be generated under Access Tokens.
Export with OTLP has more details on the format and the necessary access scopes.
Choose how you want to instrument your application
For .NET, OpenTelemetry supports automatic and manual instrumentation (or a combination of both).
Which instrumentation should I choose?
It's a good idea to start with automatic instrumentation and add manual instrumentation if the automatic approach doesn't work or doesn't provide enough information.
Automatically instrument your application optional
.NET automatic instrumentation can be configured either during development or later after deployment.
Enrichment with OneAgent
It is currently not possible to enrich automatically instrumented services with host-relevant information. To achieve this, you'd need to switch to manual instrumentation.
Find the right instrumentation library for your .NET framework (the full list of currently supported libraries can be found here) and add its current version as <PackageReference> to your .csproj project file.
Instead of hardcoding the URL and token, you might also consider reading them from storage specific to your application framework (for example, environment variables or framework secrets).
Add the initOpenTelemetry method to your startup class and invoke it as early as possible during your application startup. This initializes OpenTelemetry for the Dynatrace backend and creates default tracer and meter providers.
The file read operations, parsing the dt_metadata files in the example code, attempt to read the OneAgent data files to enrich the OTLP request and ensure that all relevant topology information is available within Dynatrace.
Instead of hardcoding the URL and token, you might also consider reading them from storage specific to your application framework (for example, environment variables or framework secrets).
Add the initOpenTelemetry method to your startup class and invoke it as early as possible during your application startup. This initializes OpenTelemetry for the Dynatrace backend and creates default tracer and meter providers.
privatestaticvoidinitOpenTelemetry(){
var port = System.Environment.GetEnvironmentVariable("PORT")??"8080";
var appBuilder = WebApplication.CreateBuilder();
appBuilder.WebHost.ConfigureKestrel(options =>{
options.ListenAnyIP(Convert.ToInt32(port));// hardcoding the port
The file read operations, parsing the dt_metadata files in the example code, attempt to read the OneAgent data files to enrich the OTLP request and ensure that all relevant topology information is available within Dynatrace.
Add tracing
Using MyActivitySource from the setup step, we can now start new activities (traces):
using var activity = Startup.MyActivitySource.StartActivity("Call to /myendpoint", ActivityKind.Consumer, parentContext.ActivityContext);
activity?.SetTag("http.method","GET");
activity?.SetTag("net.protocol.version","1.1");
In the above code, we:
Create a new activity (span) and name it "Call to /myendpoint"
Add two tags (attributes), following the semantic naming convention, specific to the action of this span: information on the HTTP method and version
The activity will be automatically set as the current and active span until the execution flow leaves the current method scope. Subsequent activities will automatically become child spans.
Collect metrics
To instantiate new metric instruments, we first need a meter object.
privatestaticreadonly Meter meter =newMeter("my-meter","1.0.0");//TODO Replace with the name of your meter
With meter, we can now create individual instruments, such as a counter.
We can now invoke the Add() method of counter to record new values with our counter and save additional attributes (for example, action.type).
counter.Add(1,new("ip","an ip address here"),new("some other key","some other value"));
Connect logs
With the loggerFactoryOT variable, we initialized under Setup, we can now create individual logger instances, which will pass logged information straight to the configured OpenTelemetry endpoint at Dynatrace.
var logger = loggerFactoryOT.CreateLogger<Startup>();
Context propagation is particularly important when network calls (for example, REST) are involved.
If you are using automatic instrumentation and your networking libraries are covered by automatic instrumentation, this will be automatically taken care of by the instrumentation libraries. Otherwise, your code needs to take this into account.
Extracting the context when receiving a request
In the following example, we assume that we have received a network call via System.Web.HttpRequest and we define a CompositeTextMapPropagator instance to fetch the context information from the HTTP headers. We then pass that instance to Extract(), returning the context object, which allows us to continue the previous trace with our spans.
var parentContext = propagator.Extract(default, HttpContext.Request, valueGetter);
using var activity = MyActivitySource.StartActivity("my-span", ActivityKind.Consumer, parentContext.ActivityContext);
Injecting the context when sending requests
In the following example, we send a REST request to another service and provide our existing context as part of the HTTP headers of our request.
To do so, we define a TextMapPropagator instance, which adds the respective information. Once we have instantiated our REST object, we pass it, along with the context and the setter instance, to Inject(), which will add the necessary headers to the request.
Configure data capture to meet privacy requirements optional
While Dynatrace automatically captures all OpenTelemetry 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 stored.
To view your custom attributes, you need to allow them in the Dynatrace web UI first. To learn how to configure attribute storage and masking, see Attribute redaction.
Verify data ingestion into Dynatrace
Once you have finished the instrumentation of your application, perform a couple of test actions to create and send demo traces, metrics, and logs and verify that they were correctly ingested into Dynatrace.
To do that for traces, go to Distributed Traces or Distributed Traces Classic (latest Dynatrace) and select the Ingested traces tab. If you use OneAgent, select PurePaths instead.
For metrics and logs, go to Metrics or Logs or Logs & Events (latest Dynatrace).