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] Adds BaggageLogRecordProcessor #2354

Open
wants to merge 22 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
5e187a6
rename event
matt-hensley Dec 2, 2024
600b7f6
BaggageLogRecordProcessor
matt-hensley Dec 2, 2024
5c186ff
logs AddBaggageProcessor extension
matt-hensley Dec 2, 2024
d275ed8
port BaggageActivityProcessor tests
matt-hensley Dec 2, 2024
f520776
update docs
matt-hensley Dec 3, 2024
cd836ed
Merge branch 'main' into baggage-log-record-processor
matt-hensley Dec 3, 2024
a50c7a3
fix warnings
matt-hensley Dec 3, 2024
806f38b
make class sealed
Kielek Dec 3, 2024
4201b3c
Make test exporter version managed centrally
Kielek Dec 3, 2024
706fbd7
explain behavior better in doc comments
matt-hensley Dec 3, 2024
66a841c
skip alloc if baggage is empty
matt-hensley Dec 3, 2024
2343ee8
clarify docs
matt-hensley Dec 3, 2024
98dac4a
Merge branch 'main' into baggage-log-record-processor
matt-hensley Dec 4, 2024
cc2cf89
init collection size to worst case (attrs + baggage)
matt-hensley Dec 4, 2024
b67bc20
Merge branch 'baggage-log-record-processor' of https://github.com/mat…
matt-hensley Dec 4, 2024
22b5ad4
Update README.md
matt-hensley Dec 5, 2024
123d4a4
Merge branch 'main' into baggage-log-record-processor
matt-hensley Dec 6, 2024
7bde5fd
reduce Baggage.Current overhead, prop is backed by expensive method
matt-hensley Dec 9, 2024
e9e372a
remove doc ref to internal type
matt-hensley Dec 9, 2024
c44bc94
LoggerProviderBuilder extension method
matt-hensley Dec 13, 2024
705cdb0
LoggerProviderBuilder tests
matt-hensley Dec 13, 2024
1c70d6f
Merge branch 'main' into baggage-log-record-processor
matt-hensley Dec 13, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ OpenTelemetry.Trace.BaggageActivityProcessor
OpenTelemetry.Trace.TracerProviderBuilderExtensions
override OpenTelemetry.RateLimitingSampler.ShouldSample(in OpenTelemetry.Trace.SamplingParameters samplingParameters) -> OpenTelemetry.Trace.SamplingResult
override OpenTelemetry.Trace.BaggageActivityProcessor.OnStart(System.Diagnostics.Activity! data) -> void
static Microsoft.Extensions.Logging.OpenTelemetryLoggingExtensions.AddBaggageProcessor(this OpenTelemetry.Logs.OpenTelemetryLoggerOptions! loggerOptions) -> OpenTelemetry.Logs.OpenTelemetryLoggerOptions!
static Microsoft.Extensions.Logging.OpenTelemetryLoggingExtensions.AddBaggageProcessor(this OpenTelemetry.Logs.OpenTelemetryLoggerOptions! loggerOptions, System.Predicate<string!>! baggageKeyPredicate) -> OpenTelemetry.Logs.OpenTelemetryLoggerOptions!
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!
Expand Down
3 changes: 3 additions & 0 deletions src/OpenTelemetry.Extensions/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ rate per second. For details see
* Updated OpenTelemetry core component version(s) to `1.10.0`.
([#2317](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/2317))

* Adds Baggage LogRecord Processor.
([#2354](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/2354))

## 1.0.0-beta.5

Released 2024-May-08
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

using OpenTelemetry.Logs;

namespace OpenTelemetry.Extensions.Internal;

internal sealed class BaggageLogRecordProcessor : BaseProcessor<LogRecord>
{
private readonly Predicate<string> baggageKeyPredicate;

public BaggageLogRecordProcessor(Predicate<string> baggageKeyPredicate)
{
this.baggageKeyPredicate = baggageKeyPredicate ?? throw new ArgumentNullException(nameof(baggageKeyPredicate));
}

public static Predicate<string> AllowAllBaggageKeys => (_) => true;

public override void OnEnd(LogRecord data)
{
if (data != null)
{
var attributes = new List<KeyValuePair<string, object?>>();
matt-hensley marked this conversation as resolved.
Show resolved Hide resolved

foreach (var entry in Baggage.Current)
{
if (this.baggageKeyPredicate(entry.Key))
{
attributes.Add(new(entry.Key, entry.Value));
}
}

if (data.Attributes != null)
{
attributes.AddRange(data.Attributes);
matt-hensley marked this conversation as resolved.
Show resolved Hide resolved
}

data.Attributes = attributes;
}

base.OnEnd(data!);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,14 @@ public void LogRecordFilterException(string? categoryName, string 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)
public void BaggageKeyActivityPredicateException(string baggageKey, string exception)
{
this.WriteEvent(3, baggageKey, exception);
}

[Event(4, Message = "Baggage key predicate threw exeption when trying to add baggage entry with key '{0}'. Baggage entry will not be added to the log record. Exception: '{1}'", Level = EventLevel.Warning)]
public void BaggageKeyLogRecordPredicateException(string baggageKey, string exception)
{
this.WriteEvent(4, baggageKey, exception);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
// SPDX-License-Identifier: Apache-2.0

using System.Diagnostics;
using OpenTelemetry;
using OpenTelemetry.Extensions.Internal;
using OpenTelemetry.Internal;
using OpenTelemetry.Logs;

Expand Down Expand Up @@ -34,4 +36,45 @@ public static OpenTelemetryLoggerOptions AttachLogsToActivityEvent(
return loggerOptions.AddProcessor(new ActivityEventAttachingLogProcessor(options));
#pragma warning restore CA2000 // Dispose objects before losing scope
}

/// <summary>
/// Adds the <see cref="BaggageLogRecordProcessor"/> to the <see cref="OpenTelemetryLoggerOptions"/>.
matt-hensley marked this conversation as resolved.
Show resolved Hide resolved
matt-hensley marked this conversation as resolved.
Show resolved Hide resolved
/// </summary>
/// <param name="loggerOptions"><see cref="OpenTelemetryLoggerOptions"/> to add the <see cref="BaggageLogRecordProcessor"/> to.</param>
/// <returns>The instance of <see cref="OpenTelemetryLoggerOptions"/> to chain the calls.</returns>
/// <exception cref="ArgumentNullException"><paramref name="loggerOptions"/> is <c>null</c>.</exception>
public static OpenTelemetryLoggerOptions AddBaggageProcessor(
matt-hensley marked this conversation as resolved.
Show resolved Hide resolved
this OpenTelemetryLoggerOptions loggerOptions)
{
return loggerOptions.AddBaggageProcessor(BaggageLogRecordProcessor.AllowAllBaggageKeys);
}

/// <summary>
/// Adds the <see cref="BaggageLogRecordProcessor"/> to the <see cref="OpenTelemetryLoggerOptions"/>.
/// </summary>
/// <param name="loggerOptions"><see cref="OpenTelemetryLoggerOptions"/> to add the <see cref="BaggageLogRecordProcessor"/> to.</param>
/// <param name="baggageKeyPredicate">Predicate to determine which baggage keys should be added to the log record.</param>
/// <returns>The instance of <see cref="OpenTelemetryLoggerOptions"/> to chain the calls.</returns>
/// <exception cref="ArgumentNullException"><paramref name="loggerOptions"/> is <c>null</c>.</exception>
/// <exception cref="ArgumentNullException"><paramref name="baggageKeyPredicate"/> is <c>null</c>.</exception>
public static OpenTelemetryLoggerOptions AddBaggageProcessor(
this OpenTelemetryLoggerOptions loggerOptions,
Predicate<string> baggageKeyPredicate)
{
Guard.ThrowIfNull(loggerOptions);
Guard.ThrowIfNull(baggageKeyPredicate);

return loggerOptions.AddProcessor(b => new BaggageLogRecordProcessor(baggageKey =>
{
try
{
return baggageKeyPredicate(baggageKey);
}
catch (Exception exception)
{
OpenTelemetryExtensionsEventSource.Log.BaggageKeyLogRecordPredicateException(baggageKey, exception.Message);
return false;
}
}));
}
}
19 changes: 19 additions & 0 deletions src/OpenTelemetry.Extensions/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,25 @@ future.
Adds a log processor which will convert log messages into events and attach them
to the currently running `Activity`.

### AddBaggageProcessor

Adds a log processor which will copy baggage entries to log records.
The method takes an optional predicate to filter the copied baggage entries
based on the entry key. If no predicate is provided, all entries are copied.

Example of AddBaggageProcessor usage with a predicate:
matt-hensley marked this conversation as resolved.
Show resolved Hide resolved

```csharp
var regex = new Regex("^allow", RegexOptions.Compiled);
var logRecordList = new List<LogRecord>();
using var loggerFactory = LoggerFactory.Create(builder => builder
matt-hensley marked this conversation as resolved.
Show resolved Hide resolved
.AddOpenTelemetry(options =>
{
options.AddBaggageProcessor(regex.IsMatch);
// other set up (exporters, processors)
})
```

## Traces

### AutoFlushActivityProcessor
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ public static TracerProviderBuilder AddBaggageActivityProcessor(
}
catch (Exception exception)
{
OpenTelemetryExtensionsEventSource.Log.BaggageKeyPredicateException(baggageKey, exception.Message);
OpenTelemetryExtensionsEventSource.Log.BaggageKeyActivityPredicateException(baggageKey, exception.Message);
return false;
}
}));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

using System.Runtime.CompilerServices;
using System.Text.RegularExpressions;
using Microsoft.Extensions.Logging;
using OpenTelemetry.Logs;
using OpenTelemetry.Trace;
using Xunit;

namespace OpenTelemetry.Extensions.Tests.Logs;

public class BaggageLogRecordProcessorTests
{
[Fact]
public void BaggageLogRecordProcessor_CanAddAllowAllBaggageKeysPredicate()
{
var logRecordList = new List<LogRecord>();
using var loggerFactory = LoggerFactory.Create(builder => builder
.AddOpenTelemetry(options =>
{
options.AddBaggageProcessor();
options.AddInMemoryExporter(logRecordList);
})
.AddFilter("*", LogLevel.Trace)); // Enable all LogLevels
Baggage.SetBaggage("allow", "value");

var logger = loggerFactory.CreateLogger(GetTestMethodName());
logger.LogError("this does not matter");
var logRecord = Assert.Single(logRecordList);
Assert.NotNull(logRecord);
Assert.NotNull(logRecord.Attributes);
Assert.Contains(logRecord.Attributes, kv => kv.Key == "allow");
}

[Fact]
public void BaggageLogRecordProcessor_CanUseCustomPredicate()
{
var logRecordList = new List<LogRecord>();
using var loggerFactory = LoggerFactory.Create(builder => builder
.AddOpenTelemetry(options =>
{
options.AddBaggageProcessor((baggageKey) => baggageKey.StartsWith("allow", StringComparison.Ordinal));
options.AddInMemoryExporter(logRecordList);
})
.AddFilter("*", LogLevel.Trace)); // Enable all LogLevels
Baggage.SetBaggage("allow", "value");
Baggage.SetBaggage("deny", "other_value");

var logger = loggerFactory.CreateLogger(GetTestMethodName());
logger.LogError("this does not matter");
var logRecord = Assert.Single(logRecordList);
Assert.NotNull(logRecord);
Assert.NotNull(logRecord.Attributes);
Assert.Contains(logRecord.Attributes, kv => kv.Key == "allow");
Assert.DoesNotContain(logRecord.Attributes, kv => kv.Key == "deny");
}

[Fact]
public void BaggageLogRecordProcessor_CanUseRegex()
{
var regex = new Regex("^allow", RegexOptions.Compiled);
var logRecordList = new List<LogRecord>();
using var loggerFactory = LoggerFactory.Create(builder => builder
.AddOpenTelemetry(options =>
{
options.AddBaggageProcessor(regex.IsMatch);
options.AddInMemoryExporter(logRecordList);
})
.AddFilter("*", LogLevel.Trace)); // Enable all LogLevels
Baggage.SetBaggage("allow", "value");
Baggage.SetBaggage("deny", "other_value");

var logger = loggerFactory.CreateLogger(GetTestMethodName());
logger.LogError("this does not matter");
var logRecord = Assert.Single(logRecordList);
Assert.NotNull(logRecord);
Assert.NotNull(logRecord.Attributes);
Assert.Contains(logRecord.Attributes, kv => kv.Key == "allow");
Assert.DoesNotContain(logRecord.Attributes, kv => kv.Key == "deny");
}

[Fact]
public void BaggageLogRecordProcessor_PredicateThrows_DoesNothing()
{
var logRecordList = new List<LogRecord>();
using var loggerFactory = LoggerFactory.Create(builder => builder
.AddOpenTelemetry(options =>
{
options.AddBaggageProcessor(_ => throw new Exception("Predicate throws an exception."));
options.AddInMemoryExporter(logRecordList);
})
.AddFilter("*", LogLevel.Trace)); // Enable all LogLevels
Baggage.SetBaggage("deny", "value");

var logger = loggerFactory.CreateLogger(GetTestMethodName());
logger.LogError("this does not matter");
var logRecord = Assert.Single(logRecordList);
Assert.NotNull(logRecord);
Assert.DoesNotContain(logRecord?.Attributes ?? [], kv => kv.Key == "deny");
}

[Fact]
public void BaggageLogRecordProcessor_PredicateThrows_OnlyDropsEntriesThatThrow()
{
var logRecordList = new List<LogRecord>();
using var loggerFactory = LoggerFactory.Create(builder => builder
.AddOpenTelemetry(options =>
{
options.AddBaggageProcessor(key =>
{
return key != "allow" ? throw new Exception("Predicate throws an exception.") : true;
});
options.AddInMemoryExporter(logRecordList);
})
.AddFilter("*", LogLevel.Trace)); // Enable all LogLevels
Baggage.SetBaggage("allow", "value");
Baggage.SetBaggage("deny", "value");
Baggage.SetBaggage("deny_2", "value");

var logger = loggerFactory.CreateLogger(GetTestMethodName());
logger.LogError("this does not matter");
var logRecord = Assert.Single(logRecordList);
Assert.NotNull(logRecord);
Assert.NotNull(logRecord.Attributes);
Assert.Contains(logRecord.Attributes, kv => kv.Key == "allow");
Assert.DoesNotContain(logRecord?.Attributes ?? [], kv => kv.Key == "deny");
}

private static string GetTestMethodName([CallerMemberName] string callingMethodName = "")
{
return callingMethodName;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,8 @@
<Compile Include="$(RepoRoot)\test\Shared\TestActivityProcessor.cs" Link="Includes\TestActivityProcessor.cs" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="OpenTelemetry.Exporter.InMemory" Version="$(OpenTelemetryCoreLatestVersion)" />
</ItemGroup>

</Project>
Loading