From e454dc9dc84d6d9ac42604e1755e1318b19b29cb Mon Sep 17 00:00:00 2001 From: "J. Kalyana Sundaram" Date: Wed, 1 Nov 2023 15:06:26 -0700 Subject: [PATCH] A code sample to show how to create new root activities that link to a previously current activity. (#4957) --- OpenTelemetry.sln | 9 +- .../Program.cs | 99 +++++++++++ .../README.md | 158 ++++++++++++++++++ .../links-creation.csproj | 5 + 4 files changed, 270 insertions(+), 1 deletion(-) create mode 100644 docs/trace/links-creation-with-new-activities/Program.cs create mode 100644 docs/trace/links-creation-with-new-activities/README.md create mode 100644 docs/trace/links-creation-with-new-activities/links-creation.csproj diff --git a/OpenTelemetry.sln b/OpenTelemetry.sln index e4908525cef..b7c4e130759 100644 --- a/OpenTelemetry.sln +++ b/OpenTelemetry.sln @@ -90,8 +90,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ISSUE_TEMPLATE", "ISSUE_TEM EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{E69578EB-B456-4062-A645-877CD964528B}" ProjectSection(SolutionItems) = preProject - .github\workflows\ci-aot.yml = .github\workflows\ci-aot.yml .github\workflows\ci-aot-md.yml = .github\workflows\ci-aot-md.yml + .github\workflows\ci-aot.yml = .github\workflows\ci-aot.yml .github\workflows\ci-instrumentation-libraries-md.yml = .github\workflows\ci-instrumentation-libraries-md.yml .github\workflows\ci-instrumentation-libraries.yml = .github\workflows\ci-instrumentation-libraries.yml .github\workflows\ci-md.yml = .github\workflows\ci-md.yml @@ -316,6 +316,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Metrics", "Metrics", "{1C45 EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "getting-started-aspnetcore", "docs\logs\getting-started-aspnetcore\getting-started-aspnetcore.csproj", "{99B4D965-8782-4694-8DFA-B7A3630CEF60}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "links-creation", "docs\trace\links-creation-with-new-activities\links-creation.csproj", "{B4856711-6D4C-4246-A686-49458D4C1301}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -590,6 +592,10 @@ Global {99B4D965-8782-4694-8DFA-B7A3630CEF60}.Debug|Any CPU.Build.0 = Debug|Any CPU {99B4D965-8782-4694-8DFA-B7A3630CEF60}.Release|Any CPU.ActiveCfg = Release|Any CPU {99B4D965-8782-4694-8DFA-B7A3630CEF60}.Release|Any CPU.Build.0 = Release|Any CPU + {B4856711-6D4C-4246-A686-49458D4C1301}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B4856711-6D4C-4246-A686-49458D4C1301}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B4856711-6D4C-4246-A686-49458D4C1301}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B4856711-6D4C-4246-A686-49458D4C1301}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -638,6 +644,7 @@ Global {A0CB9A10-F22D-4E66-A449-74B3D0361A9C} = {A49299FB-C5CD-4E0E-B7E1-B7867BBD67CC} {1C459B5B-C702-46FF-BF1A-EE795E420FFA} = {A49299FB-C5CD-4E0E-B7E1-B7867BBD67CC} {99B4D965-8782-4694-8DFA-B7A3630CEF60} = {3862190B-E2C5-418E-AFDC-DB281FB5C705} + {B4856711-6D4C-4246-A686-49458D4C1301} = {5B7FB835-3FFF-4BC2-99C5-A5B5FAE3C818} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {55639B5C-0770-4A22-AB56-859604650521} diff --git a/docs/trace/links-creation-with-new-activities/Program.cs b/docs/trace/links-creation-with-new-activities/Program.cs new file mode 100644 index 00000000000..633525a4bac --- /dev/null +++ b/docs/trace/links-creation-with-new-activities/Program.cs @@ -0,0 +1,99 @@ +// +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +using System.Diagnostics; +using OpenTelemetry; +using OpenTelemetry.Trace; + +namespace LinksCreationWithNewRootActivitiesDemo; + +internal class Program +{ + private static readonly ActivitySource MyActivitySource = new("LinksCreationWithNewRootActivities"); + + public static async Task Main(string[] args) + { + using var tracerProvider = Sdk.CreateTracerProviderBuilder() + .AddSource("LinksCreationWithNewRootActivities") + .AddConsoleExporter() + .Build(); + + using (var activity = MyActivitySource.StartActivity("OrchestratingActivity")) + { + activity?.SetTag("foo", 1); + await DoFanoutAsync(); + + using (var nestedActivity = MyActivitySource.StartActivity("WrapUp")) + { + nestedActivity?.SetTag("foo", 1); + } + } + } + + public static async Task DoFanoutAsync() + { + var previous = Activity.Current; + const int NumConcurrentOperations = 10; + + var activityContext = Activity.Current!.Context; + var links = new List + { + new ActivityLink(activityContext), + }; + + var tasks = new List(); + + // Fanning out to N concurrent operations. + // We create a new root activity for each operation and + // link it to an outer activity that happens to be the current + // activity. + for (int i = 0; i < NumConcurrentOperations; i++) + { + int operationIndex = i; + + var task = Task.Run(() => + { + // Reference: https://opentelemetry.io/docs/instrumentation/net/manual/#creating-new-root-activities + // Since we want to create a new root activity for each of the fanned out operations, + // this step helps us "de-parent" it from the current activity. + // Note: At least as of Oct 2023, this is the only mechanism to create a new root + // activity in the presence of an existing activity. This might change in the future + // if/when issue https://github.com/open-telemetry/opentelemetry-dotnet/issues/984 + // is addressed. + Activity.Current = null; + + // Reference: https://github.com/open-telemetry/opentelemetry-dotnet/tree/main/src/OpenTelemetry.Api#activity-creation-options + // Reference: https://opentelemetry.io/docs/instrumentation/net/manual/#adding-links + // We create a new root activity for each of the fanned out operations and link it to the outer activity. + using var newRootActivityForFannedOutOperation = MyActivitySource.StartActivity( + ActivityKind.Internal, // Set this to the appropriate ActivityKind depending on your scenario + name: $"FannedOutActivity {operationIndex + 1}", + links: links); + + // DO THE FANOUT WORK HERE... + }); + + tasks.Add(task); + } + + // Wait for all tasks to complete + await Task.WhenAll(tasks); + + // Reset to the previous activity now that we are done with the fanout + // This will ensure that the rest of the code executes in the context of the original activity. + Activity.Current = previous; + } +} diff --git a/docs/trace/links-creation-with-new-activities/README.md b/docs/trace/links-creation-with-new-activities/README.md new file mode 100644 index 00000000000..39552a7cbb6 --- /dev/null +++ b/docs/trace/links-creation-with-new-activities/README.md @@ -0,0 +1,158 @@ +# Creating new root activities that link to an existing activity: A Sample + +This sample shows how to create new root activities that +[link](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/overview.md#links-between-spans) +to an existing activity. This can be useful in a fan-out or batched operation +situation when you want to create a new trace with a new root activity +BEFORE invoking each of the fanned out operations, and at the same time +you want each of these new traces to be linked to the original activity. + +To give an example, let's say that: + +- Service A receives a request for a customer operation that impacts 1000s of +resources. The term "resource" here means an entity that is managed by this +service and should not be confused with the term "resource" in OpenTelemetry. +- Service A orchestrates this overall operation by fanning out multiple +calls to Service B, with one call for EACH of the impacted resources. +- Let's say the number of spans generated for a single resource operation +is in the order of several thousands of spans. + +In the above example, if you used the same trace for the entire flow, then +you would end up with a huge trace with more than million spans. This will +make visualizing and understanding the trace difficult. + +Further, it may make it difficult to do programmatic analytics at the +*individual* resource operation level (for each of the 1000s of resource +operations) as there would be no single trace that corresponds to each +of the individual resource operations. + +Instead, by creating a new trace with a new root activity before the fanout +call, you get a separate trace for each of the resource operations. In +addition, by using the "span links" functionality in OpenTelemetry, we link +each of these new root activities to the original activity. + +This enables more granular visualization and analytics. + +## How does this example work? + +To be able to create new root activities, we first set the Activity.Current +to null so that we can "de-parent" the new activity from the current activity. + +For each of the fanned out operations, this creates a new root activity. As +part of this activity creation, it links it to the previously current activity. + +Finally, we reset Activity.Current to the previous activity now after we are +done with the fanout. This will ensure that the rest of the code executes +in the context of the original activity. + +## When should you consider such an option? What are the tradeoffs? + +This is a good option to consider for operations that involve batched or +fanout operations if using the same trace causes it to become huge. +Using this approach, you can create a new trace for each of the fanned out +operations and link them to the original activity. + +A tradeoff is that now we will have multiple traces instead of a single trace. +However, many Observability tools have the ability to visualize linked traces +together, and hence it is not necessarily a concern from that perspective. +However, this model has the potential to add some complexity to any +programmatic analysis since now it has to understand the concept of linked +traces. + +## References + +- [Links between spans](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/overview.md#links-between-spans) +- [Creating new root activities](https://opentelemetry.io/docs/instrumentation/net/manual/#creating-new-root-activities) +- [Activity Creation Options](https://github.com/open-telemetry/opentelemetry-dotnet/tree/main/src/OpenTelemetry.Api#activity-creation-options) +- [A sample where links are used in a fan-in scenario](https://github.com/PacktPublishing/Modern-Distributed-Tracing-in-.NET/tree/main/chapter6/links) + +## Sample Output + +You should see output such as the below when you run this example. You can see +that EACH of the "fanned out activities" have: + +- a new trace ID +- an activity link to the original activity + +```text +Activity.TraceId: 5ce4d8ad4926ecdd0084681f46fa38d9 +Activity.SpanId: 8f9e9441f0789f6e +Activity.TraceFlags: Recorded +Activity.ActivitySourceName: LinksCreationWithNewRootActivities +Activity.DisplayName: FannedOutActivity 1 +Activity.Kind: Internal +Activity.StartTime: 2023-10-17T01:24:40.4957326Z +Activity.Duration: 00:00:00.0008656 +Activity.Links: + 2890476acefb53b93af64a0d91939051 16b83c1517629363 +Resource associated with Activity: + telemetry.sdk.name: opentelemetry + telemetry.sdk.language: dotnet + telemetry.sdk.version: 0.0.0-alpha.0.2600 + service.name: unknown_service:links-creation + +Activity.TraceId: 16a8ad23d14a085f2a1f260a4b474d05 +Activity.SpanId: 0c3e835cfd60c604 +Activity.TraceFlags: Recorded +Activity.ActivitySourceName: LinksCreationWithNewRootActivities +Activity.DisplayName: FannedOutActivity 2 +Activity.Kind: Internal +Activity.StartTime: 2023-10-17T01:24:40.5908290Z +Activity.Duration: 00:00:00.0009197 +Activity.Links: + 2890476acefb53b93af64a0d91939051 16b83c1517629363 +Resource associated with Activity: + telemetry.sdk.name: opentelemetry + telemetry.sdk.language: dotnet + telemetry.sdk.version: 0.0.0-alpha.0.2600 + service.name: unknown_service:links-creation + +Activity.TraceId: 46f0b5b68173b4acf4f50e1f5cdb3e55 +Activity.SpanId: 42e7f4439fc2b416 +Activity.TraceFlags: Recorded +Activity.ActivitySourceName: LinksCreationWithNewRootActivities +Activity.DisplayName: FannedOutActivity 3 +Activity.Kind: Internal +Activity.StartTime: 2023-10-17T01:24:40.5930378Z +Activity.Duration: 00:00:00.0008622 +Activity.Links: + 2890476acefb53b93af64a0d91939051 16b83c1517629363 +Resource associated with Activity: + telemetry.sdk.name: opentelemetry + telemetry.sdk.language: dotnet + telemetry.sdk.version: 0.0.0-alpha.0.2600 + service.name: unknown_service:links-creation + +Activity.TraceId: 2890476acefb53b93af64a0d91939051 +Activity.SpanId: 6878c2a84d4d4996 +Activity.TraceFlags: Recorded +Activity.ParentSpanId: 16b83c1517629363 +Activity.ActivitySourceName: LinksCreationWithNewRootActivities +Activity.DisplayName: WrapUp +Activity.Kind: Internal +Activity.StartTime: 2023-10-17T01:24:40.5950683Z +Activity.Duration: 00:00:00.0008843 +Activity.Tags: + foo: 1 +Resource associated with Activity: + telemetry.sdk.name: opentelemetry + telemetry.sdk.language: dotnet + telemetry.sdk.version: 0.0.0-alpha.0.2600 + service.name: unknown_service:links-creation + +Activity.TraceId: 2890476acefb53b93af64a0d91939051 +Activity.SpanId: 16b83c1517629363 +Activity.TraceFlags: Recorded +Activity.ActivitySourceName: LinksCreationWithNewRootActivities +Activity.DisplayName: OrchestratingActivity +Activity.Kind: Internal +Activity.StartTime: 2023-10-17T01:24:40.4937024Z +Activity.Duration: 00:00:00.1043390 +Activity.Tags: + foo: 1 +Resource associated with Activity: + telemetry.sdk.name: opentelemetry + telemetry.sdk.language: dotnet + telemetry.sdk.version: 0.0.0-alpha.0.2600 + service.name: unknown_service:links-creation +``` diff --git a/docs/trace/links-creation-with-new-activities/links-creation.csproj b/docs/trace/links-creation-with-new-activities/links-creation.csproj new file mode 100644 index 00000000000..19aa9791432 --- /dev/null +++ b/docs/trace/links-creation-with-new-activities/links-creation.csproj @@ -0,0 +1,5 @@ + + + + +