Skip to content
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

[baggage-api] allow metadata storage in baggage #6144

Open
wants to merge 15 commits into
base: main
Choose a base branch
from
1 change: 1 addition & 0 deletions docs/trace/extending-the-sdk/MyEnrichingProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System.Diagnostics;
using OpenTelemetry;
#pragma warning disable CS0618 // Type or member is obsolete
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you have a plan to make follow up PR with updating this, if so, I am fine?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, my initial plan was to address this warning in a separate PR.


internal class MyEnrichingProcessor : BaseProcessor<Activity>
{
Expand Down
31 changes: 31 additions & 0 deletions src/OpenTelemetry.Api/.publicApi/Stable/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
OpenTelemetry.Baggage.Enumerator
OpenTelemetry.Baggage.Enumerator.Current.get -> System.Collections.Generic.KeyValuePair<string!, OpenTelemetry.BaggageEntry>
OpenTelemetry.Baggage.Enumerator.Dispose() -> void
OpenTelemetry.Baggage.Enumerator.Enumerator() -> void
OpenTelemetry.Baggage.Enumerator.MoveNext() -> bool
OpenTelemetry.Baggage.Enumerator.Reset() -> void
OpenTelemetry.Baggage.GetBaggageWithMetadata(string! name) -> OpenTelemetry.BaggageEntry?
OpenTelemetry.Baggage.GetEnumeratorWithMetadata() -> OpenTelemetry.Baggage.Enumerator
OpenTelemetry.Baggage.SetBaggage(string! name, string? value, string? metadata) -> OpenTelemetry.Baggage
OpenTelemetry.BaggageEntry
OpenTelemetry.BaggageEntry.BaggageEntry() -> void
OpenTelemetry.BaggageEntry.BaggageEntry(string! value, OpenTelemetry.BaggageEntryMetadata? metadata = null) -> void
OpenTelemetry.BaggageEntry.Equals(OpenTelemetry.BaggageEntry other) -> bool
OpenTelemetry.BaggageEntry.Metadata.get -> OpenTelemetry.BaggageEntryMetadata?
OpenTelemetry.BaggageEntry.Value.get -> string!
OpenTelemetry.BaggageEntryMetadata
OpenTelemetry.BaggageEntryMetadata.BaggageEntryMetadata() -> void
OpenTelemetry.BaggageEntryMetadata.BaggageEntryMetadata(string? value) -> void
OpenTelemetry.BaggageEntryMetadata.Equals(OpenTelemetry.BaggageEntryMetadata other) -> bool
OpenTelemetry.BaggageEntryMetadata.Value.get -> string?
override OpenTelemetry.BaggageEntry.Equals(object? obj) -> bool
override OpenTelemetry.BaggageEntry.GetHashCode() -> int
override OpenTelemetry.BaggageEntryMetadata.Equals(object? obj) -> bool
override OpenTelemetry.BaggageEntryMetadata.GetHashCode() -> int
static OpenTelemetry.Baggage.CreateWithMetadata(System.Collections.Generic.Dictionary<string!, OpenTelemetry.BaggageEntry>? baggageItems = null) -> OpenTelemetry.Baggage
static OpenTelemetry.Baggage.GetEnumeratorWithMetadata(OpenTelemetry.Baggage baggage) -> OpenTelemetry.Baggage.Enumerator
static OpenTelemetry.Baggage.SetBaggage(string! name, string? value, string? metadata, OpenTelemetry.Baggage baggage) -> OpenTelemetry.Baggage
static OpenTelemetry.BaggageEntry.operator !=(OpenTelemetry.BaggageEntry left, OpenTelemetry.BaggageEntry right) -> bool
static OpenTelemetry.BaggageEntry.operator ==(OpenTelemetry.BaggageEntry left, OpenTelemetry.BaggageEntry right) -> bool
static OpenTelemetry.BaggageEntryMetadata.operator !=(OpenTelemetry.BaggageEntryMetadata left, OpenTelemetry.BaggageEntryMetadata right) -> bool
static OpenTelemetry.BaggageEntryMetadata.operator ==(OpenTelemetry.BaggageEntryMetadata left, OpenTelemetry.BaggageEntryMetadata right) -> bool
285 changes: 251 additions & 34 deletions src/OpenTelemetry.Api/Baggage.cs

Large diffs are not rendered by default.

54 changes: 54 additions & 0 deletions src/OpenTelemetry.Api/BaggageEntry.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

namespace OpenTelemetry;

/// <summary>
/// Baggage entry consisting of a value with optional metadata.
/// </summary>
public readonly struct BaggageEntry : IEquatable<BaggageEntry>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: If you define BaggageEntry and BaggageEntryMetadata as readonly record struct the compiler should auto-generate a lot of this boilerplate for you (IEquatable<> implementation, GetHashCode, ToString, operators, etc).

{
/// <summary>
/// Initializes a new instance of the <see cref="BaggageEntry"/> struct.
/// </summary>
/// <param name="value">Entry value.</param>
/// <param name="metadata">Entry metadata.</param>
public BaggageEntry(string value, BaggageEntryMetadata? metadata = null)
{
this.Value = value;
this.Metadata = metadata;
}

/// <summary>
/// Gets the value of the BaggageEntry.
/// </summary>
public string Value { get; }

/// <summary>
/// Gets the metadata of the BaggageEntry.
/// </summary>
public BaggageEntryMetadata? Metadata { get; }

#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
public static bool operator ==(BaggageEntry left, BaggageEntry right) => left.Equals(right);

public static bool operator !=(BaggageEntry left, BaggageEntry right) => !left.Equals(right);
#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member

/// <inheritdoc />
public bool Equals(BaggageEntry other) =>
string.Equals(this.Value, other.Value) &&
this.Metadata.Equals(other.Metadata);

/// <inheritdoc />
public override bool Equals(object? obj) => obj is BaggageEntry other && this.Equals(other);

/// <inheritdoc />
public override int GetHashCode()
{
unchecked
{
return (this.Value.GetHashCode() * 397) ^ (this.Metadata?.GetHashCode() ?? 0);
}
}
}
42 changes: 42 additions & 0 deletions src/OpenTelemetry.Api/BaggageEntryMetadata.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

namespace OpenTelemetry;

/// <summary>
/// Wraps string value of metadata.
/// <remarks>
/// Spec reference: <a href="https://github.com/open-telemetry/opentelemetry-specification/blob/v1.42.0/specification/baggage/api.md?plain=1#L117-L119">metadata</a>.
/// </remarks>
/// </summary>
public readonly struct BaggageEntryMetadata : IEquatable<BaggageEntryMetadata>
{
/// <summary>
/// Initializes a new instance of the <see cref="BaggageEntryMetadata"/> struct.
/// </summary>
/// <param name="value">Metadata value.</param>
public BaggageEntryMetadata(string? value)
{
this.Value = value;
}

/// <summary>
/// Gets the value.
/// </summary>
public string? Value { get; }

#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
public static bool operator ==(BaggageEntryMetadata left, BaggageEntryMetadata right) => left.Equals(right);

public static bool operator !=(BaggageEntryMetadata left, BaggageEntryMetadata right) => !left.Equals(right);
#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member

/// <inheritdoc />
public bool Equals(BaggageEntryMetadata other) => string.Equals(this.Value, other.Value);

/// <inheritdoc />
public override bool Equals(object? obj) => obj is BaggageEntryMetadata other && this.Equals(other);

/// <inheritdoc />
public override int GetHashCode() => this.Value?.GetHashCode() ?? 0;
}
4 changes: 4 additions & 0 deletions src/OpenTelemetry.Api/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ Notes](../../RELEASENOTES.md).

