Skip to content
Merged
19 changes: 14 additions & 5 deletions src/OpenTelemetry.Api/Baggage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ public static Baggage Create(Dictionary<string, string>? baggageItems = null)
return default;
}

Dictionary<string, string> baggageCopy = new Dictionary<string, string>(baggageItems.Count, StringComparer.OrdinalIgnoreCase);
Dictionary<string, string> baggageCopy = new Dictionary<string, string>(baggageItems.Count, StringComparer.Ordinal);
foreach (KeyValuePair<string, string> baggageItem in baggageItems)
{
if (string.IsNullOrEmpty(baggageItem.Value))
Expand Down Expand Up @@ -239,13 +239,18 @@ public IReadOnlyDictionary<string, string> GetBaggage()
/// <returns>New <see cref="Baggage"/> containing the key/value pair.</returns>
public Baggage SetBaggage(string name, string? value)
{
if (string.IsNullOrEmpty(name))
{
return this;
}

if (string.IsNullOrEmpty(value))
{
return this.RemoveBaggage(name);
}

return new Baggage(
new Dictionary<string, string>(this.baggage ?? EmptyBaggage, StringComparer.OrdinalIgnoreCase)
new Dictionary<string, string>(this.baggage ?? EmptyBaggage, StringComparer.Ordinal)
{
[name] = value!,
});
Expand All @@ -271,11 +276,15 @@ public Baggage SetBaggage(IEnumerable<KeyValuePair<string, string?>> baggageItem
return this;
}

var newBaggage = new Dictionary<string, string>(this.baggage ?? EmptyBaggage, StringComparer.OrdinalIgnoreCase);
var newBaggage = new Dictionary<string, string>(this.baggage ?? EmptyBaggage, StringComparer.Ordinal);

foreach (var item in baggageItems)
{
if (string.IsNullOrEmpty(item.Value))
if (string.IsNullOrEmpty(item.Key))
{
continue;
}
else if (string.IsNullOrEmpty(item.Value))
{
newBaggage.Remove(item.Key);
}
Expand All @@ -295,7 +304,7 @@ public Baggage SetBaggage(IEnumerable<KeyValuePair<string, string?>> baggageItem
/// <returns>New <see cref="Baggage"/> with the key/value pair removed.</returns>
public Baggage RemoveBaggage(string name)
{
var baggage = new Dictionary<string, string>(this.baggage ?? EmptyBaggage, StringComparer.OrdinalIgnoreCase);
var baggage = new Dictionary<string, string>(this.baggage ?? EmptyBaggage, StringComparer.Ordinal);
baggage.Remove(name);

return new Baggage(baggage);
Expand Down
6 changes: 6 additions & 0 deletions src/OpenTelemetry.Api/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ Notes](../../RELEASENOTES.md).

## Unreleased

* **Breaking change:** The Baggage API implements the latest [Baggage API
specification](https://github.com/open-telemetry/opentelemetry-specification/blob/v1.36.0/specification/baggage/api.md),
which disallows empty baggage names and treats baggage names and values as case
sensitive.
([#6931](https://github.com/open-telemetry/opentelemetry-dotnet/pull/6931))

## 1.15.0

Released 2026-Jan-21
Expand Down
51 changes: 39 additions & 12 deletions test/OpenTelemetry.Api.Tests/BaggageTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,26 +26,36 @@ public void EmptyTest()
[Fact]
public void SetAndGetTest()
{
var list = new List<KeyValuePair<string, string>>(2)
var list = new List<KeyValuePair<string, string>>(4)
{
new(K1, V1),
new(K2, V2),
new("key2", "VALUE2"),
new("KEY2", "VALUE2"),

// Encode Unicode characters in Basic Multilingual Plane (U+0000 to U+FFFF)
// and in supplementary code points (U+10000 to U+10FFFF)
new("key \U000000A9", "value \U0001F600"),
};

Baggage.SetBaggage(K1, V1);
var baggage = Baggage.Current.SetBaggage(K2, V2);
var baggage = Baggage.Current.SetBaggage(new KeyValuePair<string, string?>("key2", "value2"));
baggage = baggage.SetBaggage(new KeyValuePair<string, string?>("key2", "VALUE2"));
baggage = baggage.SetBaggage(new KeyValuePair<string, string?>("KEY2", "VALUE2"));
baggage = baggage.SetBaggage("key \U000000A9", "value \U0001F600");
Baggage.Current = baggage;

Assert.NotEmpty(Baggage.GetBaggage());
Assert.Equal(list, Baggage.GetBaggage(Baggage.Current));

Assert.Equal(V1, Baggage.GetBaggage(K1));
#pragma warning disable CA1308 // Normalize strings to uppercase
Assert.Equal(V1, Baggage.GetBaggage(K1.ToLower(CultureInfo.InvariantCulture)));
Assert.Null(Baggage.GetBaggage(K1.ToLower(CultureInfo.InvariantCulture)));
#pragma warning restore CA1308 // Normalize strings to uppercase
Assert.Equal(V1, Baggage.GetBaggage(K1.ToUpper(CultureInfo.InvariantCulture)));
Assert.Null(Baggage.GetBaggage(K1.ToUpper(CultureInfo.InvariantCulture)));
Assert.Null(Baggage.GetBaggage("NO_KEY"));
Assert.Equal(V2, Baggage.Current.GetBaggage(K2));
Assert.Equal("VALUE2", Baggage.Current.GetBaggage("key2"));
Assert.Equal("VALUE2", Baggage.Current.GetBaggage("KEY2"));
Assert.Equal("value \U0001F600", Baggage.Current.GetBaggage("key \U000000A9"));

Assert.Throws<ArgumentException>(() => Baggage.GetBaggage(null!));
}
Expand All @@ -65,6 +75,21 @@ public void SetExistingKeyTest()
Assert.Equal(list, Baggage.GetBaggage());
}

[Fact]
public void SetEmptyNameTest()
{
var baggage = Baggage.Current;
baggage = Baggage.SetBaggage(K1, V1, baggage);

Assert.Equal(1, Baggage.Current.Count);
Assert.Equal(1, baggage.Count);

baggage = Baggage.Current.SetBaggage(string.Empty, "unused-value-1");

Assert.Equal(1, baggage.Count);
Assert.Contains(baggage.GetBaggage(), kvp => kvp.Key == K1);
}

[Fact]
public void SetNullValueTest()
{
Expand Down Expand Up @@ -100,8 +125,8 @@ public void RemoveTest()
var baggage = Baggage.SetBaggage(new Dictionary<string, string?>
{
[K1] = V1,
[K2] = V2,
[K3] = V3,
["key2"] = "value2",
["KEY2"] = "VALUE2",
});

var baggage2 = Baggage.RemoveBaggage(K1, baggage);
Expand Down Expand Up @@ -133,9 +158,9 @@ public void ClearTest()
public void ContextFlowTest()
{
var baggage = Baggage.SetBaggage(K1, V1);
var baggage2 = Baggage.Current.SetBaggage(K2, V2);
var baggage2 = Baggage.Current.SetBaggage("key2", "value2");
Baggage.Current = baggage2;
var baggage3 = Baggage.SetBaggage(K3, V3);
var baggage3 = Baggage.SetBaggage("KEY2", "VALUE2");

Assert.Equal(1, baggage.Count);
Assert.Equal(2, baggage2.Count);
Expand Down Expand Up @@ -213,9 +238,11 @@ public void CreateBaggageTest()
["Key3"] = null!, // Note: This causes Key3 to be removed
});

Assert.Equal(2, baggage.Count);
Assert.Equal(4, baggage.Count);
Assert.Contains(baggage.GetBaggage(), kvp => kvp.Key == K1);
Assert.Equal("VALUE2", Baggage.GetBaggage("key2", baggage));
Assert.Equal("value2", Baggage.GetBaggage("key2", baggage));
Assert.Equal("VALUE2", Baggage.GetBaggage("KEY2", baggage));
Assert.Equal("VALUE3", Baggage.GetBaggage("KEY3", baggage));
}

[Fact]
Expand Down