Send traces to Cloud Observability with .NET

This Quick Start guide shows you how to configure your .net applications to send OpenTelemetry traces to Cloud Observability.

This guide does not provide documentation on application instrumentation. For .net-specific information on instrumentation, please see the .net OpenTelemetry getting started guide.

Sending OpenTelemetry data directly to Cloud Observability without a Collector for most developer setups will suffice. For non-development setups, however, it is highly recommended that you send OpenTelemetry data to Cloud Observability by way of the OpenTelemetry Collector. This can be done with or without a Launcher, as we’ll see below.

The sections below contain code snippets only. For full code listings, please see open-telemetry/opentelemetry-dotnet and open-telemetry/opentelemetry-dotnet-contrib in the OpenTelemetry GitHub .NET repos.

Pre-Requisites

Before you get started with sending OpenTelemetry data to Cloud Observability, you will need the following:

Install OpenTelemetry Packages

In your application code, you will need to install dependencies and import OpenTelemetry packages before you can send data to Cloud Observability.

Start by installing the OpenTelemetry Nuget packages.

Start tabs

Console App

1
2
3
dotnet add package OpenTelemetry --version 1.4.0-alpha.2
dotnet add package OpenTelemetry.Exporter.Console --version 1.4.0-alpha.2
dotnet add package OpenTelemetry.Exporter.OpenTelemetryProtocol --version 1.4.0-alpha.2

.NET Core App

1
2
3
4
5
dotnet add package OpenTelemetry --version 1.4.0-alpha.2
dotnet add package OpenTelemetry.Exporter.Console --version 1.4.0-alpha.2
dotnet add package OpenTelemetry.Exporter.OpenTelemetryProtocol --version 1.4.0-alpha.2
dotnet add package OpenTelemetry.Extensions.Hosting --version 1.0.0-rc9.6
dotnet add package OpenTelemetry.Instrumentation.Http --version 1.0.0-rc9.6

End tabs

Where:

  • OpenTelemetry is the OpenTelemetry SDK, a reference implementation of the OpenTelemetry API.
  • OpenTelemetry.Exporter.Console outputs traces to the console during development.
  • OpenTelemetry.Exporter.OpenTelemetryProtocol exports traces to Cloud Observability or the OpenTelemetry Collector using the OpenTelemetry Protocol (OTLP).
  • OpenTelemetry.Extensions.Hosting is used to register the .NET OpenTelemetry provider (.NET Core only)

If you are using a .csproj file, your dependencies would look like this:

Start tabs

Console App

1
2
3
4
5
6
7
8
9
...
  <ItemGroup>
	...
    <PackageReference Include="OpenTelemetry" Version="1.4.0-alpha.2" />
    <PackageReference Include="OpenTelemetry.Exporter.Console" Version="1.4.0-alpha.2" />
    <PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.4.0-alpha.2" />
	...
  </ItemGroup>
...

.NET Core App

1
2
3
4
5
6
7
8
9
10
11
...
  <ItemGroup>
	...
    <PackageReference Include="OpenTelemetry" Version="1.4.0-alpha.2" />
    <PackageReference Include="OpenTelemetry.Exporter.Console" Version="1.4.0-alpha.2" />
    <PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.4.0-alpha.2" />
    <PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.0.0-rc9.6" />
    <PackageReference Include="OpenTelemetry.Instrumentation.Http" Version="1.0.0-rc9.6" />
	...
  </ItemGroup>
...

End tabs

Code Setup

Before you can start sending OpenTelemetry data to Cloud Observability, you will need to:

  • Configure an Exporter (tells OpenTelemetry how to send data to Cloud Observability)
  • Configure a TracerProvider (provides an entrypoint to the OpenTelemetry API, allowing you to create Spans)

Import OpenTelemetry Packages

Now that you’ve installed the OpenTelemetry packages, you will need to import them in your application code.

Open up your application code, and add the following imports to your .cs file:

Start tabs

Console App

1
2
3
using OpenTelemetry;
using OpenTelemetry.Trace;
using OpenTelemetry.Resources;

.NET Core App

1
2
using OpenTelemetry.Resources;
using OpenTelemetry.Trace;

End tabs

Configure

The code snippet below shows how to configure OpenTelemetry for both a Console application and an .NET Core application.

Start tabs

Console App

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
...

// Define some important constants to initialize tracing with
var serviceName = "MyCompany.MyProduct.MyService";
var serviceVersion = "1.0.0";

// Configure important OpenTelemetry settings and the console exporter
using var tracerProvider = Sdk.CreateTracerProviderBuilder()
	.AddOtlpExporter(opt =>
	{
		opt.Endpoint = new Uri("ingest.lightstep.com:443"); //US data center
		// opt.Endpoint = new Uri("ingest.eu.lightstep.com:443"); //EU data center
		opt.Headers = new Metadata
		{
			{ "lightstep-access-token", Environment.GetEnvironmentVariable("LS_ACCESS_TOKEN")}
		};
		opt.Credentials = new SslCredentials();
	})
	.AddSource(serviceName)
	.SetResourceBuilder(
		ResourceBuilder.CreateDefault()
			.AddService(serviceName: serviceName,serviceVersion: serviceVersion))
	.Build();

var MyActivitySource = new ActivitySource(serviceName);

...

.NET Core App

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
...

var serviceName = "MyCompany.MyProduct.MyService";
var serviceVersion = "1.0.0";

