-
Notifications
You must be signed in to change notification settings - Fork 165
[Tracing] Add interoperability between the Datadog Baggage API and the OpenTelemetry Baggage API #7921
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
[Tracing] Add interoperability between the Datadog Baggage API and the OpenTelemetry Baggage API #7921
Changes from 13 commits
ac471d9
d496e9f
b52ef06
3cda30c
6dc4e28
61edb29
7d6fce8
7993bf7
acfc98d
ae367aa
e9ef733
7a603e1
5d067d8
4cf6920
6513124
af30626
64ea7e4
f8b5443
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| // <copyright file="IApiBaggage.cs" company="Datadog"> | ||
| // Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. | ||
| // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. | ||
| // </copyright> | ||
|
|
||
| #nullable enable | ||
|
|
||
| using System.Collections.Generic; | ||
| using Datadog.Trace.DuckTyping; | ||
|
|
||
| namespace Datadog.Trace.ClrProfiler.AutoInstrumentation.OpenTelemetry | ||
| { | ||
| /// <summary> | ||
| /// OpenTelemetry.Baggage interface for duck-typing | ||
| /// https://github.com/open-telemetry/opentelemetry-dotnet/blob/db429bf642c1a2c2f71b49f88d63e0a661018298/src/OpenTelemetry.Api/Baggage.cs#L16 | ||
| /// </summary> | ||
| internal interface IApiBaggage | ||
| { | ||
| [DuckField(Name = "baggage")] | ||
| Dictionary<string, string> Baggage { get; } | ||
|
|
||
| IApiBaggage Create(Dictionary<string, string?>? baggageItems); | ||
|
|
||
| IReadOnlyDictionary<string, string?> GetBaggage(); | ||
|
|
||
| string? GetBaggage(string name); | ||
|
|
||
| IBaggageHolder EnsureBaggageHolder(); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Baggage holder interface for duck-typing | ||
| /// https://github.com/open-telemetry/opentelemetry-dotnet/blob/db429bf642c1a2c2f71b49f88d63e0a661018298/src/OpenTelemetry.Api/Baggage.cs#L371 | ||
| /// </summary> | ||
| internal interface IBaggageHolder | ||
| { | ||
| [DuckField(Name = "Baggage")] | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. OMG the casing inconsistency in this code base 😅 |
||
| IApiBaggage Baggage { get; set; } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,49 @@ | ||
| // <copyright file="OTelBaggage_ClearBaggageIntegration.cs" company="Datadog"> | ||
| // Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. | ||
| // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. | ||
| // </copyright> | ||
|
|
||
| #nullable enable | ||
|
|
||
| using System; | ||
| using System.ComponentModel; | ||
| using Datadog.Trace; | ||
| using Datadog.Trace.ClrProfiler.CallTarget; | ||
| using Datadog.Trace.Configuration; | ||
| using Datadog.Trace.DuckTyping; | ||
|
|
||
| namespace Datadog.Trace.ClrProfiler.AutoInstrumentation.OpenTelemetry | ||
| { | ||
| /// <summary> | ||
| /// OpenTelemetry.Api Baggage.ClearBaggage calltarget instrumentation | ||
| /// </summary> | ||
| [InstrumentMethod( | ||
| AssemblyName = "OpenTelemetry.Api", | ||
| TypeName = "OpenTelemetry.Baggage", | ||
| MethodName = "ClearBaggage", | ||
| ReturnTypeName = "OpenTelemetry.Baggage", | ||
| ParameterTypeNames = new[] { "OpenTelemetry.Baggage" }, | ||
| MinimumVersion = "1.0.0", | ||
| MaximumVersion = "1.0.0", | ||
|
andrewlock marked this conversation as resolved.
|
||
| IntegrationName = nameof(Configuration.IntegrationId.OpenTelemetry))] | ||
| [Browsable(false)] | ||
| [EditorBrowsable(EditorBrowsableState.Never)] | ||
| public sealed class OTelBaggage_ClearBaggageIntegration | ||
| { | ||
| internal const IntegrationId IntegrationId = Configuration.IntegrationId.OpenTelemetry; | ||
|
|
||
| internal static CallTargetReturn<TReturn> OnMethodEnd<TTarget, TReturn>(TTarget instance, TReturn returnValue, Exception exception, in CallTargetState state) | ||
| { | ||
| if (Tracer.Instance.CurrentTraceSettings.Settings.IsIntegrationEnabled(IntegrationId) | ||
| && exception is null) | ||
| { | ||
| // Important: Before returning to the caller, the static OpenTelemetry.Baggage APIs set the to-be-returned baggage object as OpenTelemetry.Baggage.Current. | ||
| // However, this is not done through the public setter (which we instrument), but through the backing field, so we must manually update Datadog.Trace.Baggage.Current | ||
| // so it remains in-sync. | ||
| Baggage.Current.Clear(); | ||
|
andrewlock marked this conversation as resolved.
|
||
| } | ||
|
|
||
| return new CallTargetReturn<TReturn>(returnValue); | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| // <copyright file="OTelBaggage_GetCurrentIntegration.cs" company="Datadog"> | ||
| // Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. | ||
| // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. | ||
| // </copyright> | ||
|
|
||
| #nullable enable | ||
|
|
||
| using System; | ||
| using System.ComponentModel; | ||
| using System.Linq; | ||
| using Datadog.Trace; | ||
| using Datadog.Trace.ClrProfiler.CallTarget; | ||
| using Datadog.Trace.Configuration; | ||
| using Datadog.Trace.DuckTyping; | ||
|
|
||
| namespace Datadog.Trace.ClrProfiler.AutoInstrumentation.OpenTelemetry | ||
| { | ||
| /// <summary> | ||
| /// OpenTelemetry.Api Baggage.get_Current calltarget instrumentation | ||
| /// </summary> | ||
| [InstrumentMethod( | ||
| AssemblyName = "OpenTelemetry.Api", | ||
| TypeName = "OpenTelemetry.Baggage", | ||
| MethodName = "get_Current", | ||
| ReturnTypeName = "OpenTelemetry.Baggage", | ||
| ParameterTypeNames = new string[0], | ||
| MinimumVersion = "1.0.0", | ||
| MaximumVersion = "1.0.0", | ||
| IntegrationName = nameof(Configuration.IntegrationId.OpenTelemetry))] | ||
| [Browsable(false)] | ||
| [EditorBrowsable(EditorBrowsableState.Never)] | ||
| public sealed class OTelBaggage_GetCurrentIntegration | ||
| { | ||
| internal const IntegrationId IntegrationId = Configuration.IntegrationId.OpenTelemetry; | ||
|
|
||
| internal static CallTargetState OnMethodBegin<TInstance>(TInstance apiBaggage) | ||
| where TInstance : IApiBaggage | ||
| { | ||
| if (Tracer.Instance.CurrentTraceSettings.Settings.IsIntegrationEnabled(IntegrationId)) | ||
| { | ||
| // Since Datadog.Trace.Baggage.Current may have been updated since the last time OpenTelemetry.Baggage.Current was accessed, | ||
| // we must update the underlying OpenTelemetry.Baggage.Current store with the latest Datadog.Trace.Baggage.Current items. | ||
| // Note: When the user sets OpenTelemetry.Baggage.Current, those changes will override the contents of Datadog.Trace.Baggage.Current, | ||
| // so we can always consider Datadog.Trace.Baggage.Current as being up-to-date. | ||
| var baggageHolder = apiBaggage.EnsureBaggageHolder(); | ||
| baggageHolder.Baggage = apiBaggage.Create(Baggage.Current.ToDictionary(kvp => kvp.Key, kvp => kvp.Value)); | ||
|
zacharycmontoya marked this conversation as resolved.
Outdated
|
||
| } | ||
|
|
||
| return CallTargetState.GetDefault(); | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,78 @@ | ||
| // <copyright file="OTelBaggage_RemoveBaggageIntegration.cs" company="Datadog"> | ||
| // Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. | ||
| // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. | ||
| // </copyright> | ||
|
|
||
| #nullable enable | ||
|
|
||
| using System; | ||
| using System.ComponentModel; | ||
| using System.Linq; | ||
| using Datadog.Trace; | ||
| using Datadog.Trace.ClrProfiler.CallTarget; | ||
| using Datadog.Trace.Configuration; | ||
| using Datadog.Trace.DuckTyping; | ||
|
|
||
| namespace Datadog.Trace.ClrProfiler.AutoInstrumentation.OpenTelemetry | ||
| { | ||
| /// <summary> | ||
| /// OpenTelemetry.Api Baggage.RemoveBaggage calltarget instrumentation | ||
| /// </summary> | ||
| [InstrumentMethod( | ||
| AssemblyName = "OpenTelemetry.Api", | ||
| TypeName = "OpenTelemetry.Baggage", | ||
| MethodName = "RemoveBaggage", | ||
| ReturnTypeName = "OpenTelemetry.Baggage", | ||
| ParameterTypeNames = new[] { ClrNames.String, "OpenTelemetry.Baggage" }, | ||
| MinimumVersion = "1.0.0", | ||
| MaximumVersion = "1.0.0", | ||
| IntegrationName = nameof(Configuration.IntegrationId.OpenTelemetry))] | ||
| [Browsable(false)] | ||
| [EditorBrowsable(EditorBrowsableState.Never)] | ||
| public sealed class OTelBaggage_RemoveBaggageIntegration | ||
| { | ||
| internal const IntegrationId IntegrationId = Configuration.IntegrationId.OpenTelemetry; | ||
|
|
||
| internal static CallTargetState OnMethodBegin<TInstance, TBaggage>(TInstance instance, string key, TBaggage apiBaggage) | ||
| where TBaggage : IApiBaggage | ||
| { | ||
| // If the user provides the default baggage instance, then OpenTelemetry.Baggage.Current will be used as the baggage source, | ||
| // so we must update the underlying OpenTelemetry.Baggage.Current store to the latest Datadog.Trace.Baggage.Current items. | ||
| if (Tracer.Instance.CurrentTraceSettings.Settings.IsIntegrationEnabled(IntegrationId) | ||
| && apiBaggage.Baggage is null) | ||
| { | ||
| // Since Datadog.Trace.Baggage.Current may have been updated since the last time OpenTelemetry.Baggage.Current was accessed, | ||
| // we must update the underlying OpenTelemetry.Baggage.Current store with the latest Datadog.Trace.Baggage.Current items. | ||
| // Note: When the user sets OpenTelemetry.Baggage.Current, those changes will override the contents of Datadog.Trace.Baggage.Current, | ||
| // so we can always consider Datadog.Trace.Baggage.Current as being up-to-date. | ||
| var baggageHolder = apiBaggage.EnsureBaggageHolder(); | ||
| baggageHolder.Baggage = apiBaggage.Create(Baggage.Current.ToDictionary(kvp => kvp.Key, kvp => kvp.Value)); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Given the OTel APIs treat baggage in a case-insensitive manner, what happens if we set the same key here? Are the duplicate keys overwritten, or does it throw?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No exception, the entry just gets overwritten. But I'm resolving this with open-telemetry/opentelemetry-dotnet#6931 |
||
| } | ||
|
|
||
| return CallTargetState.GetDefault(); | ||
| } | ||
|
|
||
| internal static CallTargetReturn<TReturn> OnMethodEnd<TTarget, TReturn>(TTarget instance, TReturn returnValue, Exception exception, in CallTargetState state) | ||
| { | ||
| if (Tracer.Instance.CurrentTraceSettings.Settings.IsIntegrationEnabled(IntegrationId) | ||
| && exception is null | ||
| && returnValue.TryDuckCast<IApiBaggage>(out var apiBaggage)) | ||
| { | ||
| // Important: Before returning to the caller, the static OpenTelemetry.Baggage APIs set the to-be-returned baggage object as OpenTelemetry.Baggage.Current. | ||
| // However, this is not done through the public setter (which we instrument), but through the backing field, so we must manually update Datadog.Trace.Baggage.Current | ||
| // so it remains in-sync. | ||
| // | ||
| // Additional notes: | ||
| // - Since the Datadog Baggage model is mutable (allowing the user to get the Datadog.Trace.Baggage.Current once and continue to mutate that reference), | ||
| // we must clear then add the new baggage items. | ||
| // - The API can be invoked with an arbitrary OpenTelemetry.Baggage object passed via the parameter, so we must replace all baggage items every time | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To clarify, if the user calls
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes. Haha. For the static methods, the baggage update will take place on the baggage object passed in or |
||
| // we perform this instrumentation. | ||
| Baggage.Current.Clear(); | ||
| var newBaggage = new Baggage(apiBaggage.GetBaggage()); | ||
| newBaggage.MergeInto(Baggage.Current); | ||
| } | ||
|
|
||
| return new CallTargetReturn<TReturn>(returnValue); | ||
| } | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.