-
Notifications
You must be signed in to change notification settings - Fork 6k
Update distributed tracing docs #23313
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
2ffd9de
Update distributed tracing docs
noahfalk a83e344
Applying review feedback
noahfalk 1170982
Adding OTel resource
noahfalk bba5da2
Updating toc
noahfalk 269e248
Fix xref
noahfalk 14101c7
Addressing more feedback
noahfalk 44d5d5e
More feedback
noahfalk 982f4b1
newline
noahfalk 4d79420
last touchups I hope
noahfalk File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
368 changes: 368 additions & 0 deletions
368
docs/core/diagnostics/distributed-tracing-collection-walkthroughs.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,368 @@ | ||
--- | ||
title: Collect a distributed trace - .NET | ||
description: Tutorials to collect distributed traces in .NET applications using OpenTelemetry, Application Insights, or ActivityListener | ||
ms.topic: tutorial | ||
ms.date: 03/14/2021 | ||
--- | ||
|
||
# Collect a distributed trace | ||
|
||
**This article applies to: ✔️** .NET Core 2.1 and later versions **and** .NET Framework 4.5 and later versions | ||
|
||
Instrumented code can create <xref:System.Diagnostics.Activity> objects as part of a distributed trace, but the information | ||
in these objects needs to be collected into centralized storage so that the entire trace can be | ||
reviewed later. In this tutorial you will collect the distributed trace telemetry in different ways so that it is | ||
available to diagnose application issues when needed. See | ||
[the instrumentation tutorial](distributed-tracing-instrumentation-walkthroughs.md) if you need to add new instrumentation. | ||
|
||
## Collect traces using OpenTelemetry | ||
|
||
### Prerequisites | ||
|
||
- [.NET Core 2.1 SDK](https://dotnet.microsoft.com/download/dotnet) or a later version | ||
|
||
### Create an example application | ||
|
||
Before any distributed trace telemetry can be collected we need to produce it. Often this instrumentation might be | ||
in libraries but for simplicity you will create a small app that has some example instrumentation using | ||
<xref:System.Diagnostics.ActivitySource.StartActivity%2A>. At this point no collection is happening yet, | ||
StartActivity() has no side-effect and returns null. See | ||
[the instrumentation tutorial](distributed-tracing-instrumentation-walkthroughs.md) for more details. | ||
|
||
```dotnetcli | ||
dotnet new console | ||
``` | ||
|
||
Applications that target .NET 5 and later already have the necessary distributed tracing APIs included. For apps targeting older | ||
.NET versions add the [System.Diagnostics.DiagnosticSource NuGet package](https://www.nuget.org/packages/System.Diagnostics.DiagnosticSource/) | ||
version 5 or greater. | ||
|
||
```dotnetcli | ||
dotnet add package System.Diagnostics.DiagnosticSource | ||
``` | ||
|
||
Replace the contents of the generated Program.cs with this example source: | ||
|
||
```C# | ||
using System; | ||
using System.Diagnostics; | ||
using System.Threading.Tasks; | ||
|
||
namespace Sample.DistributedTracing | ||
{ | ||
class Program | ||
{ | ||
static ActivitySource s_source = new ActivitySource("Sample.DistributedTracing"); | ||
|
||
static async Task Main(string[] args) | ||
{ | ||
await DoSomeWork(); | ||
Console.WriteLine("Example work done"); | ||
} | ||
|
||
static async Task DoSomeWork() | ||
{ | ||
using (Activity a = s_source.StartActivity("SomeWork")) | ||
{ | ||
await StepOne(); | ||
await StepTwo(); | ||
} | ||
} | ||
|
||
static async Task StepOne() | ||
{ | ||
using (Activity a = s_source.StartActivity("StepOne")) | ||
noahfalk marked this conversation as resolved.
Show resolved
Hide resolved
|
||
{ | ||
await Task.Delay(500); | ||
} | ||
} | ||
|
||
static async Task StepTwo() | ||
{ | ||
using (Activity a = s_source.StartActivity("StepTwo")) | ||
{ | ||
await Task.Delay(1000); | ||
} | ||
} | ||
} | ||
} | ||
``` | ||
|
||
Running the app does not collect any trace data yet: | ||
|
||
```dotnetcli | ||
> dotnet run | ||
Example work done | ||
``` | ||
|
||
### Collect using OpenTelemetry | ||
|
||
[OpenTelemetry](https://opentelemetry.io/) is a vendor neutral open source project supported by the | ||
[Cloud Native Computing Foundation](https://www.cncf.io/) that aims to standardize generating and collecting telemetry for | ||
cloud-native software. In this example you will collect and display distributed trace information on the console though | ||
OpenTelemetry can be reconfigured to send it elsewhere. See the | ||
[OpenTelemetry getting started guide](https://github.com/open-telemetry/opentelemetry-dotnet/blob/main/docs/trace/getting-started/README.md) | ||
for more information. | ||
|
||
Add the [OpenTelemetry.Exporter.Console](https://www.nuget.org/packages/OpenTelemetry.Exporter.Console/) NuGet package. | ||
|
||
```dotnetcli | ||
dotnet add package OpenTelemetry.Exporter.Console | ||
``` | ||
|
||
Update Program.cs with additional OpenTelemetry using statments: | ||
|
||
```C# | ||
using OpenTelemetry; | ||
using OpenTelemetry.Resources; | ||
using OpenTelemetry.Trace; | ||
using System; | ||
noahfalk marked this conversation as resolved.
Show resolved
Hide resolved
|
||
using System.Diagnostics; | ||
using System.Threading.Tasks; | ||
``` | ||
|
||
Update Main() to create the OpenTelemetry TracerProvider: | ||
|
||
```C# | ||
public static async Task Main() | ||
{ | ||
using var tracerProvider = Sdk.CreateTracerProviderBuilder() | ||
.SetResourceBuilder(ResourceBuilder.CreateDefault().AddService("MySample")) | ||
.AddSource("Sample.DistributedTracing") | ||
noahfalk marked this conversation as resolved.
Show resolved
Hide resolved
|
||
.AddConsoleExporter() | ||
.Build(); | ||
|
||
await DoSomeWork(); | ||
Console.WriteLine("Example work done"); | ||
} | ||
``` | ||
|
||
Now the app collects distributed trace information and displays it to the console: | ||
|
||
```dotnetcli | ||
> dotnet run | ||
Activity.Id: 00-7759221f2c5599489d455b84fa0f90f4-6081a9b8041cd840-01 | ||
Activity.ParentId: 00-7759221f2c5599489d455b84fa0f90f4-9a52f72c08a9d447-01 | ||
Activity.DisplayName: StepOne | ||
Activity.Kind: Internal | ||
Activity.StartTime: 2021-03-18T10:46:46.8649754Z | ||
Activity.Duration: 00:00:00.5069226 | ||
Resource associated with Activity: | ||
service.name: MySample | ||
service.instance.id: 909a4624-3b2e-40e4-a86b-4a2c8003219e | ||
|
||
Activity.Id: 00-7759221f2c5599489d455b84fa0f90f4-d2b283db91cf774c-01 | ||
Activity.ParentId: 00-7759221f2c5599489d455b84fa0f90f4-9a52f72c08a9d447-01 | ||
Activity.DisplayName: StepTwo | ||
Activity.Kind: Internal | ||
Activity.StartTime: 2021-03-18T10:46:47.3838737Z | ||
Activity.Duration: 00:00:01.0142278 | ||
Resource associated with Activity: | ||
service.name: MySample | ||
service.instance.id: 909a4624-3b2e-40e4-a86b-4a2c8003219e | ||
|
||
Activity.Id: 00-7759221f2c5599489d455b84fa0f90f4-9a52f72c08a9d447-01 | ||
Activity.DisplayName: SomeWork | ||
Activity.Kind: Internal | ||
Activity.StartTime: 2021-03-18T10:46:46.8634510Z | ||
Activity.Duration: 00:00:01.5402045 | ||
Resource associated with Activity: | ||
service.name: MySample | ||
service.instance.id: 909a4624-3b2e-40e4-a86b-4a2c8003219e | ||
|
||
Example work done | ||
``` | ||
|
||
#### Sources | ||
|
||
In the example code you invoked `AddSource("Sample.DistributedTracing")` so that OpenTelemetry would | ||
capture the Activities produced by the ActivitySource that was already present in the code: | ||
|
||
```csharp | ||
static ActivitySource s_source = new ActivitySource("Sample.DistributedTracing"); | ||
``` | ||
|
||
Telemetry from any ActivitySource can captured by calling AddSource() with the source's name. | ||
|
||
#### Exporters | ||
|
||
The console exporter is helpful for quick examples or local development but in a production deployment | ||
you will probably want to send traces to a centralized store. OpenTelemetry supports a variety | ||
of destinations using different | ||
[exporters](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/glossary.md#exporter-library). | ||
See the [OpenTelemetry getting started guide](https://github.com/open-telemetry/opentelemetry-dotnet#getting-started) | ||
for more information on configuring OpenTelemetry. | ||
|
||
## Collect traces using Application Insights | ||
|
||
Distributed tracing telemetry is automatically captured after configuring the Application Insights SDK | ||
([ASP.NET](https://docs.microsoft.com/azure/azure-monitor/app/asp-net), [ASP.NET Core](https://docs.microsoft.com/azure/azure-monitor/app/asp-net-core)) | ||
or by enabling [code-less instrumentation](https://docs.microsoft.com/azure/azure-monitor/app/codeless-overview). | ||
|
||
See the [Application Insights distributed tracing documentation](https://docs.microsoft.com/azure/azure-monitor/app/distributed-tracing) for more | ||
information. | ||
|
||
> [!NOTE] | ||
> Currently Application Insights only supports collecting specific well-known Activity instrumentation and will ignore new user added Activities. Application | ||
> Insights offers [TrackDependency](https://docs.microsoft.com/azure/azure-monitor/app/api-custom-events-metrics#trackdependency) as a vendor | ||
> specific API for adding custom distributed tracing information. | ||
|
||
## Collect traces using custom logic | ||
|
||
Developers are free to create their own customized collection logic for Activity trace data. This example collects the | ||
telemetry using the <xref:System.Diagnostics.ActivityListener?displayProperty=nameWithType> API provided by .NET and prints | ||
it to the console. | ||
|
||
### Prerequisites | ||
|
||
- [.NET Core 2.1 SDK](https://dotnet.microsoft.com/download/dotnet) or a later version | ||
|
||
### Create an example application | ||
|
||
First you will create an example application that has some distributed trace instrumentation but no trace data is being collected. | ||
|
||
```dotnetcli | ||
dotnet new console | ||
``` | ||
|
||
Applications that target .NET 5 and later already have the necessary distributed tracing APIs included. For apps targeting older | ||
.NET versions add the [System.Diagnostics.DiagnosticSource NuGet package](https://www.nuget.org/packages/System.Diagnostics.DiagnosticSource/) | ||
version 5 or greater. | ||
|
||
```dotnetcli | ||
dotnet add package System.Diagnostics.DiagnosticSource | ||
``` | ||
|
||
Replace the contents of the generated Program.cs with this example source: | ||
|
||
```C# | ||
using System; | ||
using System.Diagnostics; | ||
using System.Threading.Tasks; | ||
|
||
namespace Sample.DistributedTracing | ||
{ | ||
class Program | ||
{ | ||
static ActivitySource s_source = new ActivitySource("Sample.DistributedTracing"); | ||
|
||
static async Task Main(string[] args) | ||
{ | ||
await DoSomeWork(); | ||
Console.WriteLine("Example work done"); | ||
} | ||
|
||
static async Task DoSomeWork() | ||
{ | ||
using (Activity a = s_source.StartActivity("SomeWork")) | ||
{ | ||
await StepOne(); | ||
await StepTwo(); | ||
} | ||
} | ||
|
||
static async Task StepOne() | ||
{ | ||
using (Activity a = s_source.StartActivity("StepOne")) | ||
{ | ||
await Task.Delay(500); | ||
} | ||
} | ||
|
||
static async Task StepTwo() | ||
{ | ||
using (Activity a = s_source.StartActivity("StepTwo")) | ||
{ | ||
await Task.Delay(1000); | ||
} | ||
} | ||
} | ||
} | ||
``` | ||
|
||
Running the app does not collect any trace data yet: | ||
|
||
```dotnetcli | ||
> dotnet run | ||
Example work done | ||
``` | ||
|
||
### Add code to collect the traces | ||
|
||
Update Main() with this code: | ||
|
||
```C# | ||
static async Task Main(string[] args) | ||
{ | ||
Activity.DefaultIdFormat = ActivityIdFormat.W3C; | ||
Activity.ForceDefaultIdFormat = true; | ||
|
||
Console.WriteLine(" {0,-15} {1,-60} {2,-15}", "OperationName", "Id", "Duration"); | ||
ActivitySource.AddActivityListener(new ActivityListener() | ||
{ | ||
ShouldListenTo = (source) => true, | ||
Sample = (ref ActivityCreationOptions<ActivityContext> options) => ActivitySamplingResult.AllDataAndRecorded, | ||
noahfalk marked this conversation as resolved.
Show resolved
Hide resolved
|
||
ActivityStarted = activity => Console.WriteLine("Started: {0,-15} {1,-60}", activity.OperationName, activity.Id), | ||
ActivityStopped = activity => Console.WriteLine("Stopped: {0,-15} {1,-60} {2,-15}", activity.OperationName, activity.Id, activity.Duration) | ||
}); | ||
|
||
await DoSomeWork(); | ||
Console.WriteLine("Example work done"); | ||
} | ||
``` | ||
|
||
The output now includes logging: | ||
|
||
```dotnetcli | ||
> dotnet run | ||
OperationName Id Duration | ||
Started: SomeWork 00-bdb5faffc2fc1548b6ba49a31c4a0ae0-c447fb302059784f-01 | ||
Started: StepOne 00-bdb5faffc2fc1548b6ba49a31c4a0ae0-a7c77a4e9a02dc4a-01 | ||
Stopped: StepOne 00-bdb5faffc2fc1548b6ba49a31c4a0ae0-a7c77a4e9a02dc4a-01 00:00:00.5093849 | ||
Started: StepTwo 00-bdb5faffc2fc1548b6ba49a31c4a0ae0-9210ad536cae9e4e-01 | ||
Stopped: StepTwo 00-bdb5faffc2fc1548b6ba49a31c4a0ae0-9210ad536cae9e4e-01 00:00:01.0111847 | ||
Stopped: SomeWork 00-bdb5faffc2fc1548b6ba49a31c4a0ae0-c447fb302059784f-01 00:00:01.5236391 | ||
Example work done | ||
``` | ||
|
||
Setting <xref:System.Diagnostics.Activity.DefaultIdFormat> and | ||
<xref:System.Diagnostics.Activity.ForceDefaultIdFormat> is optional | ||
but helps ensure the sample produces similar output on different .NET runtime versions. .NET 5 uses | ||
the W3C TraceContext ID format by default but earlier .NET versions default to using | ||
<xref:System.Diagnostics.ActivityIdFormat.Hierarchical> ID format. See | ||
[Activity IDs](distributed-tracing-concepts.md#activity-ids) for more details. | ||
|
||
<xref:System.Diagnostics.ActivityListener?displayProperty=nameWithType> is used to receive callbacks | ||
during the lifetime of an Activity. | ||
|
||
- <xref:System.Diagnostics.ActivityListener.ShouldListenTo> - Each | ||
Activity is associated with an ActivitySource which acts as its namespace and producer. | ||
This callback is invoked once for each ActivitySource in the process. Return true | ||
if you are interested in performing sampling or being notified about start/stop events | ||
for Activities produced by this source. | ||
- <xref:System.Diagnostics.ActivityListener.Sample> - By default | ||
<xref:System.Diagnostics.ActivitySource.StartActivity%2A> does not | ||
create an Activity object unless some ActivityListener indicates it should be sampled. Returning | ||
<xref:System.Diagnostics.ActivitySamplingResult.AllDataAndRecorded> | ||
indicates that the Activity should be created, | ||
<xref:System.Diagnostics.Activity.IsAllDataRequested> should be set | ||
to true, and <xref:System.Diagnostics.Activity.ActivityTraceFlags> | ||
will have the <xref:System.Diagnostics.ActivityTraceFlags.Recorded> | ||
flag set. IsAllDataRequested can be observed by the instrumented code as a hint that a listener | ||
wants to ensure that auxilliary Activity information such as Tags and Events are populated. | ||
The Recorded flag is encoded in the W3C TraceContext ID and is a hint to other processes | ||
involved in the distributed trace that this trace should be sampled. | ||
- <xref:System.Diagnostics.ActivityListener.ActivityStarted> and | ||
<xref:System.Diagnostics.ActivityListener.ActivityStopped> are | ||
called when an Activity is started and stopped respectively. These callbacks provide an | ||
oportunity to record relevant information about the Activity or potentially to modify it. | ||
When an Activity has just started much of the data may still be incomplete and it will | ||
be populated before the Activity stops. | ||
|
||
Once an ActivityListener has been created and the callbacks are populated, calling | ||
<xref:System.Diagnostics.ActivitySource.AddActivityListener(System.Diagnostics.ActivityListener)?displayProperty=nameWithType> | ||
initiates invoking the callbacks. Call | ||
<xref:System.Diagnostics.ActivityListener.Dispose?displayProperty=nameWithType> to | ||
stop the flow of callbacks. Beware that in multi-threaded code callback notifications in | ||
progress could be received while Dispose() is running or even very shortly after it has | ||
returned. |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.