[Logs] Support dependency injection in logging build-up (#3504)

* Support dependency injection in logging. Add AddOpenTelemetryEventSourceLogEmitter extension.

* CHANGELOG update.

* Bug fixes and a test.

* More fixes and more tests.

* Tweak comments for clarity.

* Added OpenTelemetryLoggerOptions.Services xml detail remarks.

* More tests.

* More tests.

* Test fix.

* More tests.

* Tests and fixes.

* Warning cleanup.

* Added resource test.

* Smooth out multiple resource configurations.

* Resource chaining fix.

* Remove throw for additional SetResourceBuilder calls.

* Warning fixes.

* Moved OpenTelemetryEventSourceLogEmitter extension to OpenTelemetryLoggerOptions.

* Tweaks.

* Switched from static to builder pattern.

* Tweaks.

* More tests.

* Fix CHANGELOG for release.

* Tweaks.

* Test updates.

* Added a detached configuration option.

* Prevent double registration to be consistent with tracer builder pattern.

* API review.

* Warning cleanup.

* Build fixes.

* Test fix.
This commit is contained in:
Mikel Blanchard 2022-08-05 17:56:04 -07:00 committed by GitHub
parent 72f4e07d96
commit 952c3b17fc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 1452 additions and 363 deletions

View File

@ -16,19 +16,16 @@
using System.Diagnostics.Tracing;
using Examples.LoggingExtensions;
using OpenTelemetry;
using OpenTelemetry.Logs;
using OpenTelemetry.Resources;
using Serilog;
var resourceBuilder = ResourceBuilder.CreateDefault().AddService("Examples.LoggingExtensions");
var openTelemetryLoggerProvider = new OpenTelemetryLoggerProvider(options =>
{
options.IncludeFormattedMessage = true;
options
.SetResourceBuilder(resourceBuilder)
.AddConsoleExporter();
});
var openTelemetryLoggerProvider = Sdk.CreateLoggerProviderBuilder()
.SetIncludeFormattedMessage(true)
.ConfigureResource(builder => builder.AddService("Examples.LoggingExtensions"))
.AddConsoleExporter()
.Build();
// Creates an OpenTelemetryEventSourceLogEmitter for routing ExampleEventSource
// events into logs

View File

@ -1,4 +1,6 @@
#nullable enable
OpenTelemetry.Logs.OpenTelemetryEventSourceLogEmitter
OpenTelemetry.Logs.OpenTelemetryEventSourceLogEmitter.OpenTelemetryEventSourceLogEmitter(OpenTelemetry.Logs.OpenTelemetryLoggerProvider! openTelemetryLoggerProvider, System.Func<string!, System.Diagnostics.Tracing.EventLevel?>! shouldListenToFunc, bool disposeProvider = true) -> void
OpenTelemetry.Logs.OpenTelemetryEventSourceLoggerOptionsExtensions
override OpenTelemetry.Logs.OpenTelemetryEventSourceLogEmitter.Dispose() -> void
static OpenTelemetry.Logs.OpenTelemetryEventSourceLoggerOptionsExtensions.AddEventSourceLogEmitter(this OpenTelemetry.Logs.OpenTelemetryLoggerOptions! options, System.Func<string!, System.Diagnostics.Tracing.EventLevel?>! shouldListenToFunc) -> OpenTelemetry.Logs.OpenTelemetryLoggerOptions!

View File

@ -1,4 +1,6 @@
#nullable enable
OpenTelemetry.Logs.OpenTelemetryEventSourceLogEmitter
OpenTelemetry.Logs.OpenTelemetryEventSourceLogEmitter.OpenTelemetryEventSourceLogEmitter(OpenTelemetry.Logs.OpenTelemetryLoggerProvider! openTelemetryLoggerProvider, System.Func<string!, System.Diagnostics.Tracing.EventLevel?>! shouldListenToFunc, bool disposeProvider = true) -> void
OpenTelemetry.Logs.OpenTelemetryEventSourceLoggerOptionsExtensions
override OpenTelemetry.Logs.OpenTelemetryEventSourceLogEmitter.Dispose() -> void
static OpenTelemetry.Logs.OpenTelemetryEventSourceLoggerOptionsExtensions.AddEventSourceLogEmitter(this OpenTelemetry.Logs.OpenTelemetryLoggerOptions! options, System.Func<string!, System.Diagnostics.Tracing.EventLevel?>! shouldListenToFunc) -> OpenTelemetry.Logs.OpenTelemetryLoggerOptions!

View File

@ -18,6 +18,7 @@ using System;
using System.Runtime.CompilerServices;
[assembly: CLSCompliant(false)]
[assembly: InternalsVisibleTo("OpenTelemetry.Extensions.EventSource.Tests" + AssemblyInfo.PublicKey)]
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2" + AssemblyInfo.MoqPublicKey)]
#if SIGNED

View File

@ -0,0 +1,78 @@
// <copyright file="OpenTelemetryEventSourceLoggerOptionsExtensions.cs" company="OpenTelemetry Authors">
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// </copyright>
using System;
using System.Collections.Generic;
using System.Diagnostics.Tracing;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using OpenTelemetry.Internal;
namespace OpenTelemetry.Logs
{
/// <summary>
/// Contains extension methods for registering OpenTelemetry EventSource utilities into logging services.
/// </summary>
public static class OpenTelemetryEventSourceLoggerOptionsExtensions
{
/// <summary>
/// Registers an <see cref="EventListener"/> which will convert <see
/// cref="EventSource"/> events into OpenTelemetry logs.
/// </summary>
/// <param name="options"><see
/// cref="OpenTelemetryLoggerOptions"/>.</param>
/// <param name="shouldListenToFunc">Callback function used to decide if
/// events should be captured for a given <see
/// cref="EventSource.Name"/>. Return <see langword="null"/> if no
/// events should be captured.</param>
/// <returns>Supplied <see cref="OpenTelemetryLoggerOptions"/> for
/// chaining calls.</returns>
public static OpenTelemetryLoggerOptions AddEventSourceLogEmitter(
this OpenTelemetryLoggerOptions options,
Func<string, EventLevel?> shouldListenToFunc)
{
Guard.ThrowIfNull(options);
Guard.ThrowIfNull(shouldListenToFunc);
options.ConfigureServices(services => services.TryAddSingleton<EventSourceManager>());
options.ConfigureProvider((sp, provider) =>
{
var manager = sp.GetRequiredService<EventSourceManager>();
manager.Emitters.Add(
new OpenTelemetryEventSourceLogEmitter(provider, shouldListenToFunc, disposeProvider: false));
});
return options;
}
internal sealed class EventSourceManager : IDisposable
{
public List<OpenTelemetryEventSourceLogEmitter> Emitters { get; } = new();
public void Dispose()
{
foreach (var emitter in this.Emitters)
{
emitter.Dispose();
}
this.Emitters.Clear();
}
}
}
}

View File

@ -17,19 +17,42 @@ dotnet add package OpenTelemetry.Extensions.EventSource --prerelease
## Usage Example
### Configured using dependency injection
```csharp
IHost host = Host.CreateDefaultBuilder(args)
.ConfigureLogging(builder =>
{
builder.ClearProviders();
// Step 1: Configure OpenTelemetry logging...
builder.AddOpenTelemetry(options =>
{
options
.ConfigureResource(builder => builder.AddService("MyService"))
.AddConsoleExporter()
// Step 2: Register OpenTelemetryEventSourceLogEmitter to listen to events...
.AddEventSourceLogEmitter((name) => name == MyEventSource.Name ? EventLevel.Informational : null);
});
})
.Build();
host.Run();
```
### Configured manually
```csharp
// Step 1: Configure OpenTelemetryLoggerProvider...
var openTelemetryLoggerProvider = new OpenTelemetryLoggerProvider(options =>
{
options
.SetResourceBuilder(ResourceBuilder.CreateDefault().AddService("MyService"))
.AddConsoleExporter();
});
var openTelemetryLoggerProvider = Sdk.CreateLoggerProviderBuilder()
.ConfigureResource(builder => builder.AddService("MyService"))
.AddConsoleExporter()
.Build();
// Step 2: Create OpenTelemetryEventSourceLogEmitter to listen to events...
using var openTelemetryEventSourceLogEmitter = new OpenTelemetryEventSourceLogEmitter(
openTelemetryLoggerProvider,
(name) => name.StartsWith("OpenTelemetry") ? EventLevel.LogAlways : null,
(name) => name == MyEventSource.Name ? EventLevel.Informational : null,
disposeProvider: true);
```

View File

@ -17,12 +17,10 @@ dotnet add package OpenTelemetry.Extensions.Serilog --prerelease
```csharp
// Step 1: Configure OpenTelemetryLoggerProvider...
var openTelemetryLoggerProvider = new OpenTelemetryLoggerProvider(options =>
{
options
.SetResourceBuilder(ResourceBuilder.CreateDefault().AddService("MyService"))
.AddConsoleExporter();
});
var openTelemetryLoggerProvider = Sdk.CreateLoggerProviderBuilder()
.ConfigureResource(builder => builder.AddService("MyService"))
.AddConsoleExporter()
.Build();
// Step 2: Register OpenTelemetry sink with Serilog...
Log.Logger = new LoggerConfiguration()

View File

@ -94,9 +94,9 @@ readonly OpenTelemetry.BaseExportProcessor<T>.exporter -> OpenTelemetry.BaseExpo
~static OpenTelemetry.Resources.ResourceBuilderExtensions.AddEnvironmentVariableDetector(this OpenTelemetry.Resources.ResourceBuilder resourceBuilder) -> OpenTelemetry.Resources.ResourceBuilder
~static OpenTelemetry.Resources.ResourceBuilderExtensions.AddService(this OpenTelemetry.Resources.ResourceBuilder resourceBuilder, string serviceName, string serviceNamespace = null, string serviceVersion = null, bool autoGenerateServiceInstanceId = true, string serviceInstanceId = null) -> OpenTelemetry.Resources.ResourceBuilder
~static OpenTelemetry.Resources.ResourceBuilderExtensions.AddTelemetrySdk(this OpenTelemetry.Resources.ResourceBuilder resourceBuilder) -> OpenTelemetry.Resources.ResourceBuilder
~static OpenTelemetry.Sdk.CreateMeterProviderBuilder() -> OpenTelemetry.Metrics.MeterProviderBuilder
~static OpenTelemetry.Sdk.CreateTracerProviderBuilder() -> OpenTelemetry.Trace.TracerProviderBuilder
~static OpenTelemetry.Sdk.SetDefaultTextMapPropagator(OpenTelemetry.Context.Propagation.TextMapPropagator textMapPropagator) -> void
static OpenTelemetry.Sdk.CreateMeterProviderBuilder() -> OpenTelemetry.Metrics.MeterProviderBuilder!
static OpenTelemetry.Sdk.CreateTracerProviderBuilder() -> OpenTelemetry.Trace.TracerProviderBuilder!
static OpenTelemetry.Sdk.SetDefaultTextMapPropagator(OpenTelemetry.Context.Propagation.TextMapPropagator! textMapPropagator) -> void
~static OpenTelemetry.SuppressInstrumentationScope.Begin(bool value = true) -> System.IDisposable
~static OpenTelemetry.Trace.TracerProviderBuilderExtensions.AddProcessor(this OpenTelemetry.Trace.TracerProviderBuilder tracerProviderBuilder, OpenTelemetry.BaseProcessor<System.Diagnostics.Activity> processor) -> OpenTelemetry.Trace.TracerProviderBuilder
~static OpenTelemetry.Trace.TracerProviderBuilderExtensions.Build(this OpenTelemetry.Trace.TracerProviderBuilder tracerProviderBuilder) -> OpenTelemetry.Trace.TracerProvider

View File

@ -7,14 +7,23 @@ OpenTelemetry.Logs.LogRecord.Timestamp.set -> void
OpenTelemetry.Logs.LogRecord.TraceFlags.set -> void
OpenTelemetry.Logs.LogRecord.TraceId.set -> void
OpenTelemetry.Logs.LogRecord.TraceState.set -> void
OpenTelemetry.Logs.OpenTelemetryLoggerOptions.AddProcessor<T>() -> OpenTelemetry.Logs.OpenTelemetryLoggerOptions!
OpenTelemetry.Logs.OpenTelemetryLoggerOptions.ConfigureProvider(System.Action<System.IServiceProvider!, OpenTelemetry.Logs.OpenTelemetryLoggerProvider!>! configure) -> OpenTelemetry.Logs.OpenTelemetryLoggerOptions!
OpenTelemetry.Logs.OpenTelemetryLoggerOptions.ConfigureResource(System.Action<OpenTelemetry.Resources.ResourceBuilder!>! configure) -> OpenTelemetry.Logs.OpenTelemetryLoggerOptions!
OpenTelemetry.Logs.OpenTelemetryLoggerOptions.ConfigureServices(System.Action<Microsoft.Extensions.DependencyInjection.IServiceCollection!>! configure) -> OpenTelemetry.Logs.OpenTelemetryLoggerOptions!
OpenTelemetry.Logs.OpenTelemetryLoggerOptions.SetIncludeFormattedMessage(bool enabled) -> OpenTelemetry.Logs.OpenTelemetryLoggerOptions!
OpenTelemetry.Logs.OpenTelemetryLoggerOptions.SetIncludeScopes(bool enabled) -> OpenTelemetry.Logs.OpenTelemetryLoggerOptions!
OpenTelemetry.Logs.OpenTelemetryLoggerOptions.SetParseStateValues(bool enabled) -> OpenTelemetry.Logs.OpenTelemetryLoggerOptions!
OpenTelemetry.Logs.OpenTelemetryLoggerOptionsExtensions
OpenTelemetry.Logs.OpenTelemetryLoggerProvider.AddProcessor(OpenTelemetry.BaseProcessor<OpenTelemetry.Logs.LogRecord!>! processor) -> OpenTelemetry.Logs.OpenTelemetryLoggerProvider!
OpenTelemetry.Logs.OpenTelemetryLoggerProvider.ForceFlush(int timeoutMilliseconds = -1) -> bool
OpenTelemetry.Logs.OpenTelemetryLoggerProvider.OpenTelemetryLoggerProvider() -> void
OpenTelemetry.Logs.OpenTelemetryLoggerProvider.OpenTelemetryLoggerProvider(System.Action<OpenTelemetry.Logs.OpenTelemetryLoggerOptions!>! configure) -> void
OpenTelemetry.Logs.OpenTelemetryLoggerOptions.ConfigureResource(System.Action<OpenTelemetry.Resources.ResourceBuilder!>! configure) -> OpenTelemetry.Logs.OpenTelemetryLoggerOptions!
*REMOVED*static Microsoft.Extensions.Logging.OpenTelemetryLoggingExtensions.AddOpenTelemetry(this Microsoft.Extensions.Logging.ILoggingBuilder! builder, System.Action<OpenTelemetry.Logs.OpenTelemetryLoggerOptions!>? configure = null) -> Microsoft.Extensions.Logging.ILoggingBuilder!
static Microsoft.Extensions.Logging.OpenTelemetryLoggingExtensions.AddOpenTelemetry(this Microsoft.Extensions.Logging.ILoggingBuilder! builder) -> Microsoft.Extensions.Logging.ILoggingBuilder!
static Microsoft.Extensions.Logging.OpenTelemetryLoggingExtensions.AddOpenTelemetry(this Microsoft.Extensions.Logging.ILoggingBuilder! builder, OpenTelemetry.Logs.OpenTelemetryLoggerProvider! openTelemetryLoggerProvider) -> Microsoft.Extensions.Logging.ILoggingBuilder!
static Microsoft.Extensions.Logging.OpenTelemetryLoggingExtensions.AddOpenTelemetry(this Microsoft.Extensions.Logging.ILoggingBuilder! builder, OpenTelemetry.Logs.OpenTelemetryLoggerProvider! openTelemetryLoggerProvider, bool disposeProvider) -> Microsoft.Extensions.Logging.ILoggingBuilder!
static Microsoft.Extensions.Logging.OpenTelemetryLoggingExtensions.AddOpenTelemetry(this Microsoft.Extensions.Logging.ILoggingBuilder! builder, System.Action<OpenTelemetry.Logs.OpenTelemetryLoggerOptions!>? configure) -> Microsoft.Extensions.Logging.ILoggingBuilder!
static OpenTelemetry.Logs.OpenTelemetryLoggerOptionsExtensions.Build(this OpenTelemetry.Logs.OpenTelemetryLoggerOptions! options) -> OpenTelemetry.Logs.OpenTelemetryLoggerProvider!
static OpenTelemetry.Sdk.CreateLoggerProviderBuilder() -> OpenTelemetry.Logs.OpenTelemetryLoggerOptions!
~static OpenTelemetry.Metrics.MeterProviderBuilderExtensions.ConfigureResource(this OpenTelemetry.Metrics.MeterProviderBuilder meterProviderBuilder, System.Action<OpenTelemetry.Resources.ResourceBuilder> configure) -> OpenTelemetry.Metrics.MeterProviderBuilder
~static OpenTelemetry.Trace.TracerProviderBuilderExtensions.ConfigureResource(this OpenTelemetry.Trace.TracerProviderBuilder tracerProviderBuilder, System.Action<OpenTelemetry.Resources.ResourceBuilder> configure) -> OpenTelemetry.Trace.TracerProviderBuilder

View File

@ -94,9 +94,9 @@ readonly OpenTelemetry.BaseExportProcessor<T>.exporter -> OpenTelemetry.BaseExpo
~static OpenTelemetry.Resources.ResourceBuilderExtensions.AddEnvironmentVariableDetector(this OpenTelemetry.Resources.ResourceBuilder resourceBuilder) -> OpenTelemetry.Resources.ResourceBuilder
~static OpenTelemetry.Resources.ResourceBuilderExtensions.AddService(this OpenTelemetry.Resources.ResourceBuilder resourceBuilder, string serviceName, string serviceNamespace = null, string serviceVersion = null, bool autoGenerateServiceInstanceId = true, string serviceInstanceId = null) -> OpenTelemetry.Resources.ResourceBuilder
~static OpenTelemetry.Resources.ResourceBuilderExtensions.AddTelemetrySdk(this OpenTelemetry.Resources.ResourceBuilder resourceBuilder) -> OpenTelemetry.Resources.ResourceBuilder
~static OpenTelemetry.Sdk.CreateMeterProviderBuilder() -> OpenTelemetry.Metrics.MeterProviderBuilder
~static OpenTelemetry.Sdk.CreateTracerProviderBuilder() -> OpenTelemetry.Trace.TracerProviderBuilder
~static OpenTelemetry.Sdk.SetDefaultTextMapPropagator(OpenTelemetry.Context.Propagation.TextMapPropagator textMapPropagator) -> void
static OpenTelemetry.Sdk.CreateMeterProviderBuilder() -> OpenTelemetry.Metrics.MeterProviderBuilder!
static OpenTelemetry.Sdk.CreateTracerProviderBuilder() -> OpenTelemetry.Trace.TracerProviderBuilder!
static OpenTelemetry.Sdk.SetDefaultTextMapPropagator(OpenTelemetry.Context.Propagation.TextMapPropagator! textMapPropagator) -> void
~static OpenTelemetry.SuppressInstrumentationScope.Begin(bool value = true) -> System.IDisposable
~static OpenTelemetry.Trace.TracerProviderBuilderExtensions.AddProcessor(this OpenTelemetry.Trace.TracerProviderBuilder tracerProviderBuilder, OpenTelemetry.BaseProcessor<System.Diagnostics.Activity> processor) -> OpenTelemetry.Trace.TracerProviderBuilder
~static OpenTelemetry.Trace.TracerProviderBuilderExtensions.Build(this OpenTelemetry.Trace.TracerProviderBuilder tracerProviderBuilder) -> OpenTelemetry.Trace.TracerProvider

View File

@ -7,14 +7,23 @@ OpenTelemetry.Logs.LogRecord.Timestamp.set -> void
OpenTelemetry.Logs.LogRecord.TraceFlags.set -> void
OpenTelemetry.Logs.LogRecord.TraceId.set -> void
OpenTelemetry.Logs.LogRecord.TraceState.set -> void
OpenTelemetry.Logs.OpenTelemetryLoggerOptions.AddProcessor<T>() -> OpenTelemetry.Logs.OpenTelemetryLoggerOptions!
OpenTelemetry.Logs.OpenTelemetryLoggerOptions.ConfigureProvider(System.Action<System.IServiceProvider!, OpenTelemetry.Logs.OpenTelemetryLoggerProvider!>! configure) -> OpenTelemetry.Logs.OpenTelemetryLoggerOptions!
OpenTelemetry.Logs.OpenTelemetryLoggerOptions.ConfigureResource(System.Action<OpenTelemetry.Resources.ResourceBuilder!>! configure) -> OpenTelemetry.Logs.OpenTelemetryLoggerOptions!
OpenTelemetry.Logs.OpenTelemetryLoggerOptions.ConfigureServices(System.Action<Microsoft.Extensions.DependencyInjection.IServiceCollection!>! configure) -> OpenTelemetry.Logs.OpenTelemetryLoggerOptions!
OpenTelemetry.Logs.OpenTelemetryLoggerOptions.SetIncludeFormattedMessage(bool enabled) -> OpenTelemetry.Logs.OpenTelemetryLoggerOptions!
OpenTelemetry.Logs.OpenTelemetryLoggerOptions.SetIncludeScopes(bool enabled) -> OpenTelemetry.Logs.OpenTelemetryLoggerOptions!
OpenTelemetry.Logs.OpenTelemetryLoggerOptions.SetParseStateValues(bool enabled) -> OpenTelemetry.Logs.OpenTelemetryLoggerOptions!
OpenTelemetry.Logs.OpenTelemetryLoggerOptionsExtensions
OpenTelemetry.Logs.OpenTelemetryLoggerProvider.AddProcessor(OpenTelemetry.BaseProcessor<OpenTelemetry.Logs.LogRecord!>! processor) -> OpenTelemetry.Logs.OpenTelemetryLoggerProvider!
OpenTelemetry.Logs.OpenTelemetryLoggerProvider.ForceFlush(int timeoutMilliseconds = -1) -> bool
OpenTelemetry.Logs.OpenTelemetryLoggerProvider.OpenTelemetryLoggerProvider() -> void
OpenTelemetry.Logs.OpenTelemetryLoggerProvider.OpenTelemetryLoggerProvider(System.Action<OpenTelemetry.Logs.OpenTelemetryLoggerOptions!>! configure) -> void
*REMOVED*static Microsoft.Extensions.Logging.OpenTelemetryLoggingExtensions.AddOpenTelemetry(this Microsoft.Extensions.Logging.ILoggingBuilder! builder, System.Action<OpenTelemetry.Logs.OpenTelemetryLoggerOptions!>? configure = null) -> Microsoft.Extensions.Logging.ILoggingBuilder!
static Microsoft.Extensions.Logging.OpenTelemetryLoggingExtensions.AddOpenTelemetry(this Microsoft.Extensions.Logging.ILoggingBuilder! builder) -> Microsoft.Extensions.Logging.ILoggingBuilder!
static Microsoft.Extensions.Logging.OpenTelemetryLoggingExtensions.AddOpenTelemetry(this Microsoft.Extensions.Logging.ILoggingBuilder! builder, OpenTelemetry.Logs.OpenTelemetryLoggerProvider! openTelemetryLoggerProvider) -> Microsoft.Extensions.Logging.ILoggingBuilder!
static Microsoft.Extensions.Logging.OpenTelemetryLoggingExtensions.AddOpenTelemetry(this Microsoft.Extensions.Logging.ILoggingBuilder! builder, OpenTelemetry.Logs.OpenTelemetryLoggerProvider! openTelemetryLoggerProvider, bool disposeProvider) -> Microsoft.Extensions.Logging.ILoggingBuilder!
static Microsoft.Extensions.Logging.OpenTelemetryLoggingExtensions.AddOpenTelemetry(this Microsoft.Extensions.Logging.ILoggingBuilder! builder, System.Action<OpenTelemetry.Logs.OpenTelemetryLoggerOptions!>? configure) -> Microsoft.Extensions.Logging.ILoggingBuilder!
static OpenTelemetry.Logs.OpenTelemetryLoggerOptionsExtensions.Build(this OpenTelemetry.Logs.OpenTelemetryLoggerOptions! options) -> OpenTelemetry.Logs.OpenTelemetryLoggerProvider!
static OpenTelemetry.Sdk.CreateLoggerProviderBuilder() -> OpenTelemetry.Logs.OpenTelemetryLoggerOptions!
~static OpenTelemetry.Metrics.MeterProviderBuilderExtensions.ConfigureResource(this OpenTelemetry.Metrics.MeterProviderBuilder meterProviderBuilder, System.Action<OpenTelemetry.Resources.ResourceBuilder> configure) -> OpenTelemetry.Metrics.MeterProviderBuilder
~static OpenTelemetry.Trace.TracerProviderBuilderExtensions.ConfigureResource(this OpenTelemetry.Trace.TracerProviderBuilder tracerProviderBuilder, System.Action<OpenTelemetry.Resources.ResourceBuilder> configure) -> OpenTelemetry.Trace.TracerProviderBuilder

View File

@ -94,9 +94,9 @@ readonly OpenTelemetry.BaseExportProcessor<T>.exporter -> OpenTelemetry.BaseExpo
~static OpenTelemetry.Resources.ResourceBuilderExtensions.AddEnvironmentVariableDetector(this OpenTelemetry.Resources.ResourceBuilder resourceBuilder) -> OpenTelemetry.Resources.ResourceBuilder
~static OpenTelemetry.Resources.ResourceBuilderExtensions.AddService(this OpenTelemetry.Resources.ResourceBuilder resourceBuilder, string serviceName, string serviceNamespace = null, string serviceVersion = null, bool autoGenerateServiceInstanceId = true, string serviceInstanceId = null) -> OpenTelemetry.Resources.ResourceBuilder
~static OpenTelemetry.Resources.ResourceBuilderExtensions.AddTelemetrySdk(this OpenTelemetry.Resources.ResourceBuilder resourceBuilder) -> OpenTelemetry.Resources.ResourceBuilder
~static OpenTelemetry.Sdk.CreateMeterProviderBuilder() -> OpenTelemetry.Metrics.MeterProviderBuilder
~static OpenTelemetry.Sdk.CreateTracerProviderBuilder() -> OpenTelemetry.Trace.TracerProviderBuilder
~static OpenTelemetry.Sdk.SetDefaultTextMapPropagator(OpenTelemetry.Context.Propagation.TextMapPropagator textMapPropagator) -> void
static OpenTelemetry.Sdk.CreateMeterProviderBuilder() -> OpenTelemetry.Metrics.MeterProviderBuilder!
static OpenTelemetry.Sdk.CreateTracerProviderBuilder() -> OpenTelemetry.Trace.TracerProviderBuilder!
static OpenTelemetry.Sdk.SetDefaultTextMapPropagator(OpenTelemetry.Context.Propagation.TextMapPropagator! textMapPropagator) -> void
~static OpenTelemetry.SuppressInstrumentationScope.Begin(bool value = true) -> System.IDisposable
~static OpenTelemetry.Trace.TracerProviderBuilderExtensions.AddProcessor(this OpenTelemetry.Trace.TracerProviderBuilder tracerProviderBuilder, OpenTelemetry.BaseProcessor<System.Diagnostics.Activity> processor) -> OpenTelemetry.Trace.TracerProviderBuilder
~static OpenTelemetry.Trace.TracerProviderBuilderExtensions.Build(this OpenTelemetry.Trace.TracerProviderBuilder tracerProviderBuilder) -> OpenTelemetry.Trace.TracerProvider

View File

@ -7,14 +7,23 @@ OpenTelemetry.Logs.LogRecord.Timestamp.set -> void
OpenTelemetry.Logs.LogRecord.TraceFlags.set -> void
OpenTelemetry.Logs.LogRecord.TraceId.set -> void
OpenTelemetry.Logs.LogRecord.TraceState.set -> void
OpenTelemetry.Logs.OpenTelemetryLoggerOptions.AddProcessor<T>() -> OpenTelemetry.Logs.OpenTelemetryLoggerOptions!
OpenTelemetry.Logs.OpenTelemetryLoggerOptions.ConfigureProvider(System.Action<System.IServiceProvider!, OpenTelemetry.Logs.OpenTelemetryLoggerProvider!>! configure) -> OpenTelemetry.Logs.OpenTelemetryLoggerOptions!
OpenTelemetry.Logs.OpenTelemetryLoggerOptions.ConfigureServices(System.Action<Microsoft.Extensions.DependencyInjection.IServiceCollection!>! configure) -> OpenTelemetry.Logs.OpenTelemetryLoggerOptions!
OpenTelemetry.Logs.OpenTelemetryLoggerOptions.SetIncludeFormattedMessage(bool enabled) -> OpenTelemetry.Logs.OpenTelemetryLoggerOptions!
OpenTelemetry.Logs.OpenTelemetryLoggerOptions.SetIncludeScopes(bool enabled) -> OpenTelemetry.Logs.OpenTelemetryLoggerOptions!
OpenTelemetry.Logs.OpenTelemetryLoggerOptions.SetParseStateValues(bool enabled) -> OpenTelemetry.Logs.OpenTelemetryLoggerOptions!
OpenTelemetry.Logs.OpenTelemetryLoggerOptionsExtensions
OpenTelemetry.Logs.OpenTelemetryLoggerProvider.AddProcessor(OpenTelemetry.BaseProcessor<OpenTelemetry.Logs.LogRecord!>! processor) -> OpenTelemetry.Logs.OpenTelemetryLoggerProvider!
OpenTelemetry.Logs.OpenTelemetryLoggerProvider.ForceFlush(int timeoutMilliseconds = -1) -> bool
OpenTelemetry.Logs.OpenTelemetryLoggerProvider.OpenTelemetryLoggerProvider() -> void
OpenTelemetry.Logs.OpenTelemetryLoggerProvider.OpenTelemetryLoggerProvider(System.Action<OpenTelemetry.Logs.OpenTelemetryLoggerOptions!>! configure) -> void
OpenTelemetry.Logs.OpenTelemetryLoggerOptions.ConfigureResource(System.Action<OpenTelemetry.Resources.ResourceBuilder!>! configure) -> OpenTelemetry.Logs.OpenTelemetryLoggerOptions!
*REMOVED*static Microsoft.Extensions.Logging.OpenTelemetryLoggingExtensions.AddOpenTelemetry(this Microsoft.Extensions.Logging.ILoggingBuilder! builder, System.Action<OpenTelemetry.Logs.OpenTelemetryLoggerOptions!>? configure = null) -> Microsoft.Extensions.Logging.ILoggingBuilder!
static Microsoft.Extensions.Logging.OpenTelemetryLoggingExtensions.AddOpenTelemetry(this Microsoft.Extensions.Logging.ILoggingBuilder! builder) -> Microsoft.Extensions.Logging.ILoggingBuilder!
static Microsoft.Extensions.Logging.OpenTelemetryLoggingExtensions.AddOpenTelemetry(this Microsoft.Extensions.Logging.ILoggingBuilder! builder, OpenTelemetry.Logs.OpenTelemetryLoggerProvider! openTelemetryLoggerProvider) -> Microsoft.Extensions.Logging.ILoggingBuilder!
static Microsoft.Extensions.Logging.OpenTelemetryLoggingExtensions.AddOpenTelemetry(this Microsoft.Extensions.Logging.ILoggingBuilder! builder, OpenTelemetry.Logs.OpenTelemetryLoggerProvider! openTelemetryLoggerProvider, bool disposeProvider) -> Microsoft.Extensions.Logging.ILoggingBuilder!
static Microsoft.Extensions.Logging.OpenTelemetryLoggingExtensions.AddOpenTelemetry(this Microsoft.Extensions.Logging.ILoggingBuilder! builder, System.Action<OpenTelemetry.Logs.OpenTelemetryLoggerOptions!>? configure) -> Microsoft.Extensions.Logging.ILoggingBuilder!
static OpenTelemetry.Logs.OpenTelemetryLoggerOptionsExtensions.Build(this OpenTelemetry.Logs.OpenTelemetryLoggerOptions! options) -> OpenTelemetry.Logs.OpenTelemetryLoggerProvider!
static OpenTelemetry.Sdk.CreateLoggerProviderBuilder() -> OpenTelemetry.Logs.OpenTelemetryLoggerOptions!
~static OpenTelemetry.Metrics.MeterProviderBuilderExtensions.ConfigureResource(this OpenTelemetry.Metrics.MeterProviderBuilder meterProviderBuilder, System.Action<OpenTelemetry.Resources.ResourceBuilder> configure) -> OpenTelemetry.Metrics.MeterProviderBuilder
~static OpenTelemetry.Trace.TracerProviderBuilderExtensions.ConfigureResource(this OpenTelemetry.Trace.TracerProviderBuilder tracerProviderBuilder, System.Action<OpenTelemetry.Resources.ResourceBuilder> configure) -> OpenTelemetry.Trace.TracerProviderBuilder

View File

@ -94,9 +94,9 @@ readonly OpenTelemetry.BaseExportProcessor<T>.exporter -> OpenTelemetry.BaseExpo
~static OpenTelemetry.Resources.ResourceBuilderExtensions.AddEnvironmentVariableDetector(this OpenTelemetry.Resources.ResourceBuilder resourceBuilder) -> OpenTelemetry.Resources.ResourceBuilder
~static OpenTelemetry.Resources.ResourceBuilderExtensions.AddService(this OpenTelemetry.Resources.ResourceBuilder resourceBuilder, string serviceName, string serviceNamespace = null, string serviceVersion = null, bool autoGenerateServiceInstanceId = true, string serviceInstanceId = null) -> OpenTelemetry.Resources.ResourceBuilder
~static OpenTelemetry.Resources.ResourceBuilderExtensions.AddTelemetrySdk(this OpenTelemetry.Resources.ResourceBuilder resourceBuilder) -> OpenTelemetry.Resources.ResourceBuilder
~static OpenTelemetry.Sdk.CreateMeterProviderBuilder() -> OpenTelemetry.Metrics.MeterProviderBuilder
~static OpenTelemetry.Sdk.CreateTracerProviderBuilder() -> OpenTelemetry.Trace.TracerProviderBuilder
~static OpenTelemetry.Sdk.SetDefaultTextMapPropagator(OpenTelemetry.Context.Propagation.TextMapPropagator textMapPropagator) -> void
static OpenTelemetry.Sdk.CreateMeterProviderBuilder() -> OpenTelemetry.Metrics.MeterProviderBuilder!
static OpenTelemetry.Sdk.CreateTracerProviderBuilder() -> OpenTelemetry.Trace.TracerProviderBuilder!
static OpenTelemetry.Sdk.SetDefaultTextMapPropagator(OpenTelemetry.Context.Propagation.TextMapPropagator! textMapPropagator) -> void
~static OpenTelemetry.SuppressInstrumentationScope.Begin(bool value = true) -> System.IDisposable
~static OpenTelemetry.Trace.TracerProviderBuilderExtensions.AddProcessor(this OpenTelemetry.Trace.TracerProviderBuilder tracerProviderBuilder, OpenTelemetry.BaseProcessor<System.Diagnostics.Activity> processor) -> OpenTelemetry.Trace.TracerProviderBuilder
~static OpenTelemetry.Trace.TracerProviderBuilderExtensions.Build(this OpenTelemetry.Trace.TracerProviderBuilder tracerProviderBuilder) -> OpenTelemetry.Trace.TracerProvider

View File

@ -7,14 +7,23 @@ OpenTelemetry.Logs.LogRecord.Timestamp.set -> void
OpenTelemetry.Logs.LogRecord.TraceFlags.set -> void
OpenTelemetry.Logs.LogRecord.TraceId.set -> void
OpenTelemetry.Logs.LogRecord.TraceState.set -> void
OpenTelemetry.Logs.OpenTelemetryLoggerOptions.AddProcessor<T>() -> OpenTelemetry.Logs.OpenTelemetryLoggerOptions!
OpenTelemetry.Logs.OpenTelemetryLoggerOptions.ConfigureProvider(System.Action<System.IServiceProvider!, OpenTelemetry.Logs.OpenTelemetryLoggerProvider!>! configure) -> OpenTelemetry.Logs.OpenTelemetryLoggerOptions!
OpenTelemetry.Logs.OpenTelemetryLoggerOptions.ConfigureResource(System.Action<OpenTelemetry.Resources.ResourceBuilder!>! configure) -> OpenTelemetry.Logs.OpenTelemetryLoggerOptions!
OpenTelemetry.Logs.OpenTelemetryLoggerOptions.ConfigureServices(System.Action<Microsoft.Extensions.DependencyInjection.IServiceCollection!>! configure) -> OpenTelemetry.Logs.OpenTelemetryLoggerOptions!
OpenTelemetry.Logs.OpenTelemetryLoggerOptions.SetIncludeFormattedMessage(bool enabled) -> OpenTelemetry.Logs.OpenTelemetryLoggerOptions!
OpenTelemetry.Logs.OpenTelemetryLoggerOptions.SetIncludeScopes(bool enabled) -> OpenTelemetry.Logs.OpenTelemetryLoggerOptions!
OpenTelemetry.Logs.OpenTelemetryLoggerOptions.SetParseStateValues(bool enabled) -> OpenTelemetry.Logs.OpenTelemetryLoggerOptions!
OpenTelemetry.Logs.OpenTelemetryLoggerOptionsExtensions
OpenTelemetry.Logs.OpenTelemetryLoggerProvider.AddProcessor(OpenTelemetry.BaseProcessor<OpenTelemetry.Logs.LogRecord!>! processor) -> OpenTelemetry.Logs.OpenTelemetryLoggerProvider!
OpenTelemetry.Logs.OpenTelemetryLoggerProvider.ForceFlush(int timeoutMilliseconds = -1) -> bool
OpenTelemetry.Logs.OpenTelemetryLoggerProvider.OpenTelemetryLoggerProvider() -> void
OpenTelemetry.Logs.OpenTelemetryLoggerProvider.OpenTelemetryLoggerProvider(System.Action<OpenTelemetry.Logs.OpenTelemetryLoggerOptions!>! configure) -> void
*REMOVED*static Microsoft.Extensions.Logging.OpenTelemetryLoggingExtensions.AddOpenTelemetry(this Microsoft.Extensions.Logging.ILoggingBuilder! builder, System.Action<OpenTelemetry.Logs.OpenTelemetryLoggerOptions!>? configure = null) -> Microsoft.Extensions.Logging.ILoggingBuilder!
static Microsoft.Extensions.Logging.OpenTelemetryLoggingExtensions.AddOpenTelemetry(this Microsoft.Extensions.Logging.ILoggingBuilder! builder) -> Microsoft.Extensions.Logging.ILoggingBuilder!
static Microsoft.Extensions.Logging.OpenTelemetryLoggingExtensions.AddOpenTelemetry(this Microsoft.Extensions.Logging.ILoggingBuilder! builder, OpenTelemetry.Logs.OpenTelemetryLoggerProvider! openTelemetryLoggerProvider) -> Microsoft.Extensions.Logging.ILoggingBuilder!
static Microsoft.Extensions.Logging.OpenTelemetryLoggingExtensions.AddOpenTelemetry(this Microsoft.Extensions.Logging.ILoggingBuilder! builder, OpenTelemetry.Logs.OpenTelemetryLoggerProvider! openTelemetryLoggerProvider, bool disposeProvider) -> Microsoft.Extensions.Logging.ILoggingBuilder!
static Microsoft.Extensions.Logging.OpenTelemetryLoggingExtensions.AddOpenTelemetry(this Microsoft.Extensions.Logging.ILoggingBuilder! builder, System.Action<OpenTelemetry.Logs.OpenTelemetryLoggerOptions!>? configure) -> Microsoft.Extensions.Logging.ILoggingBuilder!
static OpenTelemetry.Logs.OpenTelemetryLoggerOptionsExtensions.Build(this OpenTelemetry.Logs.OpenTelemetryLoggerOptions! options) -> OpenTelemetry.Logs.OpenTelemetryLoggerProvider!
static OpenTelemetry.Sdk.CreateLoggerProviderBuilder() -> OpenTelemetry.Logs.OpenTelemetryLoggerOptions!
~static OpenTelemetry.Metrics.MeterProviderBuilderExtensions.ConfigureResource(this OpenTelemetry.Metrics.MeterProviderBuilder meterProviderBuilder, System.Action<OpenTelemetry.Resources.ResourceBuilder> configure) -> OpenTelemetry.Metrics.MeterProviderBuilder
~static OpenTelemetry.Trace.TracerProviderBuilderExtensions.ConfigureResource(this OpenTelemetry.Trace.TracerProviderBuilder tracerProviderBuilder, System.Action<OpenTelemetry.Resources.ResourceBuilder> configure) -> OpenTelemetry.Trace.TracerProviderBuilder

View File

@ -2,6 +2,10 @@
## Unreleased
* Added `Sdk.CreateLoggerProviderBuilder` method and support for dependency
injection scenarios when configuring `OpenTelemetryLoggerProvider`
([#3504](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3504))
## 1.4.0-alpha.1
Released 2022-Aug-02

View File

@ -1,103 +0,0 @@
// <copyright file="OpenTelemetryLoggerOptions.cs" company="OpenTelemetry Authors">
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// </copyright>
#nullable enable
using System;
using System.Collections.Generic;
using OpenTelemetry.Internal;
using OpenTelemetry.Resources;
namespace OpenTelemetry.Logs
{
/// <summary>
/// Contains OpenTelemetry logging options.
/// </summary>
public class OpenTelemetryLoggerOptions
{
internal readonly List<BaseProcessor<LogRecord>> Processors = new();
internal ResourceBuilder ResourceBuilder = ResourceBuilder.CreateDefault();
/// <summary>
/// Gets or sets a value indicating whether or not log scopes should be
/// included on generated <see cref="LogRecord"/>s. Default value:
/// False.
/// </summary>
public bool IncludeScopes { get; set; }
/// <summary>
/// Gets or sets a value indicating whether or not formatted log message
/// should be included on generated <see cref="LogRecord"/>s. Default
/// value: False.
/// </summary>
public bool IncludeFormattedMessage { get; set; }
/// <summary>
/// Gets or sets a value indicating whether or not log state should be
/// parsed into <see cref="LogRecord.StateValues"/> on generated <see
/// cref="LogRecord"/>s. Default value: False.
/// </summary>
/// <remarks>
/// Note: When <see cref="ParseStateValues"/> is set to <see
/// langword="true"/> <see cref="LogRecord.State"/> will always be <see
/// langword="null"/>.
/// </remarks>
public bool ParseStateValues { get; set; }
/// <summary>
/// Adds processor to the options.
/// </summary>
/// <param name="processor">Log processor to add.</param>
/// <returns>Returns <see cref="OpenTelemetryLoggerOptions"/> for chaining.</returns>
public OpenTelemetryLoggerOptions AddProcessor(BaseProcessor<LogRecord> processor)
{
Guard.ThrowIfNull(processor);
this.Processors.Add(processor);
return this;
}
/// <summary>
/// Sets the <see cref="ResourceBuilder"/> from which the Resource associated with
/// this provider is built from. Overwrites currently set ResourceBuilder.
/// You should usually use <see cref="ConfigureResource(Action{ResourceBuilder})"/> instead
/// (call <see cref="ResourceBuilder.Clear"/> if desired).
/// </summary>
/// <param name="resourceBuilder"><see cref="ResourceBuilder"/> from which Resource will be built.</param>
/// <returns>Returns <see cref="OpenTelemetryLoggerOptions"/> for chaining.</returns>
public OpenTelemetryLoggerOptions SetResourceBuilder(ResourceBuilder resourceBuilder)
{
Guard.ThrowIfNull(resourceBuilder);
this.ResourceBuilder = resourceBuilder;
return this;
}
/// <summary>
/// Modify the <see cref="ResourceBuilder"/> from which the Resource associated with
/// this provider is built from in-place.
/// </summary>
/// <param name="configure">An action which modifies the provided <see cref="ResourceBuilder"/> in-place.</param>
/// <returns>Returns <see cref="OpenTelemetryLoggerOptions"/> for chaining.</returns>
public OpenTelemetryLoggerOptions ConfigureResource(Action<ResourceBuilder> configure)
{
Guard.ThrowIfNull(configure, nameof(configure));
configure(this.ResourceBuilder);
return this;
}
}
}

View File

@ -18,7 +18,9 @@
using System;
using System.Collections;
using System.Diagnostics;
using System.Threading;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using OpenTelemetry.Internal;
@ -36,7 +38,9 @@ namespace OpenTelemetry.Logs
internal readonly bool IncludeFormattedMessage;
internal readonly bool ParseStateValues;
internal BaseProcessor<LogRecord>? Processor;
internal ResourceBuilder? ResourceBuilder;
internal Resource Resource;
private readonly ServiceProvider? ownedServiceProvider;
private readonly Hashtable loggers = new();
private ILogRecordPool? threadStaticPool = LogRecordThreadStaticPool.Instance;
private bool disposed;
@ -52,17 +56,9 @@ namespace OpenTelemetry.Logs
/// Initializes a new instance of the <see cref="OpenTelemetryLoggerProvider"/> class.
/// </summary>
/// <param name="options"><see cref="OpenTelemetryLoggerOptions"/>.</param>
[Obsolete("Use the Sdk.CreateLoggerProviderBuilder method instead")]
public OpenTelemetryLoggerProvider(IOptionsMonitor<OpenTelemetryLoggerOptions> options)
: this(options?.CurrentValue ?? throw new ArgumentNullException(nameof(options)))
{
}
/// <summary>
/// Initializes a new instance of the <see cref="OpenTelemetryLoggerProvider"/> class.
/// </summary>
/// <param name="configure"><see cref="OpenTelemetryLoggerOptions"/> configuration callback.</param>
public OpenTelemetryLoggerProvider(Action<OpenTelemetryLoggerOptions> configure)
: this(BuildOptions(configure ?? throw new ArgumentNullException(nameof(configure))))
: this(options?.CurrentValue ?? throw new ArgumentNullException(nameof(options)), serviceProvider: null, ownsServiceProvider: false)
{
}
@ -70,11 +66,17 @@ namespace OpenTelemetry.Logs
/// Initializes a new instance of the <see cref="OpenTelemetryLoggerProvider"/> class.
/// </summary>
public OpenTelemetryLoggerProvider()
: this(BuildOptions(configure: null))
: this(new(), serviceProvider: null, ownsServiceProvider: false)
{
}
// Note: This is only for tests. Options will be missing ServiceCollection & ServiceProvider features will be unavailable.
internal OpenTelemetryLoggerProvider(OpenTelemetryLoggerOptions options)
: this(options, serviceProvider: null, ownsServiceProvider: false)
{
}
internal OpenTelemetryLoggerProvider(OpenTelemetryLoggerOptions options, IServiceProvider? serviceProvider, bool ownsServiceProvider)
{
Guard.ThrowIfNull(options);
@ -82,12 +84,68 @@ namespace OpenTelemetry.Logs
this.IncludeFormattedMessage = options.IncludeFormattedMessage;
this.ParseStateValues = options.ParseStateValues;
this.Resource = options.ResourceBuilder.Build();
if (ownsServiceProvider)
{
this.ownedServiceProvider = serviceProvider as ServiceProvider;
Debug.Assert(this.ownedServiceProvider != null, "ownedServiceProvider was null");
}
// Step 1: Add any processors added to options.
foreach (var processor in options.Processors)
{
this.AddProcessor(processor);
}
this.ResourceBuilder = options.ResourceBuilder ?? ResourceBuilder.CreateDefault();
if (serviceProvider != null)
{
// Step 2: Look for any Action<IServiceProvider,
// OpenTelemetryLoggerProvider> configuration actions registered and
// execute them.
var registeredConfigurations = serviceProvider.GetServices<Action<IServiceProvider, OpenTelemetryLoggerProvider>>();
foreach (var registeredConfiguration in registeredConfigurations)
{
registeredConfiguration?.Invoke(serviceProvider, this);
}
}
var configurationActions = options.ConfigurationActions;
if (configurationActions?.Count > 0)
{
// Step 3: Execute any configuration actions.
if (serviceProvider == null)
{
throw new InvalidOperationException("Configuration actions were registered on options but no service provider was supplied.");
}
// Note: Not using a foreach loop because additional actions can be
// added during each call.
for (int i = 0; i < configurationActions.Count; i++)
{
configurationActions[i](serviceProvider, this);
}
options.ConfigurationActions = null;
}
if (serviceProvider != null)
{
// Step 4: Look for any processors registered directly with the service provider.
var registeredProcessors = serviceProvider.GetServices<BaseProcessor<LogRecord>>();
foreach (BaseProcessor<LogRecord> processor in registeredProcessors)
{
this.AddProcessor(processor);
}
}
this.Resource = this.ResourceBuilder.Build();
this.ResourceBuilder = null;
}
internal IExternalScopeProvider? ScopeProvider { get; private set; }
@ -158,12 +216,16 @@ namespace OpenTelemetry.Logs
}
/// <summary>
/// Create a <see cref="LogEmitter"/>.
/// Add a processor to the <see cref="OpenTelemetryLoggerProvider"/>.
/// </summary>
/// <returns><see cref="LogEmitter"/>.</returns>
internal LogEmitter CreateEmitter() => new(this);
internal OpenTelemetryLoggerProvider AddProcessor(BaseProcessor<LogRecord> processor)
/// <remarks>
/// Note: The supplied <paramref name="processor"/> will be
/// automatically disposed when then the <see
/// cref="OpenTelemetryLoggerProvider"/> is disposed.
/// </remarks>
/// <param name="processor">Log processor to add.</param>
/// <returns>The supplied <see cref="OpenTelemetryLoggerOptions"/> for chaining.</returns>
public OpenTelemetryLoggerProvider AddProcessor(BaseProcessor<LogRecord> processor)
{
Guard.ThrowIfNull(processor);
@ -196,6 +258,12 @@ namespace OpenTelemetry.Logs
return this;
}
/// <summary>
/// Create a <see cref="LogEmitter"/>.
/// </summary>
/// <returns><see cref="LogEmitter"/>.</returns>
internal LogEmitter CreateEmitter() => new(this);
internal bool ContainsBatchProcessor(BaseProcessor<LogRecord> processor)
{
if (processor is BatchExportProcessor<LogRecord>)
@ -229,6 +297,8 @@ namespace OpenTelemetry.Logs
// Wait for up to 5 seconds grace period
this.Processor?.Shutdown(5000);
this.Processor?.Dispose();
this.ownedServiceProvider?.Dispose();
}
this.disposed = true;
@ -237,12 +307,5 @@ namespace OpenTelemetry.Logs
base.Dispose(disposing);
}
private static OpenTelemetryLoggerOptions BuildOptions(Action<OpenTelemetryLoggerOptions>? configure)
{
OpenTelemetryLoggerOptions options = new();
configure?.Invoke(options);
return options;
}
}
}

View File

@ -17,10 +17,12 @@
#nullable enable
using System;
using System.Linq;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Configuration;
using Microsoft.Extensions.Options;
using OpenTelemetry.Internal;
using OpenTelemetry.Logs;
@ -34,6 +36,11 @@ namespace Microsoft.Extensions.Logging
/// <summary>
/// Adds an OpenTelemetry logger named 'OpenTelemetry' to the <see cref="ILoggerFactory"/>.
/// </summary>
/// <remarks>
/// Note: This is safe to be called more than once and should be used by
/// library authors to ensure at least one <see
/// cref="OpenTelemetryLoggerProvider"/> is registered.
/// </remarks>
/// <param name="builder">The <see cref="ILoggingBuilder"/> to use.</param>
/// <returns>The supplied <see cref="ILoggingBuilder"/> for call chaining.</returns>
public static ILoggingBuilder AddOpenTelemetry(this ILoggingBuilder builder)
@ -42,6 +49,11 @@ namespace Microsoft.Extensions.Logging
/// <summary>
/// Adds an OpenTelemetry logger named 'OpenTelemetry' to the <see cref="ILoggerFactory"/>.
/// </summary>
/// <remarks>
/// Note: This is should only be called once during application
/// bootstrap for a given <see cref="IServiceCollection"/>. This should
/// not be used by library authors.
/// </remarks>
/// <param name="builder">The <see cref="ILoggingBuilder"/> to use.</param>
/// <param name="configure">Optional configuration action.</param>
/// <returns>The supplied <see cref="ILoggingBuilder"/> for call chaining.</returns>
@ -50,11 +62,54 @@ namespace Microsoft.Extensions.Logging
Guard.ThrowIfNull(builder);
builder.AddConfiguration();
builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton<ILoggerProvider, OpenTelemetryLoggerProvider>());
builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton<ILoggerProvider, OpenTelemetryLoggerProvider>(sp =>
{
var registeredBuilders = sp.GetServices<TrackedOpenTelemetryLoggerOptions>();
if (registeredBuilders.Count() > 1)
{
throw new NotSupportedException("Multiple logger provider builders cannot be registered in the same service collection.");
}
var finalOptions = sp.GetRequiredService<IOptionsMonitor<OpenTelemetryLoggerOptions>>().CurrentValue;
return new OpenTelemetryLoggerProvider(finalOptions, sp, ownsServiceProvider: false);
}));
// Note: This will bind logger options element (eg "Logging:OpenTelemetry") to OpenTelemetryLoggerOptions
LoggerProviderOptions.RegisterProviderOptions<OpenTelemetryLoggerOptions, OpenTelemetryLoggerProvider>(builder.Services);
if (configure != null)
{
builder.Services.Configure(configure);
/*
* We do a two-phase configuration here.
*
* Step 1: Configure callback is first invoked immediately. This
* is to make "Services" available for extension authors to
* register additional dependencies into the collection if
* needed.
*/
var options = new OpenTelemetryLoggerOptions(builder.Services);
configure(options);
builder.Services.AddSingleton(new TrackedOpenTelemetryLoggerOptions(options));
/*
* Step 2: When ServiceProvider is built from "Services" and the
* LoggerFactory is created then the options pipeline runs and
* builds a new OpenTelemetryLoggerOptions from configuration
* and callbacks are executed. "Services" can no longer be
* modified in this phase because the ServiceProvider is already
* complete. We apply the inline options to the final instance
* to bridge this gap.
*/
builder.Services.Configure<OpenTelemetryLoggerOptions>(finalOptions =>
{
options.ApplyTo(finalOptions);
});
}
return builder;
@ -106,5 +161,15 @@ namespace Microsoft.Extensions.Logging
return builder;
}
private sealed class TrackedOpenTelemetryLoggerOptions
{
public TrackedOpenTelemetryLoggerOptions(OpenTelemetryLoggerOptions options)
{
this.Options = options;
}
public OpenTelemetryLoggerOptions Options { get; }
}
}
}

View File

@ -0,0 +1,334 @@
// <copyright file="OpenTelemetryLoggerOptions.cs" company="OpenTelemetry Authors">
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// </copyright>
#nullable enable
using System;
using System.Collections.Generic;
using System.Diagnostics;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Options;
using OpenTelemetry.Internal;
using OpenTelemetry.Resources;
namespace OpenTelemetry.Logs
{
/// <summary>
/// Contains OpenTelemetry logging options.
/// </summary>
public class OpenTelemetryLoggerOptions
{
internal readonly List<BaseProcessor<LogRecord>> Processors = new();
internal ResourceBuilder? ResourceBuilder;
internal List<Action<IServiceProvider, OpenTelemetryLoggerProvider>>? ConfigurationActions = new();
private const bool DefaultIncludeScopes = false;
private const bool DefaultIncludeFormattedMessage = false;
private const bool DefaultParseStateValues = false;
private IServiceCollection? services;
private bool? includeScopes;
private bool? includeFormattedMessage;
private bool? parseStateValues;
/// <summary>
/// Initializes a new instance of the <see cref="OpenTelemetryLoggerOptions"/> class.
/// </summary>
public OpenTelemetryLoggerOptions()
: this(services: null)
{
}
internal OpenTelemetryLoggerOptions(IServiceCollection? services)
{
this.services = services;
}
/// <summary>
/// Gets or sets a value indicating whether or not log scopes should be
/// included on generated <see cref="LogRecord"/>s. Default value:
/// False.
/// </summary>
public bool IncludeScopes
{
get => this.includeScopes ?? DefaultIncludeScopes;
set => this.includeScopes = value;
}
/// <summary>
/// Gets or sets a value indicating whether or not formatted log message
/// should be included on generated <see cref="LogRecord"/>s. Default
/// value: False.
/// </summary>
public bool IncludeFormattedMessage
{
get => this.includeFormattedMessage ?? DefaultIncludeFormattedMessage;
set => this.includeFormattedMessage = value;
}
/// <summary>
/// Gets or sets a value indicating whether or not log state should be
/// parsed into <see cref="LogRecord.StateValues"/> on generated <see
/// cref="LogRecord"/>s. Default value: False.
/// </summary>
/// <remarks>
/// Note: When <see cref="ParseStateValues"/> is set to <see
/// langword="true"/> <see cref="LogRecord.State"/> will always be <see
/// langword="null"/>.
/// </remarks>
public bool ParseStateValues
{
get => this.parseStateValues ?? DefaultParseStateValues;
set => this.parseStateValues = value;
}
internal IServiceCollection? Services => this.services;
/// <summary>
/// Adds processor to the options.
/// </summary>
/// <remarks>
/// Note: The supplied <paramref name="processor"/> will be
/// automatically disposed when then the final <see
/// cref="OpenTelemetryLoggerProvider"/> built from the options is
/// disposed.
/// </remarks>
/// <param name="processor">Log processor to add.</param>
/// <returns>Returns <see cref="OpenTelemetryLoggerOptions"/> for chaining.</returns>
public OpenTelemetryLoggerOptions AddProcessor(BaseProcessor<LogRecord> processor)
{
Guard.ThrowIfNull(processor);
this.Processors.Add(processor);
return this;
}
/// <summary>
/// Adds a processor to the options which will be retrieved using dependency injection.
/// </summary>
/// <remarks>
/// Note: The type specified by <typeparamref name="T"/> will be
/// registered as a singleton service into application services.
/// </remarks>
/// <typeparam name="T">Processor type.</typeparam>
/// <returns>The supplied <see cref="OpenTelemetryLoggerOptions"/> for chaining.</returns>
public OpenTelemetryLoggerOptions AddProcessor<T>()
where T : BaseProcessor<LogRecord>
{
return this
.ConfigureServices(services => services.TryAddSingleton<BaseProcessor<LogRecord>, T>());
}
/// <summary>
/// Sets the <see cref="Resources.ResourceBuilder"/> from which the Resource associated with
/// this provider is built from. Overwrites currently set ResourceBuilder.
/// You should usually use <see cref="ConfigureResource(Action{ResourceBuilder})"/> instead
/// (call <see cref="ResourceBuilder.Clear"/> if desired).
/// </summary>
/// <param name="resourceBuilder"><see cref="Resources.ResourceBuilder"/> from which Resource will be built.</param>
/// <returns>Returns <see cref="OpenTelemetryLoggerOptions"/> for chaining.</returns>
public OpenTelemetryLoggerOptions SetResourceBuilder(ResourceBuilder resourceBuilder)
{
Guard.ThrowIfNull(resourceBuilder);
this.ResourceBuilder = resourceBuilder;
return this;
}
/// <summary>
/// Modify the <see cref="Resources.ResourceBuilder"/> from which the Resource associated with
/// this provider is built from in-place.
/// </summary>
/// <param name="configure">An action which modifies the provided <see cref="Resources.ResourceBuilder"/> in-place.</param>
/// <returns>Returns <see cref="OpenTelemetryLoggerOptions"/> for chaining.</returns>
public OpenTelemetryLoggerOptions ConfigureResource(
Action<ResourceBuilder> configure)
{
Guard.ThrowIfNull(configure);
this.ConfigureProvider((sp, provider) =>
{
Debug.Assert(provider.ResourceBuilder != null, "provider.ResourceBuilder was null");
configure(provider.ResourceBuilder!);
});
return this;
}
/// <summary>
/// Register a callback action to configure the <see
/// cref="IServiceCollection"/> where logging services are configured.
/// </summary>
/// <remarks>
/// Note: Logging services are only available during the application
/// configuration phase. When using "Options" pattern via <see
/// cref="OptionsServiceCollectionExtensions"/> or interfaces such as
/// <see cref="IConfigureOptions{T}"/> logging services will be
/// unavailable because "Options" are built after application services
/// have been configured.
/// </remarks>
/// <param name="configure">Configuration callback.</param>
/// <returns>The supplied <see cref="OpenTelemetryLoggerOptions"/> for chaining.</returns>
public OpenTelemetryLoggerOptions ConfigureServices(
Action<IServiceCollection> configure)
{
Guard.ThrowIfNull(configure);
var services = this.services;
if (services == null)
{
throw new NotSupportedException("Services cannot be configured outside of application configuration phase.");
}
configure(services);
return this;
}
/// <summary>
/// Register a callback action to configure the <see
/// cref="OpenTelemetryLoggerProvider"/> once the application <see
/// cref="IServiceProvider"/> is available.
/// </summary>
/// <param name="configure">Configuration callback.</param>
/// <returns>The supplied <see cref="OpenTelemetryLoggerOptions"/> for chaining.</returns>
public OpenTelemetryLoggerOptions ConfigureProvider(
Action<IServiceProvider, OpenTelemetryLoggerProvider> configure)
{
Guard.ThrowIfNull(configure);
var configurationActions = this.ConfigurationActions;
if (configurationActions == null)
{
throw new NotSupportedException("Configuration actions cannot be registered on options after OpenTelemetryLoggerProvider has been created.");
}
configurationActions.Add(configure);
return this;
}
/// <summary>
/// Sets the value of the <see cref="IncludeFormattedMessage"/> options.
/// </summary>
/// <param name="enabled"><see langword="true"/> to enable the option or
/// <see langword="false"/> to disable it.</param>
/// <returns>The supplied <see cref="OpenTelemetryLoggerOptions"/> for
/// chaining.</returns>
public OpenTelemetryLoggerOptions SetIncludeFormattedMessage(bool enabled)
{
this.includeFormattedMessage = enabled;
return this;
}
/// <summary>
/// Sets the value of the <see cref="IncludeScopes"/> options.
/// </summary>
/// <param name="enabled"><see langword="true"/> to enable the option or
/// <see langword="false"/> to disable it.</param>
/// <returns>The supplied <see cref="OpenTelemetryLoggerOptions"/> for
/// chaining.</returns>
public OpenTelemetryLoggerOptions SetIncludeScopes(bool enabled)
{
this.includeScopes = enabled;
return this;
}
/// <summary>
/// Sets the value of the <see cref="ParseStateValues"/> options.
/// </summary>
/// <param name="enabled"><see langword="true"/> to enable the option or
/// <see langword="false"/> to disable it.</param>
/// <returns>The supplied <see cref="OpenTelemetryLoggerOptions"/> for
/// chaining.</returns>
public OpenTelemetryLoggerOptions SetParseStateValues(bool enabled)
{
this.parseStateValues = enabled;
return this;
}
internal OpenTelemetryLoggerProvider Build()
{
var services = this.services;
if (services == null)
{
throw new NotSupportedException("LoggerProviderBuilder build method cannot be called multiple times.");
}
this.services = null;
var serviceProvider = services.BuildServiceProvider();
var finalOptions = serviceProvider.GetRequiredService<IOptionsMonitor<OpenTelemetryLoggerOptions>>().CurrentValue;
this.ApplyTo(finalOptions);
var provider = new OpenTelemetryLoggerProvider(
finalOptions,
serviceProvider,
ownsServiceProvider: true);
this.ConfigurationActions = null;
return provider;
}
internal void ApplyTo(OpenTelemetryLoggerOptions other)
{
Debug.Assert(other != null, "other instance was null");
if (this.ResourceBuilder != null)
{
other!.ResourceBuilder = this.ResourceBuilder;
}
if (this.includeFormattedMessage.HasValue)
{
other!.includeFormattedMessage = this.includeFormattedMessage;
}
if (this.includeScopes.HasValue)
{
other!.includeScopes = this.includeScopes;
}
if (this.parseStateValues.HasValue)
{
other!.parseStateValues = this.parseStateValues;
}
Debug.Assert(this.Processors != null && other!.Processors != null, "Processors was null");
foreach (var processor in this.Processors!)
{
other!.Processors!.Add(processor);
}
Debug.Assert(this.ConfigurationActions != null && other!.ConfigurationActions != null, "ConfigurationActions was null");
foreach (var configurationAction in this.ConfigurationActions!)
{
other!.ConfigurationActions!.Add(configurationAction);
}
}
}
}

View File

@ -0,0 +1,45 @@
// <copyright file="OpenTelemetryLoggerOptionsExtensions.cs" company="OpenTelemetry Authors">
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// </copyright>
#nullable enable
using System;
using OpenTelemetry.Internal;
namespace OpenTelemetry.Logs;
/// <summary>
/// Contains extension methods for the <see cref="OpenTelemetryLoggerOptions"/> class.
/// </summary>
public static class OpenTelemetryLoggerOptionsExtensions
{
/// <summary>
/// Run the given actions to initialize the <see cref="OpenTelemetryLoggerProvider"/>.
/// </summary>
/// <param name="options"><see cref="OpenTelemetryLoggerOptions"/>.</param>
/// <returns><see cref="OpenTelemetryLoggerProvider"/>.</returns>
public static OpenTelemetryLoggerProvider Build(this OpenTelemetryLoggerOptions options)
{
Guard.ThrowIfNull(options);
if (options is not OpenTelemetryLoggerOptionsSdk openTelemetryLoggerOptionsSdk)
{
throw new NotSupportedException("Build is only supported on options instances created using the Sdk.CreateLoggerProviderBuilder method.");
}
return openTelemetryLoggerOptionsSdk.Build();
}
}

View File

@ -0,0 +1,30 @@
// <copyright file="OpenTelemetryLoggerOptionsSdk.cs" company="OpenTelemetry Authors">
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// </copyright>
#nullable enable
using Microsoft.Extensions.DependencyInjection;
namespace OpenTelemetry.Logs;
internal sealed class OpenTelemetryLoggerOptionsSdk : OpenTelemetryLoggerOptions
{
public OpenTelemetryLoggerOptionsSdk()
: base(new ServiceCollection())
{
this.ConfigureServices(services => services.AddOptions());
}
}

View File

@ -14,9 +14,12 @@
// limitations under the License.
// </copyright>
#nullable enable
using System.Diagnostics;
using OpenTelemetry.Context.Propagation;
using OpenTelemetry.Internal;
using OpenTelemetry.Logs;
using OpenTelemetry.Metrics;
using OpenTelemetry.Trace;
@ -51,9 +54,24 @@ namespace OpenTelemetry
/// <param name="textMapPropagator">TextMapPropagator to be set as default.</param>
public static void SetDefaultTextMapPropagator(TextMapPropagator textMapPropagator)
{
Guard.ThrowIfNull(textMapPropagator);
Propagators.DefaultTextMapPropagator = textMapPropagator;
}
/// <summary>
/// Creates a <see cref="OpenTelemetryLoggerOptions"/> which is used to build
/// an <see cref="OpenTelemetryLoggerProvider"/>. In a typical application, a single
/// <see cref="OpenTelemetryLoggerProvider"/> is created at application startup and disposed
/// at application shutdown. It is important to ensure that the provider is not
/// disposed too early.
/// </summary>
/// <returns><see cref="OpenTelemetryLoggerOptions"/> instance, which is used to build a <see cref="OpenTelemetryLoggerProvider"/>.</returns>
public static OpenTelemetryLoggerOptions CreateLoggerProviderBuilder()
{
return new OpenTelemetryLoggerOptionsSdk();
}
/// <summary>
/// Creates a <see cref="MeterProviderBuilder"/> which is used to build
/// a <see cref="MeterProvider"/>. In a typical application, a single

View File

@ -32,45 +32,41 @@ namespace OpenTelemetry.Extensions.EventSource.Tests
[InlineData(false)]
public void OpenTelemetryEventSourceLogEmitterDisposesProviderTests(bool dispose)
{
List<LogRecord> exportedItems = new();
#pragma warning disable CA2000 // Dispose objects before losing scope
var openTelemetryLoggerProvider = new WrappedOpenTelemetryLoggerProvider(options =>
{
options.AddInMemoryExporter(exportedItems);
});
var disposeTrackingProcessor = new DisposeTrackingProcessor();
#pragma warning restore CA2000 // Dispose objects before losing scope
using (var openTelemetryEventSourceLogEmitter = new OpenTelemetryEventSourceLogEmitter(
openTelemetryLoggerProvider,
(name) => null,
disposeProvider: dispose))
using (var openTelemetryLoggerProvider = Sdk.CreateLoggerProviderBuilder()
.AddProcessor(disposeTrackingProcessor)
.Build())
{
}
using (var openTelemetryEventSourceLogEmitter = new OpenTelemetryEventSourceLogEmitter(
openTelemetryLoggerProvider,
(name) => null,
disposeProvider: dispose))
{
}
Assert.Equal(dispose, openTelemetryLoggerProvider.Disposed);
Assert.Equal(dispose, disposeTrackingProcessor.Disposed);
if (!dispose)
{
openTelemetryLoggerProvider.Dispose();
}
Assert.True(openTelemetryLoggerProvider.Disposed);
Assert.True(disposeTrackingProcessor.Disposed);
}
[Theory]
[InlineData("OpenTelemetry.Extensions.EventSource.Tests", EventLevel.LogAlways, 2)]
[InlineData("OpenTelemetry.Extensions.EventSource.Tests", EventLevel.Warning, 1)]
[InlineData(TestEventSource.EventSourceName, EventLevel.LogAlways, 2)]
[InlineData(TestEventSource.EventSourceName, EventLevel.Warning, 1)]
[InlineData("_invalid_", EventLevel.LogAlways, 0)]
public void OpenTelemetryEventSourceLogEmitterFilterTests(string sourceName, EventLevel? eventLevel, int expectedNumberOfLogRecords)
{
List<LogRecord> exportedItems = new();
#pragma warning disable CA2000 // Dispose objects before losing scope
var openTelemetryLoggerProvider = new WrappedOpenTelemetryLoggerProvider(options =>
{
options.AddInMemoryExporter(exportedItems);
});
var openTelemetryLoggerProvider = Sdk.CreateLoggerProviderBuilder()
.AddInMemoryExporter(exportedItems)
.Build();
#pragma warning restore CA2000 // Dispose objects before losing scope
using (var openTelemetryEventSourceLogEmitter = new OpenTelemetryEventSourceLogEmitter(
@ -90,17 +86,16 @@ namespace OpenTelemetry.Extensions.EventSource.Tests
List<LogRecord> exportedItems = new();
#pragma warning disable CA2000 // Dispose objects before losing scope
var openTelemetryLoggerProvider = new WrappedOpenTelemetryLoggerProvider(options =>
{
options.AddInMemoryExporter(exportedItems);
});
var openTelemetryLoggerProvider = Sdk.CreateLoggerProviderBuilder()
.AddInMemoryExporter(exportedItems)
.Build();
#pragma warning restore CA2000 // Dispose objects before losing scope
TestEventSource.Log.SimpleEvent();
using (var openTelemetryEventSourceLogEmitter = new OpenTelemetryEventSourceLogEmitter(
openTelemetryLoggerProvider,
(name) => name == "OpenTelemetry.Extensions.EventSource.Tests" ? EventLevel.LogAlways : null))
(name) => name == TestEventSource.EventSourceName ? EventLevel.LogAlways : null))
{
TestEventSource.Log.SimpleEvent();
}
@ -114,15 +109,14 @@ namespace OpenTelemetry.Extensions.EventSource.Tests
List<LogRecord> exportedItems = new();
#pragma warning disable CA2000 // Dispose objects before losing scope
var openTelemetryLoggerProvider = new WrappedOpenTelemetryLoggerProvider(options =>
{
options.AddInMemoryExporter(exportedItems);
});
var openTelemetryLoggerProvider = Sdk.CreateLoggerProviderBuilder()
.AddInMemoryExporter(exportedItems)
.Build();
#pragma warning restore CA2000 // Dispose objects before losing scope
using (var openTelemetryEventSourceLogEmitter = new OpenTelemetryEventSourceLogEmitter(
openTelemetryLoggerProvider,
(name) => name == "OpenTelemetry.Extensions.EventSource.Tests" ? EventLevel.LogAlways : null))
(name) => name == TestEventSource.EventSourceName ? EventLevel.LogAlways : null))
{
TestEventSource.Log.SimpleEvent();
}
@ -145,7 +139,7 @@ namespace OpenTelemetry.Extensions.EventSource.Tests
Assert.Equal(ActivityTraceFlags.None, logRecord.TraceFlags);
Assert.NotNull(logRecord.StateValues);
Assert.Contains(logRecord.StateValues, kvp => kvp.Key == "event_source.name" && (string?)kvp.Value == "OpenTelemetry.Extensions.EventSource.Tests");
Assert.Contains(logRecord.StateValues, kvp => kvp.Key == "event_source.name" && (string?)kvp.Value == TestEventSource.EventSourceName);
}
[Fact]
@ -157,15 +151,14 @@ namespace OpenTelemetry.Extensions.EventSource.Tests
List<LogRecord> exportedItems = new();
#pragma warning disable CA2000 // Dispose objects before losing scope
var openTelemetryLoggerProvider = new WrappedOpenTelemetryLoggerProvider(options =>
{
options.AddInMemoryExporter(exportedItems);
});
var openTelemetryLoggerProvider = Sdk.CreateLoggerProviderBuilder()
.AddInMemoryExporter(exportedItems)
.Build();
#pragma warning restore CA2000 // Dispose objects before losing scope
using (var openTelemetryEventSourceLogEmitter = new OpenTelemetryEventSourceLogEmitter(
openTelemetryLoggerProvider,
(name) => name == "OpenTelemetry.Extensions.EventSource.Tests" ? EventLevel.LogAlways : null))
(name) => name == TestEventSource.EventSourceName ? EventLevel.LogAlways : null))
{
TestEventSource.Log.SimpleEvent();
}
@ -190,16 +183,15 @@ namespace OpenTelemetry.Extensions.EventSource.Tests
List<LogRecord> exportedItems = new();
#pragma warning disable CA2000 // Dispose objects before losing scope
var openTelemetryLoggerProvider = new WrappedOpenTelemetryLoggerProvider(options =>
{
options.IncludeFormattedMessage = formatMessage;
options.AddInMemoryExporter(exportedItems);
});
var openTelemetryLoggerProvider = Sdk.CreateLoggerProviderBuilder()
.SetIncludeFormattedMessage(formatMessage)
.AddInMemoryExporter(exportedItems)
.Build();
#pragma warning restore CA2000 // Dispose objects before losing scope
using (var openTelemetryEventSourceLogEmitter = new OpenTelemetryEventSourceLogEmitter(
openTelemetryLoggerProvider,
(name) => name == "OpenTelemetry.Extensions.EventSource.Tests" ? EventLevel.LogAlways : null))
(name) => name == TestEventSource.EventSourceName ? EventLevel.LogAlways : null))
{
TestEventSource.Log.ComplexEvent("Test_Message", 18);
}
@ -231,7 +223,7 @@ namespace OpenTelemetry.Extensions.EventSource.Tests
Assert.Equal(ActivityTraceFlags.None, logRecord.TraceFlags);
Assert.NotNull(logRecord.StateValues);
Assert.Contains(logRecord.StateValues, kvp => kvp.Key == "event_source.name" && (string?)kvp.Value == "OpenTelemetry.Extensions.EventSource.Tests");
Assert.Contains(logRecord.StateValues, kvp => kvp.Key == "event_source.name" && (string?)kvp.Value == TestEventSource.EventSourceName);
Assert.Contains(logRecord.StateValues, kvp => kvp.Key == "arg1" && (string?)kvp.Value == "Test_Message");
Assert.Contains(logRecord.StateValues, kvp => kvp.Key == "arg2" && (int?)kvp.Value == 18);
}
@ -258,15 +250,14 @@ namespace OpenTelemetry.Extensions.EventSource.Tests
List<LogRecord> exportedItems = new();
#pragma warning disable CA2000 // Dispose objects before losing scope
var openTelemetryLoggerProvider = new WrappedOpenTelemetryLoggerProvider(options =>
{
options.AddInMemoryExporter(exportedItems);
});
var openTelemetryLoggerProvider = Sdk.CreateLoggerProviderBuilder()
.AddInMemoryExporter(exportedItems)
.Build();
#pragma warning restore CA2000 // Dispose objects before losing scope
using (var openTelemetryEventSourceLogEmitter = new OpenTelemetryEventSourceLogEmitter(
openTelemetryLoggerProvider,
(name) => name == "OpenTelemetry.Extensions.EventSource.Tests" ? EventLevel.LogAlways : null))
(name) => name == TestEventSource.EventSourceName ? EventLevel.LogAlways : null))
{
TestEventSource.Log.WorkStart();
@ -293,13 +284,8 @@ namespace OpenTelemetry.Extensions.EventSource.Tests
}
}
private sealed class WrappedOpenTelemetryLoggerProvider : OpenTelemetryLoggerProvider
private sealed class DisposeTrackingProcessor : BaseProcessor<LogRecord>
{
public WrappedOpenTelemetryLoggerProvider(Action<OpenTelemetryLoggerOptions> configure)
: base(configure)
{
}
public bool Disposed { get; private set; }
protected override void Dispose(bool disposing)
@ -310,55 +296,6 @@ namespace OpenTelemetry.Extensions.EventSource.Tests
}
}
[EventSource(Name = "OpenTelemetry.Extensions.EventSource.Tests")]
private sealed class TestEventSource : System.Diagnostics.Tracing.EventSource
{
public const int SimpleEventId = 1;
public const string SimpleEventMessage = "Warning event with no arguments.";
public const int ComplexEventId = 2;
public const string ComplexEventMessage = "Information event with two arguments: '{0}' & '{1}'.";
public const string ComplexEventMessageStructured = "Information event with two arguments: '{arg1}' & '{arg2}'.";
public static TestEventSource Log { get; } = new();
[Event(SimpleEventId, Message = SimpleEventMessage, Level = EventLevel.Warning)]
public void SimpleEvent()
{
this.WriteEvent(SimpleEventId);
}
[Event(ComplexEventId, Message = ComplexEventMessage, Level = EventLevel.Informational)]
public void ComplexEvent(string arg1, int arg2)
{
this.WriteEvent(ComplexEventId, arg1, arg2);
}
[Event(3, Level = EventLevel.Verbose)]
public void WorkStart()
{
this.WriteEvent(3);
}
[Event(4, Level = EventLevel.Verbose)]
public void WorkStop()
{
this.WriteEvent(4);
}
[Event(5, Level = EventLevel.Verbose)]
public void SubworkStart()
{
this.WriteEvent(5);
}
[Event(6, Level = EventLevel.Verbose)]
public void SubworkStop()
{
this.WriteEvent(6);
}
}
private sealed class TplEventSourceListener : EventListener
{
private readonly List<System.Diagnostics.Tracing.EventSource> eventSources = new();

View File

@ -0,0 +1,65 @@
// <copyright file="OpenTelemetryEventSourceLoggerOptionsExtensionsTests.cs" company="OpenTelemetry Authors">
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// </copyright>
#nullable enable
using System.Collections.Generic;
using System.Diagnostics.Tracing;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using OpenTelemetry.Logs;
using Xunit;
namespace OpenTelemetry.Extensions.EventSource.Tests
{
public class OpenTelemetryEventSourceLoggerOptionsExtensionsTests
{
[Fact]
public void AddOpenTelemetryEventSourceLogEmitterTest()
{
var exportedItems = new List<LogRecord>();
var services = new ServiceCollection();
services.AddLogging(configure =>
{
configure.AddOpenTelemetry(options =>
{
options
.AddInMemoryExporter(exportedItems)
.AddEventSourceLogEmitter((name) => name == TestEventSource.EventSourceName ? EventLevel.LogAlways : null);
});
});
OpenTelemetryEventSourceLoggerOptionsExtensions.EventSourceManager? eventSourceManager = null;
using (var serviceProvider = services.BuildServiceProvider())
{
var loggerFactory = serviceProvider.GetRequiredService<ILoggerFactory>();
eventSourceManager = serviceProvider.GetRequiredService<OpenTelemetryEventSourceLoggerOptionsExtensions.EventSourceManager>();
Assert.Single(eventSourceManager.Emitters);
TestEventSource.Log.SimpleEvent();
}
Assert.Single(exportedItems);
Assert.Empty(eventSourceManager.Emitters);
}
}
}

View File

@ -0,0 +1,71 @@
// <copyright file="TestEventSource.cs" company="OpenTelemetry Authors">
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// </copyright>
using System.Diagnostics.Tracing;
namespace OpenTelemetry.Extensions.EventSource.Tests
{
[EventSource(Name = TestEventSource.EventSourceName)]
public sealed class TestEventSource : System.Diagnostics.Tracing.EventSource
{
public const string EventSourceName = "OpenTelemetry.Extensions.EventSource.Tests";
public const int SimpleEventId = 1;
public const string SimpleEventMessage = "Warning event with no arguments.";
public const int ComplexEventId = 2;
public const string ComplexEventMessage = "Information event with two arguments: '{0}' & '{1}'.";
public const string ComplexEventMessageStructured = "Information event with two arguments: '{arg1}' & '{arg2}'.";
public static TestEventSource Log { get; } = new();
[Event(SimpleEventId, Message = SimpleEventMessage, Level = EventLevel.Warning)]
public void SimpleEvent()
{
this.WriteEvent(SimpleEventId);
}
[Event(ComplexEventId, Message = ComplexEventMessage, Level = EventLevel.Informational)]
public void ComplexEvent(string arg1, int arg2)
{
this.WriteEvent(ComplexEventId, arg1, arg2);
}
[Event(3, Level = EventLevel.Verbose)]
public void WorkStart()
{
this.WriteEvent(3);
}
[Event(4, Level = EventLevel.Verbose)]
public void WorkStop()
{
this.WriteEvent(4);
}
[Event(5, Level = EventLevel.Verbose)]
public void SubworkStart()
{
this.WriteEvent(5);
}
[Event(6, Level = EventLevel.Verbose)]
public void SubworkStop()
{
this.WriteEvent(6);
}
}
}

View File

@ -33,29 +33,24 @@ namespace OpenTelemetry.Extensions.Serilog.Tests
[InlineData(false)]
public void SerilogDisposesProviderTests(bool dispose)
{
List<LogRecord> exportedItems = new();
#pragma warning disable CA2000 // Dispose objects before losing scope
var openTelemetryLoggerProvider = new WrappedOpenTelemetryLoggerProvider(options =>
{
options.AddInMemoryExporter(exportedItems);
});
var disposeTrackingProcessor = new DisposeTrackingProcessor();
#pragma warning restore CA2000 // Dispose objects before losing scope
Log.Logger = new LoggerConfiguration()
.WriteTo.OpenTelemetry(openTelemetryLoggerProvider, disposeProvider: dispose)
.CreateLogger();
Log.CloseAndFlush();
Assert.Equal(dispose, openTelemetryLoggerProvider.Disposed);
if (!dispose)
using (var openTelemetryLoggerProvider = Sdk.CreateLoggerProviderBuilder()
.AddProcessor(disposeTrackingProcessor)
.Build())
{
openTelemetryLoggerProvider.Dispose();
Log.Logger = new LoggerConfiguration()
.WriteTo.OpenTelemetry(openTelemetryLoggerProvider, disposeProvider: dispose)
.CreateLogger();
Log.CloseAndFlush();
Assert.Equal(dispose, disposeTrackingProcessor.Disposed);
}
Assert.True(openTelemetryLoggerProvider.Disposed);
Assert.True(disposeTrackingProcessor.Disposed);
}
[Theory]
@ -66,12 +61,10 @@ namespace OpenTelemetry.Extensions.Serilog.Tests
List<LogRecord> exportedItems = new();
#pragma warning disable CA2000 // Dispose objects before losing scope
var openTelemetryLoggerProvider = new OpenTelemetryLoggerProvider(options =>
{
options.IncludeFormattedMessage = includeFormattedMessage;
options.AddInMemoryExporter(exportedItems);
});
var openTelemetryLoggerProvider = Sdk.CreateLoggerProviderBuilder()
.SetIncludeFormattedMessage(includeFormattedMessage)
.AddInMemoryExporter(exportedItems)
.Build();
#pragma warning restore CA2000 // Dispose objects before losing scope
Log.Logger = new LoggerConfiguration()
@ -119,10 +112,9 @@ namespace OpenTelemetry.Extensions.Serilog.Tests
List<LogRecord> exportedItems = new();
#pragma warning disable CA2000 // Dispose objects before losing scope
var openTelemetryLoggerProvider = new OpenTelemetryLoggerProvider(options =>
{
options.AddInMemoryExporter(exportedItems);
});
var openTelemetryLoggerProvider = Sdk.CreateLoggerProviderBuilder()
.AddInMemoryExporter(exportedItems)
.Build();
#pragma warning restore CA2000 // Dispose objects before losing scope
Log.Logger = new LoggerConfiguration()
@ -151,10 +143,9 @@ namespace OpenTelemetry.Extensions.Serilog.Tests
List<LogRecord> exportedItems = new();
#pragma warning disable CA2000 // Dispose objects before losing scope
var openTelemetryLoggerProvider = new OpenTelemetryLoggerProvider(options =>
{
options.AddInMemoryExporter(exportedItems);
});
var openTelemetryLoggerProvider = Sdk.CreateLoggerProviderBuilder()
.AddInMemoryExporter(exportedItems)
.Build();
#pragma warning restore CA2000 // Dispose objects before losing scope
Log.Logger = new LoggerConfiguration()
@ -181,10 +172,9 @@ namespace OpenTelemetry.Extensions.Serilog.Tests
List<LogRecord> exportedItems = new();
#pragma warning disable CA2000 // Dispose objects before losing scope
var openTelemetryLoggerProvider = new OpenTelemetryLoggerProvider(options =>
{
options.AddInMemoryExporter(exportedItems);
});
var openTelemetryLoggerProvider = Sdk.CreateLoggerProviderBuilder()
.AddInMemoryExporter(exportedItems)
.Build();
#pragma warning restore CA2000 // Dispose objects before losing scope
Log.Logger = new LoggerConfiguration()
@ -214,10 +204,9 @@ namespace OpenTelemetry.Extensions.Serilog.Tests
List<LogRecord> exportedItems = new();
#pragma warning disable CA2000 // Dispose objects before losing scope
var openTelemetryLoggerProvider = new OpenTelemetryLoggerProvider(options =>
{
options.AddInMemoryExporter(exportedItems);
});
var openTelemetryLoggerProvider = Sdk.CreateLoggerProviderBuilder()
.AddInMemoryExporter(exportedItems)
.Build();
#pragma warning restore CA2000 // Dispose objects before losing scope
Log.Logger = new LoggerConfiguration()
@ -253,10 +242,9 @@ namespace OpenTelemetry.Extensions.Serilog.Tests
InvalidOperationException ex = new();
#pragma warning disable CA2000 // Dispose objects before losing scope
var openTelemetryLoggerProvider = new OpenTelemetryLoggerProvider(options =>
{
options.AddInMemoryExporter(exportedItems);
});
var openTelemetryLoggerProvider = Sdk.CreateLoggerProviderBuilder()
.AddInMemoryExporter(exportedItems)
.Build();
#pragma warning restore CA2000 // Dispose objects before losing scope
Log.Logger = new LoggerConfiguration()
@ -276,13 +264,8 @@ namespace OpenTelemetry.Extensions.Serilog.Tests
Assert.Equal(ex, logRecord.Exception);
}
private sealed class WrappedOpenTelemetryLoggerProvider : OpenTelemetryLoggerProvider
private sealed class DisposeTrackingProcessor : BaseProcessor<LogRecord>
{
public WrappedOpenTelemetryLoggerProvider(Action<OpenTelemetryLoggerOptions> configure)
: base(configure)
{
}
public bool Disposed { get; private set; }
protected override void Dispose(bool disposing)

View File

@ -29,10 +29,9 @@ namespace OpenTelemetry.Logs.Tests
{
var exportedItems = new List<LogRecord>();
using var provider = new OpenTelemetryLoggerProvider(options =>
{
options.AddInMemoryExporter(exportedItems);
});
using var provider = Sdk.CreateLoggerProviderBuilder()
.AddInMemoryExporter(exportedItems)
.Build();
var logEmitter = provider.CreateEmitter();
@ -82,10 +81,9 @@ namespace OpenTelemetry.Logs.Tests
{
var exportedItems = new List<LogRecord>();
using var provider = new OpenTelemetryLoggerProvider(options =>
{
options.AddInMemoryExporter(exportedItems);
});
using var provider = Sdk.CreateLoggerProviderBuilder()
.AddInMemoryExporter(exportedItems)
.Build();
var logEmitter = provider.CreateEmitter();
@ -117,10 +115,9 @@ namespace OpenTelemetry.Logs.Tests
{
var exportedItems = new List<LogRecord>();
using var provider = new OpenTelemetryLoggerProvider(options =>
{
options.AddInMemoryExporter(exportedItems);
});
using var provider = Sdk.CreateLoggerProviderBuilder()
.AddInMemoryExporter(exportedItems)
.Build();
var logEmitter = provider.CreateEmitter();
@ -148,10 +145,9 @@ namespace OpenTelemetry.Logs.Tests
{
var exportedItems = new List<LogRecord>();
using var provider = new OpenTelemetryLoggerProvider(options =>
{
options.AddInMemoryExporter(exportedItems);
});
using var provider = Sdk.CreateLoggerProviderBuilder()
.AddInMemoryExporter(exportedItems)
.Build();
var logEmitter = provider.CreateEmitter();

View File

@ -0,0 +1,121 @@
// <copyright file="OpenTelemetryLoggerOptionsSdkTests.cs" company="OpenTelemetry Authors">
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// </copyright>
#nullable enable
using System;
using System.Collections.Generic;
using Microsoft.Extensions.DependencyInjection;
using OpenTelemetry.Resources;
using Xunit;
namespace OpenTelemetry.Logs.Tests;
public class OpenTelemetryLoggerOptionsSdkTests
{
[Fact]
public void CreateLoggerProviderBuilderBuildValidProviderTest()
{
List<LogRecord> exportedItems = new();
using var provider = Sdk.CreateLoggerProviderBuilder()
.AddInMemoryExporter(exportedItems)
.Build();
Assert.NotNull(provider);
provider.CreateEmitter().Emit(new()
{
Message = "Hello world",
});
Assert.Single(exportedItems);
}
[Fact]
public void CreateLoggerProviderBuilderExtensionPointsTest()
{
int optionsConfigureInvocations = 0;
OpenTelemetryLoggerProvider? providerFromConfigureCallback = null;
var returnedOptions = Sdk.CreateLoggerProviderBuilder()
.AddProcessor(new CustomProcessor())
.AddProcessor<CustomProcessor>()
.ConfigureServices(services =>
{
services.AddSingleton<TestClass1>();
services.AddSingleton<CustomProcessor>();
services.AddSingleton<BaseProcessor<LogRecord>, CustomProcessor>();
services.Configure<OpenTelemetryLoggerOptions>(o =>
{
optionsConfigureInvocations++;
Assert.Null(o.Services);
Assert.Throws<NotSupportedException>(() => o.ConfigureServices(s => { }));
o.ConfigureResource(r => r.AddAttributes(new Dictionary<string, object> { ["key1"] = "value1" }));
o.ConfigureProvider((sp, p) => optionsConfigureInvocations++);
});
})
.ConfigureProvider((sp, p) =>
{
Assert.NotNull(sp);
providerFromConfigureCallback = p;
Assert.NotNull(sp.GetService<TestClass1>());
});
using var provider = returnedOptions.Build();
Assert.NotNull(provider);
Assert.Throws<NotSupportedException>(() => returnedOptions.ConfigureServices(s => { }));
Assert.Throws<NotSupportedException>(() => returnedOptions.ConfigureResource(r => { }));
Assert.Throws<NotSupportedException>(() => returnedOptions.ConfigureProvider((sp, p) => { }));
Assert.Throws<NotSupportedException>(() => returnedOptions.Build());
Assert.Equal(2, optionsConfigureInvocations);
Assert.NotNull(providerFromConfigureCallback);
Assert.Equal(provider, providerFromConfigureCallback);
Assert.NotNull(provider.Resource?.Attributes);
Assert.Contains(provider.Resource!.Attributes, kvp => kvp.Key == "key1" && (string)kvp.Value == "value1");
var processor = provider.Processor as CompositeProcessor<LogRecord>;
Assert.NotNull(processor);
int count = 0;
var current = processor?.Head;
while (current != null)
{
count++;
current = current.Next;
}
Assert.Equal(3, count);
}
private sealed class TestClass1
{
}
private sealed class CustomProcessor : BaseProcessor<LogRecord>
{
}
}

View File

@ -43,18 +43,15 @@ namespace OpenTelemetry.Logs.Tests
{
OpenTelemetryLoggerOptions defaults = new();
using OpenTelemetryLoggerProvider provider = new(options =>
{
options.IncludeScopes = !defaults.IncludeScopes;
options.IncludeFormattedMessage = !defaults.IncludeFormattedMessage;
options.ParseStateValues = !defaults.ParseStateValues;
options.SetResourceBuilder(ResourceBuilder
using OpenTelemetryLoggerProvider provider = Sdk.CreateLoggerProviderBuilder()
.SetIncludeScopes(!defaults.IncludeScopes)
.SetIncludeFormattedMessage(!defaults.IncludeFormattedMessage)
.SetParseStateValues(!defaults.ParseStateValues)
.SetResourceBuilder(ResourceBuilder
.CreateEmpty()
.AddAttributes(new[] { new KeyValuePair<string, object>("key1", "value1") }));
options.AddInMemoryExporter(new List<LogRecord>());
});
.AddAttributes(new[] { new KeyValuePair<string, object>("key1", "value1") }))
.AddInMemoryExporter(new List<LogRecord>())
.Build();
Assert.Equal(!defaults.IncludeScopes, provider.IncludeScopes);
Assert.Equal(!defaults.IncludeFormattedMessage, provider.IncludeFormattedMessage);

View File

@ -16,8 +16,12 @@
#nullable enable
using System;
using System.Collections.Generic;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using OpenTelemetry.Resources;
using Xunit;
namespace OpenTelemetry.Logs.Tests;
@ -25,7 +29,7 @@ namespace OpenTelemetry.Logs.Tests;
public sealed class OpenTelemetryLoggingExtensionsTests
{
[Fact]
public void ServiceCollectionAddOpenTelemetryNoParametersTest()
public void LoggingBuilderAddOpenTelemetryNoParametersTest()
{
bool optionsCallbackInvoked = false;
@ -51,10 +55,9 @@ public sealed class OpenTelemetryLoggingExtensionsTests
}
[Theory]
[InlineData(1, 0)]
[InlineData(1, 1)]
[InlineData(5, 5)]
public void ServiceCollectionAddOpenTelemetryConfigureActionTests(int numberOfBuilderRegistrations, int numberOfOptionsRegistrations)
[InlineData(1)]
[InlineData(5)]
public void LoggingBuilderAddOpenTelemetryConfigureActionTests(int numberOfOptionsRegistrations)
{
int configureCallbackInvocations = 0;
int optionsCallbackInvocations = 0;
@ -64,10 +67,8 @@ public sealed class OpenTelemetryLoggingExtensionsTests
serviceCollection.AddLogging(configure =>
{
for (int i = 0; i < numberOfBuilderRegistrations; i++)
{
configure.AddOpenTelemetry(ConfigureCallback);
}
configure.AddOpenTelemetry(); // <- Just to verify this doesn't cause a throw.
configure.AddOpenTelemetry(ConfigureCallback);
});
for (int i = 0; i < numberOfOptionsRegistrations; i++)
@ -75,15 +76,17 @@ public sealed class OpenTelemetryLoggingExtensionsTests
serviceCollection.Configure<OpenTelemetryLoggerOptions>(OptionsCallback);
}
Assert.NotNull(optionsInstance);
using ServiceProvider serviceProvider = serviceCollection.BuildServiceProvider();
optionsInstance = null;
ILoggerFactory? loggerFactory = serviceProvider.GetService<ILoggerFactory>();
Assert.NotNull(loggerFactory);
Assert.NotNull(optionsInstance);
Assert.Equal(numberOfBuilderRegistrations, configureCallbackInvocations);
Assert.Equal(1, configureCallbackInvocations);
Assert.Equal(numberOfOptionsRegistrations, optionsCallbackInvocations);
void ConfigureCallback(OpenTelemetryLoggerOptions options)
@ -94,7 +97,8 @@ public sealed class OpenTelemetryLoggingExtensionsTests
}
else
{
Assert.Equal(optionsInstance, options);
// Note: In the callback phase each options instance is unique
Assert.NotEqual(optionsInstance, options);
}
configureCallbackInvocations++;
@ -108,6 +112,7 @@ public sealed class OpenTelemetryLoggingExtensionsTests
}
else
{
// Note: In the options phase each instance is the same
Assert.Equal(optionsInstance, options);
}
@ -116,7 +121,23 @@ public sealed class OpenTelemetryLoggingExtensionsTests
}
[Fact]
public void ServiceCollectionAddOpenTelemetryWithProviderTest()
public void LoggingBuilderAddOpenTelemetryMultipleBuildersThrows()
{
var serviceCollection = new ServiceCollection();
serviceCollection.AddLogging(configure =>
{
configure.AddOpenTelemetry(optiosn => { });
configure.AddOpenTelemetry(optiosn => { });
});
using ServiceProvider serviceProvider = serviceCollection.BuildServiceProvider();
Assert.Throws<NotSupportedException>(() => serviceProvider.GetService<ILoggerFactory>());
}
[Fact]
public void LoggingBuilderAddOpenTelemetryWithProviderTest()
{
var provider = new WrappedOpenTelemetryLoggerProvider();
@ -145,7 +166,7 @@ public sealed class OpenTelemetryLoggingExtensionsTests
[Theory]
[InlineData(true)]
[InlineData(false)]
public void ServiceCollectionAddOpenTelemetryWithProviderAndDisposeSpecifiedTests(bool dispose)
public void LoggingBuilderAddOpenTelemetryWithProviderAndDisposeSpecifiedTests(bool dispose)
{
var provider = new WrappedOpenTelemetryLoggerProvider();
@ -197,6 +218,283 @@ public sealed class OpenTelemetryLoggingExtensionsTests
Assert.True(provider.Disposed);
}
[Fact]
public void LoggingBuilderAddOpenTelemetryServicesAvailableTest()
{
int invocationCount = 0;
var services = new ServiceCollection();
services.AddLogging(configure =>
{
configure.AddOpenTelemetry(options =>
{
invocationCount++;
Assert.NotNull(options.Services);
});
});
services.Configure<OpenTelemetryLoggerOptions>(options =>
{
invocationCount++;
// Note: Services are no longer available once OpenTelemetryLoggerOptions has been created
Assert.Null(options.Services);
});
using var serviceProvider = services.BuildServiceProvider();
var loggerFactory = serviceProvider.GetRequiredService<ILoggerFactory>();
Assert.Equal(2, invocationCount);
}
[Fact]
public void LoggingBuilderAddOpenTelemetryProcessorThroughDependencyTest()
{
CustomProcessor.InstanceCount = 0;
var services = new ServiceCollection();
services.AddLogging(configure =>
{
configure.AddOpenTelemetry(options =>
{
options.AddProcessor<CustomProcessor>();
});
});
CustomProcessor? customProcessor = null;
using (var serviceProvider = services.BuildServiceProvider())
{
var loggerFactory = serviceProvider.GetRequiredService<ILoggerFactory>();
customProcessor = serviceProvider.GetRequiredService<BaseProcessor<LogRecord>>() as CustomProcessor;
Assert.NotNull(customProcessor);
loggerFactory.Dispose();
Assert.False(customProcessor!.Disposed);
}
Assert.True(customProcessor.Disposed);
Assert.Equal(1, CustomProcessor.InstanceCount);
}
[Fact]
public void LoggingBuilderAddOpenTelemetryConfigureCallbackTest()
{
var services = new ServiceCollection();
services.AddSingleton<TestClass>();
CustomProcessor? customProcessor = null;
services.AddLogging(configure =>
{
configure.AddOpenTelemetry(options =>
{
options.ConfigureProvider((sp, provider) =>
{
var testClass = sp.GetRequiredService<TestClass>();
customProcessor = new CustomProcessor
{
TestClass = testClass,
};
provider.AddProcessor(customProcessor);
});
});
});
using var serviceProvider = services.BuildServiceProvider();
var loggerFactory = serviceProvider.GetRequiredService<ILoggerFactory>();
Assert.NotNull(customProcessor?.TestClass);
}
[Fact]
public void LoggingBuilderAddOpenTelemetryExternalRegistrationTest()
{
CustomProcessor.InstanceCount = 0;
var services = new ServiceCollection();
services.AddSingleton<BaseProcessor<LogRecord>>(sp => new CustomProcessor());
services.AddSingleton<BaseProcessor<LogRecord>>(sp => new CustomProcessor());
services.AddLogging(configure =>
{
configure.AddOpenTelemetry();
});
using var serviceProvider = services.BuildServiceProvider();
var loggerFactory = serviceProvider.GetRequiredService<ILoggerFactory>();
Assert.Equal(2, CustomProcessor.InstanceCount);
}
[Fact]
public void LoggingBuilderAddOpenTelemetryOptionsOrderingTest()
{
int configureInvocationCount = 0;
var services = new ServiceCollection();
OpenTelemetryLoggerProvider? provider = null;
services.Configure<OpenTelemetryLoggerOptions>(options =>
{
// Note: This will be applied first to the final options
options.IncludeFormattedMessage = true;
options.IncludeScopes = true;
options.ParseStateValues = true;
options.AddProcessor(new CustomProcessor(0));
options.ConfigureProvider((sp, p) =>
{
Assert.Null(provider);
provider = p;
configureInvocationCount++;
});
});
services.AddLogging(configure =>
{
configure.AddOpenTelemetry(options =>
{
// Note: This will run first, but be applied second to the final options
options.IncludeFormattedMessage = false;
options.ParseStateValues = false;
options.AddProcessor(new CustomProcessor(1));
options.ConfigureProvider((sp, p) =>
{
configureInvocationCount++;
Assert.NotNull(provider);
Assert.Equal(provider, p);
});
});
});
services.Configure<OpenTelemetryLoggerOptions>(options =>
{
// Note: This will be applied last to the final options
options.ParseStateValues = true;
options.AddProcessor(new CustomProcessor(2));
options.ConfigureProvider((sp, p) =>
{
configureInvocationCount++;
Assert.NotNull(provider);
Assert.Equal(provider, p);
});
});
using var serviceProvider = services.BuildServiceProvider();
var loggerFactory = serviceProvider.GetRequiredService<ILoggerFactory>();
Assert.NotNull(provider);
Assert.Equal(3, configureInvocationCount);
var finalOptions = serviceProvider.GetRequiredService<IOptionsMonitor<OpenTelemetryLoggerOptions>>().CurrentValue;
Assert.False(finalOptions.IncludeFormattedMessage);
Assert.True(finalOptions.IncludeScopes);
Assert.True(finalOptions.ParseStateValues);
var processor = provider!.Processor as CompositeProcessor<LogRecord>;
Assert.NotNull(processor);
int count = 0;
var current = processor!.Head;
while (current != null)
{
var instance = current.Value as CustomProcessor;
Assert.Equal(count, instance?.Id);
count++;
current = current.Next;
}
Assert.Equal(3, count);
}
[Fact]
public void LoggingBuilderAddOpenTelemetryResourceTest()
{
var services = new ServiceCollection();
OpenTelemetryLoggerProvider? provider = null;
services.AddLogging(configure =>
{
configure.AddOpenTelemetry(options =>
{
options.SetResourceBuilder(ResourceBuilder.CreateDefault().AddService("Examples.LoggingExtensions"));
options.ConfigureProvider((sp, p) => provider = p);
});
});
services.Configure<OpenTelemetryLoggerOptions>(options =>
{
options.ConfigureResource(builder => builder.AddAttributes(new Dictionary<string, object> { ["key1"] = "value1" }));
});
services.Configure<OpenTelemetryLoggerOptions>(options =>
{
options.ConfigureResource(builder => builder.AddAttributes(new Dictionary<string, object> { ["key2"] = "value2" }));
});
using var serviceProvider = services.BuildServiceProvider();
var loggerFactory = serviceProvider.GetRequiredService<ILoggerFactory>();
Assert.NotNull(provider);
var resource = provider!.Resource;
Assert.NotNull(resource);
Assert.Contains(resource.Attributes, kvp => kvp.Key == "service.name");
Assert.Contains(resource.Attributes, kvp => kvp.Key == "service.instance.id");
Assert.Contains(resource.Attributes, kvp => kvp.Key == "key1");
Assert.Contains(resource.Attributes, kvp => kvp.Key == "key2");
}
[Fact]
public void LoggingBuilderAddOpenTelemetryDetachedConfigurationTest()
{
int configurationInvocations = 0;
var services = new ServiceCollection();
services.AddLogging(configure => configure.AddOpenTelemetry());
services.AddSingleton<Action<IServiceProvider, OpenTelemetryLoggerProvider>>((sp, provider) => configurationInvocations++);
using var serviceProvider = services.BuildServiceProvider();
var loggerFactory = serviceProvider.GetRequiredService<ILoggerFactory>();
Assert.Equal(1, configurationInvocations);
}
private sealed class WrappedOpenTelemetryLoggerProvider : OpenTelemetryLoggerProvider
{
public bool Disposed { get; private set; }
@ -208,4 +506,32 @@ public sealed class OpenTelemetryLoggingExtensionsTests
base.Dispose(disposing);
}
}
private sealed class CustomProcessor : BaseProcessor<LogRecord>
{
public CustomProcessor(int? id = null)
{
this.Id = id;
InstanceCount++;
}
public static int InstanceCount { get; set; }
public int? Id { get; }
public bool Disposed { get; private set; }
public TestClass? TestClass { get; set; }
protected override void Dispose(bool disposing)
{
this.Disposed = true;
base.Dispose(disposing);
}
}
private sealed class TestClass
{
}
}