var builder = WebApplication.CreateBuilder(args);

// Configure to send data via the OTLP exporter.
// By default, it will send to port 4318, which the collector is listening on.
builder.Services.AddOpenTelemetryTracing(tracerProviderBuilder =>
{
	tracerProviderBuilder
	.AddOtlpExporter(opt =>
	{
		opt.Endpoint = new Uri("ingest.lightstep.com:443"); //US data center
		// opt.Endpoint = new Uri("ingest.eu.lightstep.com:443"); //EU data center
		opt.Headers = new Metadata
		{
			{ "lightstep-access-token", Environment.GetEnvironmentVariable("LS_ACCESS_TOKEN")}
		};
		opt.Credentials = new SslCredentials();
	})
	.AddSource(serviceName)
	.SetResourceBuilder(
		ResourceBuilder.CreateDefault()
			.AddService(serviceName: serviceName, serviceVersion: serviceVersion))
	.AddHttpClientInstrumentation()
	.AddAspNetCoreInstrumentation();
});

var app = builder.Build();

...

End code tabs

More on OtlpExporter configuration can be found here

Notes: Configuration

  • Be sure to set the LS_ACCESS_TOKEN environment variable Cloud Observability access token before running your application: export LS_ACCESS_TOKEN=<LS_ACCESS_TOKEN>.
  • AddOtlpExporter sets up OpenTelemetry to send data to Cloud Observability using the OpenTelemetry Protocol (OTLP). I recommend sticking with OTLP in your clients.
  • SetResourceBuilder creates resources: key-value pairs which describe your service. It’s important to add as many relevant resources as you can, as this improves the quality of your tracing data. See Semantic Conventions for ideas on what to add. The two that are most important to add are service.name and service.version. If you don’t name your service, it will get named unknown_service by default.
  • AddAspNetCoreInstrumentation() enables .NET Core instrumentation. This does not apply to Console applications.
  • AddHttpClientInstrumentation() enables instrumentation for HTTP requests. If you want to instrument gRPC requests instead, use AddGrpcClientInstrumentation().

It is important to add instrumentation for every supported library. You can find all of the available instrumentation here.

To use HTTP instead of gRPC, the AddOtlpExporter configuration would be altered as follows:

1
2
3
4
5
6
7
8
9
10
.AddOtlpExporter(opt =>
{
	opt.Endpoint = new Uri("https://ingest.lightstep.com/traces/otlp/v0.9"); //US data center
	// opt.Endpoint = new Uri("https://ingest.eu.lightstep.com/traces/otlp/v0.9"); //EU data center
	opt.Headers = new Metadata
	{
		{ "lightstep-access-token", Environment.GetEnvironmentVariable("LS_ACCESS_TOKEN")}
	};
	opt.Credentials = new SslCredentials();
})

More info on using OtlpExportProtocol.HttpProtobuf can be found here.

Expandable end

Notes: Using a Collector

If you wish to use an OpenTelemetry Collector, the configuration for AddOtlpExporter would look like this:

Start tabs

Collector (gRPC)

1
2
3
4
5
6
...
.AddOtlpExporter(opt =>
{
	opt.Endpoint = new Uri("http://0.0.0.0:4317");
})
...

Collector (HTTP)

1
2
3
4
5
6
7
...
.AddOtlpExporter(opt =>
{
	opt.Protocol = OtlpExportProtocol.HttpProtobuf;
	opt.Endpoint = new Uri("http://0.0.0.0:4318/v1/traces");
})
...

Noteworthy items:

  • When opt.Protocol is not specified, the default value is OtlpExportProtocol.Grpc.
  • More info on using OtlpExportProtocol.HttpProtobuf can be found here.

End code tabs

Expandable end

Troubleshooting

Operation Names Aren’t Human Readable

Symptom
Operation names in Cloud Observability are not clear or are very long and unhelpful.

This feature requires a Satellite upgrade to the June 2020 release.

This can happen because the auto-installer is getting the name from a parameter in Datadog that might not be appropriate for your language. You can set that parameter to different values to see if that results in better operation names.

You can use either the resource or the name parameter, or both.

Which to use (or if using the both, the order to use) depends on the language of the installer. Refer to the Datadog docs for more info.

To configure how the operation name is set, add the following parameter to your Microsatellite configuration:

1
2
3
4
5
receivers:
  datadog:
	operation_name_extractors:
  	  - resource
  	  - name

If you set both, the order of the values matters. Cloud Observability tries to extract a name from the first variable value. If one isn’t found, it looks for the second value and uses that.

Expandable end

Check for multiple versions of OpenTelemetry

If multiple versions of OpenTelemetry are installed, traces are not created or propagated correctly. Check your dependencies to ensure that only a single version of OpenTelemetry is installed.

Expandable end

Check that security is configured correctly

Cloud Observability’s public Microsatellites only accept spans via a secure endpoint. If you see security related errors (e.g., Netty TLS errors with Java), you may not have the necessary root certificate installed on the machine where the tracer is running. To add a root certificate, see the documentation about encrypted connections.

Expandable end

Check that Metrics are enabled in the OpenTelemetry Collector

Not seeing metrics come through the OpenTelemetry Collector? Make sure that you have defined a Metrics pipeline in your Collector’s YAML config file.

Cloud Observability access token

See also

Send traces to Cloud Observability with Python

Send traces to Cloud Observability with Java

Send traces to Cloud Observability with Node.JS

Updated Sep 20, 2022