## Unreleased

* Update `Baggage` implementation to allow
[`metadata`](https://github.com/open-telemetry/opentelemetry-specification/blob/v1.42.0/specification/baggage/api.md?plain=1#L117-L119)
storage. ([#6144](https://github.com/open-telemetry/opentelemetry-dotnet/pull/6144))

## 1.11.1

Released 2025-Jan-22
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,9 @@ public override void Inject<T>(PropagationContext context, T carrier, Action<T,
return;
}

#pragma warning disable CS0618 // Type or member is obsolete
using var e = context.Baggage.GetEnumerator();
#pragma warning restore CS0618 // Type or member is obsolete

if (e.MoveNext() == true)
{
Expand Down
2 changes: 2 additions & 0 deletions src/OpenTelemetry.Shims.OpenTracing/SpanContextShim.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,7 @@ public SpanContextShim(in Trace.SpanContext spanContext)
public string SpanId => this.SpanContext.SpanId.ToString();

public IEnumerable<KeyValuePair<string, string>> GetBaggageItems()
#pragma warning disable CS0618 // Type or member is obsolete
=> Baggage.GetBaggage();
#pragma warning restore CS0618 // Type or member is obsolete
}
136 changes: 136 additions & 0 deletions test/OpenTelemetry.Api.Tests/BaggageTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ public class BaggageTests
private const string V2 = "Value2";
private const string V3 = "Value3";

private const string M1 = "Metadata1";
private const string M2 = "Metadata2";
private const string M3 = "Metadata3";

[Fact]
public void EmptyTest()
{
Expand Down Expand Up @@ -47,6 +51,44 @@ public void SetAndGetTest()
Assert.Throws<ArgumentException>(() => Baggage.GetBaggage(null!));
}

[Fact]
public void SetAndGetWithMetadataTest()
{
var expectedBaggageContents = new List<KeyValuePair<string, BaggageEntry>>(2)
{
new(K1, new(V1, new BaggageEntryMetadata(M1))),
new(K2, new(V2, new BaggageEntryMetadata(M2))),
};

var baggage = Baggage.Current
.SetBaggage(K1, V1, M1)
.SetBaggage(K2, V2, M2);
Baggage.Current = baggage;

Assert.NotEmpty(Baggage.GetBaggage());

var returnedBaggage = new List<KeyValuePair<string, BaggageEntry>>();

var enumerator = Baggage.Current.GetEnumeratorWithMetadata();
while (enumerator.MoveNext())
{
returnedBaggage.Add(enumerator.Current);
}

Assert.Equal(expectedBaggageContents, returnedBaggage);

var expectedBaggageEntry1 = new BaggageEntry(V1, new BaggageEntryMetadata(M1));
var expectedBaggageEntry2 = new BaggageEntry(V2, new BaggageEntryMetadata(M2));

Assert.Equal(expectedBaggageEntry1, Baggage.Current.GetBaggageWithMetadata(K1));
Assert.Equal(expectedBaggageEntry1, Baggage.Current.GetBaggageWithMetadata(K1.ToLower()));
Assert.Equal(expectedBaggageEntry1, Baggage.Current.GetBaggageWithMetadata(K1.ToUpper()));

Assert.Null(Baggage.Current.GetBaggageWithMetadata("NO_KEY"));

Assert.Equal(expectedBaggageEntry2, Baggage.Current.GetBaggageWithMetadata(K2));
}

[Fact]
public void SetExistingKeyTest()
{
Expand Down Expand Up @@ -109,6 +151,33 @@ public void RemoveTest()
Assert.DoesNotContain(new KeyValuePair<string, string>(K1, V1), baggage2.GetBaggage());
}

[Fact]
public void RemoveWithMetadataTest()
{
var baggage = Baggage.CreateWithMetadata(new Dictionary<string, BaggageEntry>
{
[K1] = new(V1, new BaggageEntryMetadata(M1)),
[K2] = new(V2, new BaggageEntryMetadata(M2)),
[K3] = new(V3, new BaggageEntryMetadata(M3)),
});

var baggage2 = Baggage.RemoveBaggage(K1, baggage);

Assert.Equal(3, baggage.Count);
Assert.Equal(2, baggage2.Count);

Assert.Null(baggage2.GetBaggageWithMetadata(K1));

var b = new List<KeyValuePair<string, BaggageEntry>>();
var enumeratorWithMetadata = baggage2.GetEnumeratorWithMetadata();
while (enumeratorWithMetadata.MoveNext())
{
b.Add(enumeratorWithMetadata.Current);
}

Assert.DoesNotContain(new KeyValuePair<string, BaggageEntry>(K1, new BaggageEntry(V1, new BaggageEntryMetadata(M1))), b);
}

[Fact]
public void ClearTest()
{
Expand Down Expand Up @@ -175,6 +244,35 @@ public void EnumeratorTest()
Assert.False(enumerator.MoveNext());
}

[Fact]
public void EnumeratorWithMetadataTest()
{
var list = new List<KeyValuePair<string, BaggageEntry>>(2)
{
new(K1, new BaggageEntry(V1)),
new(K2, new BaggageEntry(V2, new BaggageEntryMetadata(M2))),
};

var baggage = Baggage.SetBaggage(K1, V1);
baggage = Baggage.SetBaggage(K2, V2, M2, baggage);

var enumerator = Baggage.GetEnumeratorWithMetadata(baggage);

Assert.True(enumerator.MoveNext());
var baggageItem1 = enumerator.Current;
Assert.True(enumerator.MoveNext());
var baggageItem2 = enumerator.Current;
Assert.False(enumerator.MoveNext());

Assert.Equal(list, new List<KeyValuePair<string, BaggageEntry>> { baggageItem1, baggageItem2 });

Baggage.ClearBaggage();

enumerator = Baggage.GetEnumeratorWithMetadata(default);

Assert.False(enumerator.MoveNext());
}

[Fact]
public void EqualsTest()
{
Expand Down Expand Up @@ -240,6 +338,22 @@ public void EqualityTests()
Assert.True(baggage.Equals((object)baggage2));
}

[Fact]
public void EqualityWithMetadataTests()
{
var emptyBaggage = Baggage.Create(null);

var baggage = Baggage.SetBaggage(K1, V1, M1, default);

Assert.NotEqual(emptyBaggage, baggage);

Assert.True(emptyBaggage != baggage);

baggage = Baggage.ClearBaggage(baggage);

Assert.Equal(emptyBaggage, baggage);
}

[Fact]
public void GetHashCodeTests()
{
Expand All @@ -257,6 +371,28 @@ public void GetHashCodeTests()
Assert.Equal(expectedBaggage.GetHashCode(), baggage.GetHashCode());
}

[Fact]
public void GetHashCodeWithMetadataTests()
{
var baggage = Baggage.Current;
var emptyBaggage = Baggage.Create(null);

baggage = Baggage.SetBaggage(K1, V1, M1, baggage).SetBaggage(K2, V2);
baggage = Baggage.SetBaggage(K3, V3, M3, baggage);

Assert.NotEqual(emptyBaggage.GetHashCode(), baggage.GetHashCode());

var expectedBaggage = Baggage.CreateWithMetadata(
new Dictionary<string, BaggageEntry>
{
[K1] = new(V1, new BaggageEntryMetadata(M1)),
[K2] = new(V2),
[K3] = new(V3, new BaggageEntryMetadata(M3)),
});

Assert.Equal(expectedBaggage.GetHashCode(), baggage.GetHashCode());
}

[Fact]
public async Task AsyncLocalTests()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ public Dictionary<string, string> GetChildActivityTraceContext()
[Route("api/GetChildActivityBaggageContext")]
public IReadOnlyDictionary<string, string> GetChildActivityBaggageContext()
{
#pragma warning disable CS0618 // Type or member is obsolete
var result = Baggage.Current.GetBaggage();
#pragma warning restore CS0618 // Type or member is obsolete
return result;
}

Expand Down
Loading