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

[Extensions] Update BaggageActivityProcessor to use baggage key predicate #1816

Open
wants to merge 38 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
c83125b
Update BaggageActivityProcessor to use baggage key predicate
MikeGoldsmith May 20, 2024
5882102
add changelog entry
MikeGoldsmith May 20, 2024
f0197b3
fix readme lint errors
MikeGoldsmith May 20, 2024
faf5890
more linting
MikeGoldsmith May 20, 2024
1d29bde
update extension to take predicate as param
MikeGoldsmith May 20, 2024
e1f0a0c
add regex extension method and example
MikeGoldsmith May 21, 2024
cce029b
Merge branch 'main' of github.com:open-telemetry/opentelemetry-dotnet…
MikeGoldsmith May 21, 2024
2823819
Merge branch 'main' into mike/baggage-predicate
MikeGoldsmith May 22, 2024
7d22d69
add tests
MikeGoldsmith May 22, 2024
bb6c60d
Merge branch 'mike/baggage-predicate' of github.com:honeycombio/opent…
MikeGoldsmith May 22, 2024
65d17e4
add stringcompare in tst to resolve warning
MikeGoldsmith May 22, 2024
94932d1
remove regex extension method, move all keys to processor class
MikeGoldsmith Jun 6, 2024
d2d7ab0
wrap baggage key invovationto prevent exceptions leaking
MikeGoldsmith Jun 6, 2024
603a7a0
clean up readme examples
MikeGoldsmith Jun 6, 2024
798c6de
use builder pattern in extention method
MikeGoldsmith Jun 7, 2024
ca4a71b
update all baggage keys name
MikeGoldsmith Jun 7, 2024
20dce83
add baggage activity error to event source
MikeGoldsmith Jun 7, 2024
aa70c90
update public api
MikeGoldsmith Jun 7, 2024
d7d9878
docs cleanup
MikeGoldsmith Jun 7, 2024
7a86096
update changelog
MikeGoldsmith Jun 7, 2024
e834e70
rename test file
MikeGoldsmith Jun 7, 2024
bd28cde
update test names
MikeGoldsmith Jun 7, 2024
21c2c5b
Merge branch 'main' into mike/baggage-predicate
MikeGoldsmith Jun 7, 2024
3ccca82
fix lint errors
MikeGoldsmith Jun 7, 2024
6c114a3
Apply suggestions from code review
MikeGoldsmith Jun 7, 2024
597411d
Merge branch 'main' into mike/baggage-predicate
MikeGoldsmith Jun 11, 2024
8b76412
Apply suggestions from code review
MikeGoldsmith Jun 12, 2024
f147ef9
tweak warning in readme
MikeGoldsmith Jun 12, 2024
bc08c17
update exception message
MikeGoldsmith Jun 12, 2024
6661810
fix linter error
MikeGoldsmith Jun 12, 2024
881e8da
Merge branch 'main' into mike/baggage-predicate
MikeGoldsmith Jun 12, 2024
3239b2e
fix test name
MikeGoldsmith Jun 17, 2024
19fda8c
remove unused TestActivityProcessor in tests
MikeGoldsmith Jun 17, 2024
325f39f
add test to verify only entries that throw are dropped
MikeGoldsmith Jun 17, 2024
87478e0
update baggage key event source message with bagage key
MikeGoldsmith Jun 17, 2024
24ce184
Merge branch 'mike/baggage-predicate' of github.com:honeycombio/opent…
MikeGoldsmith Jun 17, 2024
2d5fc39
Merge branch 'main' of github.com:open-telemetry/opentelemetry-dotnet…
MikeGoldsmith Jun 17, 2024
761f4cf
Merge branch 'main' into mike/baggage-predicate
MikeGoldsmith Jun 18, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ OpenTelemetry.Logs.LogToActivityEventConversionOptions.ScopeConverter.get -> Sys
OpenTelemetry.Logs.LogToActivityEventConversionOptions.ScopeConverter.set -> void
OpenTelemetry.Logs.LogToActivityEventConversionOptions.StateConverter.get -> System.Action<System.Diagnostics.ActivityTagsCollection!, System.Collections.Generic.IReadOnlyList<System.Collections.Generic.KeyValuePair<string!, object?>>!>!
OpenTelemetry.Logs.LogToActivityEventConversionOptions.StateConverter.set -> void
OpenTelemetry.Trace.BaggageActivityProcessor
OpenTelemetry.Trace.TracerProviderBuilderExtensions
override OpenTelemetry.Trace.BaggageActivityProcessor.OnStart(System.Diagnostics.Activity! data) -> void
static Microsoft.Extensions.Logging.OpenTelemetryLoggingExtensions.AttachLogsToActivityEvent(this OpenTelemetry.Logs.OpenTelemetryLoggerOptions! loggerOptions, System.Action<OpenTelemetry.Logs.LogToActivityEventConversionOptions!>? configure = null) -> OpenTelemetry.Logs.OpenTelemetryLoggerOptions!
static OpenTelemetry.Trace.BaggageActivityProcessor.AllowAllBaggageKeys.get -> System.Predicate<string!>!
static OpenTelemetry.Trace.TracerProviderBuilderExtensions.AddAutoFlushActivityProcessor(this OpenTelemetry.Trace.TracerProviderBuilder! builder, System.Func<System.Diagnostics.Activity!, bool>! predicate, int timeoutMilliseconds = 10000) -> OpenTelemetry.Trace.TracerProviderBuilder!
static OpenTelemetry.Trace.TracerProviderBuilderExtensions.AddBaggageActivityProcessor(this OpenTelemetry.Trace.TracerProviderBuilder! builder) -> OpenTelemetry.Trace.TracerProviderBuilder!
static OpenTelemetry.Trace.TracerProviderBuilderExtensions.AddBaggageActivityProcessor(this OpenTelemetry.Trace.TracerProviderBuilder! builder, System.Predicate<string!>! baggageKeyPredicate) -> OpenTelemetry.Trace.TracerProviderBuilder!
3 changes: 3 additions & 0 deletions src/OpenTelemetry.Extensions/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

