- Installation
- Introduction
- Introduction to OpenTelemetry .NET Tracing API
- Instrumenting a library/application with .NET Activity API
- Instrumenting a library/application with OpenTelemetry.API Shim
- References
dotnet add package OpenTelemetry.Api
Application developers and library authors use OpenTelemetry API to instrument their application/library. The API only surfaces necessary abstractions to instrument an application/library. It does not address concerns like how telemetry is exported to a specific telemetry backend, how to sample the telemetry, etc. The API consists of Tracing API, Logging API, Metrics API, Context and Propagation API, and a set of semantic conventions.
Tracing API allows users to generate Spans, which represent a single operation within a trace. Spans can be nested to form a trace tree. Each trace contains a root span, which typically describes the entire operation and, optionally one or more child-spans for its child-operations.
OpenTelemetry does not introduce its own API for logging. Instead it recommends to integrate with existing well-known logging libraries for each language. For .NET, the logging API is simply the Microsoft.Extensions.Logging API.
Metrics API allows users to capture measurements about the execution of a computer program at runtime. The Metrics API is designed to process raw measurements, generally with the intent to produce continuous summaries of those measurements.
Warning: OpenTelemetry .NET has a prototype Metrics API implementation only and is not recommended for any production use. The API is expected to change heavily. Please check the Metric support plan.
.NET runtime had Activity
class for a long time, which was meant to be used
for tracing purposes and represents the equivalent of the OpenTelemetry
Span.
OpenTelemetry .NET is reusing the existing Activity
and associated classes to
represent the OpenTelemetry Span
. This means, users can instrument their
applications/libraries to emit OpenTelemetry compatible traces by using just the
.NET Runtime.
The Activity
and associated classes are shipped as part of
System.Diagnostics.DiagnosticSource
nuget package. Version 5.0.0 of this
package contains improvements to Activity
class which makes it more closely
aligned with OpenTelemetry API
specification.
Even though Activity
enables all the scenarios OpenTelemetry supports, users
who are already familiar with OpenTelemetry terminology may find it easy to
operate with that terminology. For instance, StartSpan
may be preferred over
StartActivity
. To help with this transition, the OpenTelemetry.API package has
shim classes
to wrap around the .NET Activity
classes.
The shim exist only in the API. OpenTelemetry SDK for .NET will be operating
entirely with Activity
only. Irrespective of whether shim classes or
Activity
is used for instrumentation, the end result would be same. i.e
Processors/Exporters see the same data.
The recommended way of instrumenting is by using the .NET Activity API. Users are required to just take dependency on the DiagnosticSource. Adding dependency to OpenTelemetry.API is required only for the following scenarios:
- You want to use terminology matching OpenTelemetry spec (Span vs Activity). The shim can be useful for such users. Refer to the comparison of Activity API and OpenTelemetry Tracing API if you want to compare the differences.
- Your library performs communication with other libraries/components, and want to access Propagators, to inject and extract context data. Some of the most common libraries requiring this include HttpClient, ASP.NET, ASP.NET Core. This repo already provides instrumentation for these common libraries. If your library is not built on top of these, and want to leverage propagators, follow the Context propagation section.
As mentioned in the introduction, the instrumentation API for OpenTelemetry .NET
is the .NET Activity
API. Guidance for instrumenting using this API is
documented fully in the TBD(dotnet activity user guide link), but is described
here as well.
-
Install the
System.Diagnostics.DiagnosticSource
package version5.0.0
or above to your application or library.<ItemGroup> <PackageReference Include="System.Diagnostics.DiagnosticSource" Version="5.0.0" /> </ItemGroup>
-
Create an
ActivitySource
, providing the name and version of the library/application doing the instrumentation.ActivitySource
instance is typically created once and is reused throughout the application/library.static ActivitySource activitySource = new ActivitySource( "companyname.product.instrumentationlibrary", "semver1.0.0");
The above requires import of the
System.Diagnostics
namespace. -
Use the
ActivitySource
instance from above to createActivity
instances, which represent a single operation within a trace. The parameter passed is theDisplayName
of the activity.var activity = activitySource.StartActivity("ActivityName");
If there are no listeners interested in this activity, the activity above will be null. This happens when the final application does not enable OpenTelemetry (or other
ActivityListener
s), or when OpenTelemetry samplers chose not to sample this activity. Ensure that all subsequent calls using this activity are protected with a null check. -
Populate activity with tags following the OpenTelemetry semantic conventions. It is highly recommended to check
activity.IsAllDataRequested
, before populating any tags which are not readily available.IsAllDataRequested
is the same as Span.IsRecording and will be false when samplers decide to not record the activity, and this can be used to avoid any expensive operation to retrieve tags.activity?.SetTag("http.method", "GET"); if (activity != null && activity.IsAllDataRequested == true) { activity.SetTag("http.url", "http://www.mywebsite.com"); }
-
Perform application/library logic.
-
Stop the activity when done.
activity?.Stop();
Alternately, as
Activity
implementsIDisposable
, it can be used with ausing
block, which ensures activity gets stopped upon disposal. This is shown below.using (var activity = activitySource.StartActivity("ActivityName") { activity?.SetTag("http.method", "GET"); } // Activity gets stopped automatically at end of this block during dispose.
The above showed the basic usage of instrumenting using Activity
. The
following sections describes more features.
Basic usage example above showed how StartActivity
method can be used to start
an Activity
. The started activity will automatically becomes the Current
activity. It is important to note that the StartActivity
returns null
, if no
listeners are interested in the activity to be created. This happens when the
final application does not enable OpenTelemetry, or when OpenTelemetry samplers
chose not to sample this activity.
StartActivity
has many overloads to control the activity creation.
-
ActivityKind
Activity
has a property calledActivityKind
which represents OpenTelemetry SpanKind. The default value will beInternal
.StartActivity
allows passing theActivityKind
while starting anActivity
.var activity = activitySource.StartActivity("ActivityName", ActivityKind.Server);
-
Parent using
ActivityContext
ActivityContext
represents the OpenTelemetry SpanContext. While starting a newActivity
, the currently activeActivity
is automatically taken as the parent of the new activity being created.StartActivity
allows passing explicitActivityContext
to override this behavior.var parentContext = new ActivityContext( ActivityTraceId.CreateFromString("0af7651916cd43dd8448eb211c80319c"), ActivitySpanId.CreateFromString("b7ad6b7169203331"), ActivityTraceFlags.None); var activity = activitySource.StartActivity( "ActivityName", ActivityKind.Server, parentContext);
As
ActivityContext
follows the W3C Trace-Context, it is also possible to provide the parent context as a single string matching thetraceparent
header of the W3C Trace-Context. This is shown below.var activity = activitySource.StartActivity( "ActivityName", ActivityKind.Server, "00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01");
-
Initial Tags
Tags
inActivity
represents the OpenTelemetry Span Attributes. Earlier sample showed the usage ofSetTag
method ofActivity
to add tags. Refer to the specification for best practices on naming tags. It is also possible to provide an initial set of tags during activity creation, as shown below. Tags provided at activity creation are accessible for Samplers, whereas any tags added usingSetTag
are not available for samplers.var initialTags = new ActivityTagsCollection(); initialTags["com.mycompany.product.mytag1"] = "tagValue1"; initialTags["com.mycompany.product.mytag2"] = "tagValue2"; var activity = activitySource.StartActivity( "ActivityName", ActivityKind.Server, "00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01", initialTags);
The above requires import of the
System.Collections.Generic
namespace. -
Activity Links
Apart from the parent-child relation, activities can be linked using
ActivityLinks
which represent the OpenTelemetry Links. The linked activities must be provided during the creation time, as shown below.var activityLinks = new List<ActivityLink>(); var linkedContext1 = new ActivityContext( ActivityTraceId.CreateFromString("0af7651916cd43dd8448eb211c80319c"), ActivitySpanId.CreateFromString("b7ad6b7169203331"), ActivityTraceFlags.None); var linkedContext2 = new ActivityContext( ActivityTraceId.CreateFromString("4bf92f3577b34da6a3ce929d0e0e4736"), ActivitySpanId.CreateFromString("00f067aa0ba902b7"), ActivityTraceFlags.Recorded); activityLinks.Add(new ActivityLink(linkedContext1)); activityLinks.Add(new ActivityLink(linkedContext2)); var activity = activitySource.StartActivity( "ActivityWithLinks", ActivityKind.Server, default(ActivityContext), initialTags, activityLinks);
Note that
Activity
above is created withdefault(ActivityContext)
parent, which makes it child of implicitActivity.Current
or orphan if there is noCurrent
.
It is possible to add
events
to Activity
using the AddEvent
method as shown below.
activity?.AddEvent(new ActivityEvent("sample activity event."));
Apart from providing name, timestamp and attributes can be provided by using
corresponding overloads of ActivityEvent
.
OpenTelemetry defines a concept called
Status
to be associated with Activity
. There is no Status
class in .NET, and hence
Status
is set to an Activity
using the following special tags:
otel.status_code
is the Tag
name used to store the StatusCode
, and
otel.status_description
is the Tag
name used to store the optional
Description
.
Example:
activity?.SetTag("otel.status_code", "ERROR");
activity?.SetTag("otel.status_description", "error status description");
Values for the StatusCode tag must be one of the strings "UNSET", "OK", or "ERROR",
which correspond respectively to the enums Unset
, Ok
, and Error
from
StatusCode
.
If using OpenTelemetry API
shim, then you
can leverage the SetStatus
extension method on Activity
as well.
As mentioned in the introduction section, using OpenTelemetry.API Shim is only recommended if you want to use OpenTelemetry terminology like Tracer, Span instead of ActivitySource, Activity.
Follow this code for example usage of this shim.
OpenTelemetry.API must be used to access Propagators API which defines how to extract and inject context across process boundaries. This is typically required if you are not using any of the .NET communication libraries which has instrumentations already available which does the propagation (eg: Asp.Net Core or HttpClient). In such cases, context extraction and propagation is the responsibility of the library itself. An example would be a producer-consumer pattern using some queuing library like RabbitMQ. Follow the messaging example for examples on how to inject and extract context.