195 lines
8.4 KiB
Markdown
195 lines
8.4 KiB
Markdown
# OpenTelemetry .NET Traces
|
|
|
|
<details>
|
|
<summary>Table of Contents</summary>
|
|
|
|
* [Best Practices](#best-practices)
|
|
* [Package Version](#package-version)
|
|
* [Tracing API](#tracing-api)
|
|
* [ActivitySource](#activitysource)
|
|
* [Activity](#activity)
|
|
* [TracerProvider Management](#tracerprovider-management)
|
|
* [Correlation](#correlation)
|
|
|
|
</details>
|
|
|
|
## Best Practices
|
|
|
|
The following tutorials have demonstrated the best practices for using traces
|
|
with OpenTelemetry .NET:
|
|
|
|
* [Getting Started - ASP.NET Core
|
|
Application](./getting-started-aspnetcore/README.md)
|
|
* [Getting Started - Console Application](./getting-started-console/README.md)
|
|
|
|
## Package Version
|
|
|
|
:heavy_check_mark: You should always use the
|
|
[System.Diagnostics.Activity](https://learn.microsoft.com/dotnet/api/system.diagnostics.activity)
|
|
APIs from the latest stable version of
|
|
[System.Diagnostics.DiagnosticSource](https://www.nuget.org/packages/System.Diagnostics.DiagnosticSource/)
|
|
package, regardless of the .NET runtime version being used:
|
|
|
|
* If you are using the latest stable version of [OpenTelemetry .NET
|
|
SDK](../../src/OpenTelemetry/README.md), you do not have to worry about the
|
|
version of `System.Diagnostics.DiagnosticSource` package because it is already
|
|
taken care of for you via [package
|
|
dependency](../../Directory.Packages.props).
|
|
* The .NET runtime team is holding a high bar for backward compatibility on
|
|
`System.Diagnostics.DiagnosticSource` even during major version bumps, so
|
|
compatibility is not a concern here.
|
|
|
|
## Tracing API
|
|
|
|
### ActivitySource
|
|
|
|
:stop_sign: You should avoid creating
|
|
[`System.Diagnostics.ActivitySource`](https://learn.microsoft.com/dotnet/api/system.diagnostics.activitysource)
|
|
too frequently. `ActivitySource` is fairly expensive and meant to be reused
|
|
throughout the application. For most applications, it can be modeled as static
|
|
readonly field (e.g. [Program.cs](./getting-started-console/Program.cs)) or
|
|
singleton via dependency injection (e.g.
|
|
[InstrumentationSource.cs](../../examples/AspNetCore/InstrumentationSource.cs)).
|
|
|
|
:heavy_check_mark: You should use dot-separated
|
|
[UpperCamelCase](https://en.wikipedia.org/wiki/Camel_case) as the
|
|
[`ActivitySource.Name`](https://learn.microsoft.com/dotnet/api/system.diagnostics.activitysource.name).
|
|
In many cases, using the fully qualified class name might be a good option.
|
|
|
|
```csharp
|
|
static readonly ActivitySource MyActivitySource = new("MyCompany.MyProduct.MyLibrary");
|
|
```
|
|
|
|
### Activity
|
|
|
|
:heavy_check_mark: You should check
|
|
[`Activity.IsAllDataRequested`](https://learn.microsoft.com/dotnet/api/system.diagnostics.activity.isalldatarequested)
|
|
before [setting
|
|
Tags](https://learn.microsoft.com/dotnet/api/system.diagnostics.activity.settag)
|
|
for better performance.
|
|
|
|
```csharp
|
|
using (var activity = MyActivitySource.StartActivity("SayHello"))
|
|
{
|
|
if (activity != null && activity.IsAllDataRequested == true)
|
|
{
|
|
activity.SetTag("http.url", "http://www.mywebsite.com");
|
|
}
|
|
}
|
|
```
|
|
|
|
:heavy_check_mark: You should use
|
|
[Activity.SetTag](https://learn.microsoft.com/dotnet/api/system.diagnostics.activity.settag)
|
|
to [set
|
|
attributes](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/api.md#set-attributes).
|
|
|
|
:heavy_check_mark: You should finish/stop the activity properly. This can be
|
|
done implicitly via a `using` statement, which is recommended. You can also
|
|
explicitly call
|
|
[Activity.Dispose](https://learn.microsoft.com/dotnet/api/system.diagnostics.activity.dispose)
|
|
or
|
|
[Activity.Stop](https://learn.microsoft.com/dotnet/api/system.diagnostics.activity.stop).
|
|
|
|
> [!NOTE]
|
|
> Activities which are not yet finished/stopped will not be exported.
|
|
|
|
:stop_sign: You should avoid calling
|
|
[Activity.AddEvent](https://learn.microsoft.com/dotnet/api/system.diagnostics.activity.addevent)
|
|
in a loop. Activities are not designed to handle hundreds or thousands of
|
|
events, a better model is to use [correlated
|
|
logs](../logs/README.md#log-correlation) or
|
|
[Activity.Links](https://learn.microsoft.com/dotnet/api/system.diagnostics.activity.links).
|
|
|
|
> [!WARNING]
|
|
> The following code is not modeling `Activity.Events` correctly, and is very
|
|
likely to have usability and performance problems.
|
|
|
|
```csharp
|
|
private static async Task Test()
|
|
{
|
|
Activity activity = Activity.Current;
|
|
|
|
while (true)
|
|
{
|
|
activity.AddEvent(new ActivityEvent("Processing background task."));
|
|
await Task.Delay(1000);
|
|
}
|
|
}
|
|
```
|
|
|
|
## TracerProvider Management
|
|
|
|
:stop_sign: You should avoid creating `TracerProvider` instances too frequently,
|
|
`TracerProvider` is fairly expensive and meant to be reused throughout the
|
|
application. For most applications, one `TracerProvider` instance per process
|
|
would be sufficient.
|
|
|
|
:heavy_check_mark: You should properly manage the lifecycle of `TracerProvider`
|
|
instances if they are created by you.
|
|
|
|
Here is the rule of thumb when managing the lifecycle of `TracerProvider`:
|
|
|
|
* If you are building an application with [dependency injection
|
|
(DI)](https://learn.microsoft.com/dotnet/core/extensions/dependency-injection)
|
|
(e.g. [ASP.NET Core](https://learn.microsoft.com/aspnet/core) and [.NET
|
|
Worker](https://learn.microsoft.com/dotnet/core/extensions/workers)), in most
|
|
cases you should create the `TracerProvider` instance and let DI manage its
|
|
lifecycle. Refer to the [Getting Started with OpenTelemetry .NET Traces in 5
|
|
Minutes - ASP.NET Core Application](./getting-started-aspnetcore/README.md)
|
|
tutorial to learn more.
|
|
* If you are building an application without DI, create a `TracerProvider`
|
|
instance and manage the lifecycle explicitly. Refer to the [Getting Started
|
|
with OpenTelemetry .NET Traces in 5 Minutes - Console
|
|
Application](./getting-started-console/README.md) tutorial to learn more.
|
|
* If you forget to dispose the `TracerProvider` instance before the application
|
|
ends, activities might get dropped due to the lack of proper flush.
|
|
* If you dispose the `TracerProvider` instance too early, any subsequent
|
|
activities will not be collected.
|
|
|
|
## Correlation
|
|
|
|
In OpenTelemetry, traces are automatically [correlated to
|
|
logs](../logs/README.md#log-correlation) and can be [correlated to
|
|
metrics](../metrics/README.md#metrics-correlation) via
|
|
[exemplars](../metrics/exemplars/README.md).
|
|
|
|
### Manually creating Activities
|
|
|
|
As shown in the [getting started](getting-started-console/README.md) guide, it
|
|
is very easy to manually create `Activity`. Due to this, it can be tempting to
|
|
create too many activities (eg: for each method call). In addition to being
|
|
expensive, excessive activities can also make trace visualization harder.
|
|
Instead of manually creating `Activity`, check if you can leverage
|
|
instrumentation libraries, such as [ASP.NET
|
|
Core](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/tree/main/src/OpenTelemetry.Instrumentation.AspNetCore/README.md),
|
|
[HttpClient](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/tree/main/src/OpenTelemetry.Instrumentation.Http/README.md)
|
|
which will not only create and populate `Activity` with tags(attributes),
|
|
but also take care of propagating/restoring the context across process
|
|
boundaries. If the `Activity` produced by the instrumentation library is missing
|
|
some information you need, it is generally recommended to enrich the existing
|
|
Activity with that information, as opposed to creating a new one.
|
|
|
|
### Modelling static tags as Resource
|
|
|
|
Tags such as `MachineName`, `Environment` etc. which are static throughout the
|
|
process lifetime should be modelled as `Resource`, instead of adding them to
|
|
each `Activity`. Refer to this [doc](./customizing-the-sdk/README.md#resource)
|
|
for details and examples.
|
|
|
|
## Common issues that lead to missing traces
|
|
|
|
* The `ActivitySource` used to create the `Activity` is not added to the
|
|
`TracerProvider`. Use `AddSource` method to enable the activity from a given
|
|
`ActivitySource`.
|
|
* `TracerProvider` is disposed too early. You need to ensure that the
|
|
`TracerProvider` instance is kept active for traces to be collected. In a
|
|
typical application, a single TracerProvider is built at application startup,
|
|
and is disposed of at application shutdown. For an ASP.NET Core application,
|
|
use `AddOpenTelemetry` and `WithTraces` methods from the
|
|
`OpenTelemetry.Extensions.Hosting` package to correctly setup
|
|
`TracerProvider`. Here is a [sample ASP.NET Core
|
|
app](../../examples/AspNetCore/Program.cs) for reference. For simpler
|
|
applications such as Console apps, refer to this
|
|
[example](../../docs/trace/getting-started-console/Program.cs).
|
|
* TODO: Sampling
|