## Unreleased

* Update BaggageActivityProcessor to require baggage key predicate.
([#1816](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/1816))

* Updated OpenTelemetry core component version(s) to `1.9.0`.
([#1888](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/1888))

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,10 @@ public void LogRecordFilterException(string? categoryName, string exception)
{
this.WriteEvent(2, categoryName, exception);
}

[Event(3, Message = "Baggage key predicate threw exeption when trying to add baggage entry with key '{0}'. Baggage entry will not be added to the activity. Exception: '{1}'", Level = EventLevel.Warning)]
public void BaggageKeyPredicateException(string baggageKey, string exception)
{
this.WriteEvent(3, baggageKey, exception);
}
}
30 changes: 28 additions & 2 deletions src/OpenTelemetry.Extensions/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,36 @@ and adds the baggage keys and values to the `Activity` as tags (attributes) on s

Add this activity processor to a tracer provider.

Example of adding BaggageActivityProcessor to `TracerProvider`:
For example, to add all baggage entries to new activities:

```cs
var tracerProvider = Sdk.CreateTracerProviderBuilder()
.AddBaggageActivityProcessor()
.AddBaggageActivityProcessor(BaggageActivityProcessor.AllowAllBaggageKeys)
.Build();
```

Alternatively, you can select which baggage keys you want to copy using a
custom predicate function.

For example, to only copy baggage entries where the key start with `my-key`
using a custom function:

```cs
var tracerProvider = Sdk.CreateTracerProviderBuilder()
.AddBaggageActivityProcessor((baggageKey) => baggageKey.StartWith("my-key", System.StringComparison.Ordinal))
.Build();
```

For example, to only copy baggage entries where the key matches the regular
expression `^my-key`:

```cs
var baggageKeyRegex = new Regex("^mykey", RegexOptions.Compiled);
var tracerProvider = Sdk.CreateTracerProviderBuilder()
.AddBaggageActivityProcessor((baggageKey) => baggageKeyRegex.IsMatch(baggageKey))
.Build();
```

Warning: The baggage key predicate is executed for every baggage entry for each
started activity.
Do not use slow or intensive operations.
28 changes: 24 additions & 4 deletions src/OpenTelemetry.Extensions/Trace/BaggageActivityProcessor.cs
Original file line number Diff line number Diff line change
@@ -1,21 +1,41 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

using System;
using System.Diagnostics;

namespace OpenTelemetry.Trace;

/// <summary>
/// Activity processor that adds <see cref="Baggage"/> fields to every new span.
/// Activity processor that adds <see cref="Baggage"/> fields to every new activity.
/// </summary>
internal sealed class BaggageActivityProcessor : BaseProcessor<Activity>
public sealed class BaggageActivityProcessor : BaseProcessor<Activity>
MikeGoldsmith marked this conversation as resolved.
Show resolved Hide resolved
{
private readonly Predicate<string> baggageKeyPredicate;

/// <summary>
/// Initializes a new instance of the <see cref="BaggageActivityProcessor"/> class.
/// </summary>
/// <param name="baggageKeyPredicate">Predicate to determine which baggage keys should be added to the activity.</param>
internal BaggageActivityProcessor(Predicate<string> baggageKeyPredicate)
{
this.baggageKeyPredicate = baggageKeyPredicate ?? throw new ArgumentNullException(nameof(baggageKeyPredicate));
}

/// <summary>
/// Gets a baggage key predicate that returns <c>true</c> for all baggage keys.
/// </summary>
public static Predicate<string> AllowAllBaggageKeys => (_) => true;

/// <inheritdoc />
public override void OnStart(Activity activity)
public override void OnStart(Activity data)
{
foreach (var entry in Baggage.Current)
{
activity.SetTag(entry.Key, entry.Value);
if (this.baggageKeyPredicate(entry.Key))
MikeGoldsmith marked this conversation as resolved.
Show resolved Hide resolved
{
data?.SetTag(entry.Key, entry.Value);
}
}
}
}
20 changes: 16 additions & 4 deletions src/OpenTelemetry.Extensions/TracerProviderBuilderExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,14 +53,26 @@ public static class TracerProviderBuilderExtensions
/// Adds the <see cref="BaggageActivityProcessor"/> to the <see cref="TracerProviderBuilder"/>.
/// </summary>
/// <param name="builder"><see cref="TracerProviderBuilder"/> to add the <see cref="BaggageActivityProcessor"/> to.</param>
/// <param name="baggageKeyPredicate">Predicate to determine which baggage keys should be added to the activity.</param>
/// <returns>The instance of <see cref="TracerProviderBuilder"/> to chain the calls.</returns>
public static TracerProviderBuilder AddBaggageActivityProcessor(
this TracerProviderBuilder builder)
this TracerProviderBuilder builder,
Predicate<string> baggageKeyPredicate)
{
Guard.ThrowIfNull(builder);
Guard.ThrowIfNull(baggageKeyPredicate);

#pragma warning disable CA2000 // Dispose objects before losing scope
return builder.AddProcessor(new BaggageActivityProcessor());
#pragma warning restore CA2000 // Dispose objects before losing scope
return builder.AddProcessor(b => new BaggageActivityProcessor(baggageKey =>
{
try
{
return baggageKeyPredicate(baggageKey);
}
catch (Exception exception)
{
OpenTelemetryExtensionsEventSource.Log.BaggageKeyPredicateException(baggageKey, exception.Message);
return false;
}
}));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Text.RegularExpressions;
using OpenTelemetry.Trace;
using Xunit;

namespace OpenTelemetry.Extensions.Tests.Trace;

public class BaggageActivityProcessorTests
{
[Fact]
public void BaggageActivityProcessor_CanAddAllowAllBaggageKeysPredicate()
{
var sourceName = GetTestMethodName();

using var provider = Sdk.CreateTracerProviderBuilder()
.AddBaggageActivityProcessor(BaggageActivityProcessor.AllowAllBaggageKeys)
.AddSource(sourceName)
.Build();

Baggage.SetBaggage("key", "value");
Baggage.SetBaggage("other_key", "other_value");

using var source = new ActivitySource(sourceName);
using var activity = source.StartActivity("name", ActivityKind.Server);
Assert.NotNull(activity);
activity.Stop();

Assert.Contains(activity.Tags, kv => kv.Key == "key" && kv.Value == "value");
Assert.Contains(activity.Tags, kv => kv.Key == "other_key" && kv.Value == "other_value");
}

[Fact]
public void BaggageActivityProcessor_CanUseCustomPredicate()
{
var sourceName = GetTestMethodName();

using var provider = Sdk.CreateTracerProviderBuilder()
.AddBaggageActivityProcessor((baggageKey) => baggageKey.StartsWith("key", StringComparison.Ordinal))
.AddSource(sourceName)
.Build();

Baggage.SetBaggage("key", "value");
Baggage.SetBaggage("other_key", "other_value");

using var source = new ActivitySource(sourceName);
using var activity = source.StartActivity("name", ActivityKind.Client);
Assert.NotNull(activity);
activity.Stop();

Assert.Contains(activity.Tags, kv => kv.Key == "key" && kv.Value == "value");
Assert.DoesNotContain(activity.Tags, kv => kv.Key == "other_key" && kv.Value == "other_value");
}

[Fact]
public void BaggageActivityProcessor_CanUseRegex()
{
var sourceName = GetTestMethodName();

var regex = new Regex("^mykey", RegexOptions.Compiled);
using var provider = Sdk.CreateTracerProviderBuilder()
.AddBaggageActivityProcessor((baggageKey) => regex.IsMatch(baggageKey))
.AddSource(sourceName)
.Build();

Baggage.SetBaggage("mykey", "value");
Baggage.SetBaggage("other_key", "other_value");

using var source = new ActivitySource(sourceName);
using var activity = source.StartActivity("name", ActivityKind.Client);
Assert.NotNull(activity);
activity.Stop();

Assert.Contains(activity.Tags, kv => kv.Key == "mykey" && kv.Value == "value");
Assert.DoesNotContain(activity.Tags, kv => kv.Key == "other_key" && kv.Value == "other_value");
}

[Fact]
public void BaggageActivityProcessor_PredicateThrows_DoesNothing()
{
var sourceName = GetTestMethodName();

using var provider = Sdk.CreateTracerProviderBuilder()
.AddBaggageActivityProcessor(_ => throw new Exception("Predicate throws an exception."))
MikeGoldsmith marked this conversation as resolved.
Show resolved Hide resolved
.AddSource(sourceName)
.Build();

Baggage.SetBaggage("key", "value");

using var source = new ActivitySource(sourceName);
using var activity = source.StartActivity("name", ActivityKind.Server);
Assert.NotNull(activity);
activity.Stop();

Assert.DoesNotContain(activity.Tags, kv => kv.Key == "key" && kv.Value == "value");
}

[Fact]
public void BaggageActivityProcessor_PredicateThrows_OnlyDropsEntriesThatThrow()
{
var sourceName = GetTestMethodName();

// First call to predicate should not throw, second call should.
using var provider = Sdk.CreateTracerProviderBuilder()
.AddBaggageActivityProcessor(key =>
{
if (key == "key")
{
throw new Exception("Predicate throws an exception.");
}

return true;
})
.AddSource(sourceName)
.Build();

Baggage.SetBaggage("key", "value");
Baggage.SetBaggage("other_key", "other_value");
Baggage.SetBaggage("another_key", "another_value");

using var source = new ActivitySource(sourceName);
using var activity = source.StartActivity("name", ActivityKind.Server);
Assert.NotNull(activity);
activity.Stop();

// Only keys that do not throw should be added.
Assert.DoesNotContain(activity.Tags, kv => kv.Key == "key" && kv.Value == "value");
Assert.Contains(activity.Tags, kv => kv.Key == "other_key" && kv.Value == "other_value");
Assert.Contains(activity.Tags, kv => kv.Key == "another_key" && kv.Value == "another_value");
}

private static string GetTestMethodName([CallerMemberName] string callingMethodName = "")
{
return callingMethodName;
}
}