diff --git a/examples/LoggingExtensions/Program.cs b/examples/LoggingExtensions/Program.cs index 24ec26eadbf..795398f2966 100644 --- a/examples/LoggingExtensions/Program.cs +++ b/examples/LoggingExtensions/Program.cs @@ -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 diff --git a/src/OpenTelemetry.Extensions.EventSource/.publicApi/netstandard2.0/PublicAPI.Unshipped.txt b/src/OpenTelemetry.Extensions.EventSource/.publicApi/netstandard2.0/PublicAPI.Unshipped.txt index 32c7f36aea5..bd136d125a8 100644 --- a/src/OpenTelemetry.Extensions.EventSource/.publicApi/netstandard2.0/PublicAPI.Unshipped.txt +++ b/src/OpenTelemetry.Extensions.EventSource/.publicApi/netstandard2.0/PublicAPI.Unshipped.txt @@ -1,4 +1,6 @@ #nullable enable OpenTelemetry.Logs.OpenTelemetryEventSourceLogEmitter OpenTelemetry.Logs.OpenTelemetryEventSourceLogEmitter.OpenTelemetryEventSourceLogEmitter(OpenTelemetry.Logs.OpenTelemetryLoggerProvider! openTelemetryLoggerProvider, System.Func! 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! shouldListenToFunc) -> OpenTelemetry.Logs.OpenTelemetryLoggerOptions! diff --git a/src/OpenTelemetry.Extensions.EventSource/.publicApi/netstandard2.1/PublicAPI.Unshipped.txt b/src/OpenTelemetry.Extensions.EventSource/.publicApi/netstandard2.1/PublicAPI.Unshipped.txt index 32c7f36aea5..bd136d125a8 100644 --- a/src/OpenTelemetry.Extensions.EventSource/.publicApi/netstandard2.1/PublicAPI.Unshipped.txt +++ b/src/OpenTelemetry.Extensions.EventSource/.publicApi/netstandard2.1/PublicAPI.Unshipped.txt @@ -1,4 +1,6 @@ #nullable enable OpenTelemetry.Logs.OpenTelemetryEventSourceLogEmitter OpenTelemetry.Logs.OpenTelemetryEventSourceLogEmitter.OpenTelemetryEventSourceLogEmitter(OpenTelemetry.Logs.OpenTelemetryLoggerProvider! openTelemetryLoggerProvider, System.Func! 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! shouldListenToFunc) -> OpenTelemetry.Logs.OpenTelemetryLoggerOptions! diff --git a/src/OpenTelemetry.Extensions.EventSource/AssemblyInfo.cs b/src/OpenTelemetry.Extensions.EventSource/AssemblyInfo.cs index a51a83d9d1d..1aa11097133 100644 --- a/src/OpenTelemetry.Extensions.EventSource/AssemblyInfo.cs +++ b/src/OpenTelemetry.Extensions.EventSource/AssemblyInfo.cs @@ -18,6 +18,7 @@ using System.Runtime.CompilerServices; [assembly: CLSCompliant(false)] +[assembly: InternalsVisibleTo("OpenTelemetry.Extensions.EventSource.Tests" + AssemblyInfo.PublicKey)] [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2" + AssemblyInfo.MoqPublicKey)] #if SIGNED diff --git a/src/OpenTelemetry.Extensions.EventSource/OpenTelemetryEventSourceLoggerOptionsExtensions.cs b/src/OpenTelemetry.Extensions.EventSource/OpenTelemetryEventSourceLoggerOptionsExtensions.cs new file mode 100644 index 00000000000..582e65a5c1e --- /dev/null +++ b/src/OpenTelemetry.Extensions.EventSource/OpenTelemetryEventSourceLoggerOptionsExtensions.cs @@ -0,0 +1,78 @@ +// +// 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. +// + +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 +{ + /// + /// Contains extension methods for registering OpenTelemetry EventSource utilities into logging services. + /// + public static class OpenTelemetryEventSourceLoggerOptionsExtensions + { + /// + /// Registers an which will convert events into OpenTelemetry logs. + /// + /// . + /// Callback function used to decide if + /// events should be captured for a given . Return if no + /// events should be captured. + /// Supplied for + /// chaining calls. + public static OpenTelemetryLoggerOptions AddEventSourceLogEmitter( + this OpenTelemetryLoggerOptions options, + Func shouldListenToFunc) + { + Guard.ThrowIfNull(options); + Guard.ThrowIfNull(shouldListenToFunc); + + options.ConfigureServices(services => services.TryAddSingleton()); + + options.ConfigureProvider((sp, provider) => + { + var manager = sp.GetRequiredService(); + + manager.Emitters.Add( + new OpenTelemetryEventSourceLogEmitter(provider, shouldListenToFunc, disposeProvider: false)); + }); + + return options; + } + + internal sealed class EventSourceManager : IDisposable + { + public List Emitters { get; } = new(); + + public void Dispose() + { + foreach (var emitter in this.Emitters) + { + emitter.Dispose(); + } + + this.Emitters.Clear(); + } + } + } +} diff --git a/src/OpenTelemetry.Extensions.EventSource/README.md b/src/OpenTelemetry.Extensions.EventSource/README.md index 5259aae3425..b88f146a0eb 100644 --- a/src/OpenTelemetry.Extensions.EventSource/README.md +++ b/src/OpenTelemetry.Extensions.EventSource/README.md @@ -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); ``` diff --git a/src/OpenTelemetry.Extensions.Serilog/README.md b/src/OpenTelemetry.Extensions.Serilog/README.md index 39372b4e142..2d0c837bfbb 100644 --- a/src/OpenTelemetry.Extensions.Serilog/README.md +++ b/src/OpenTelemetry.Extensions.Serilog/README.md @@ -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() diff --git a/src/OpenTelemetry/.publicApi/net462/PublicAPI.Shipped.txt b/src/OpenTelemetry/.publicApi/net462/PublicAPI.Shipped.txt index afa4f5a68a7..352877fe2fe 100644 --- a/src/OpenTelemetry/.publicApi/net462/PublicAPI.Shipped.txt +++ b/src/OpenTelemetry/.publicApi/net462/PublicAPI.Shipped.txt @@ -94,9 +94,9 @@ readonly OpenTelemetry.BaseExportProcessor.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 processor) -> OpenTelemetry.Trace.TracerProviderBuilder ~static OpenTelemetry.Trace.TracerProviderBuilderExtensions.Build(this OpenTelemetry.Trace.TracerProviderBuilder tracerProviderBuilder) -> OpenTelemetry.Trace.TracerProvider diff --git a/src/OpenTelemetry/.publicApi/net462/PublicAPI.Unshipped.txt b/src/OpenTelemetry/.publicApi/net462/PublicAPI.Unshipped.txt index 791e1c4f84a..5f9d0a1ac8c 100644 --- a/src/OpenTelemetry/.publicApi/net462/PublicAPI.Unshipped.txt +++ b/src/OpenTelemetry/.publicApi/net462/PublicAPI.Unshipped.txt @@ -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() -> OpenTelemetry.Logs.OpenTelemetryLoggerOptions! +OpenTelemetry.Logs.OpenTelemetryLoggerOptions.ConfigureProvider(System.Action! configure) -> OpenTelemetry.Logs.OpenTelemetryLoggerOptions! +OpenTelemetry.Logs.OpenTelemetryLoggerOptions.ConfigureResource(System.Action! configure) -> OpenTelemetry.Logs.OpenTelemetryLoggerOptions! +OpenTelemetry.Logs.OpenTelemetryLoggerOptions.ConfigureServices(System.Action! 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! processor) -> OpenTelemetry.Logs.OpenTelemetryLoggerProvider! OpenTelemetry.Logs.OpenTelemetryLoggerProvider.ForceFlush(int timeoutMilliseconds = -1) -> bool OpenTelemetry.Logs.OpenTelemetryLoggerProvider.OpenTelemetryLoggerProvider() -> void -OpenTelemetry.Logs.OpenTelemetryLoggerProvider.OpenTelemetryLoggerProvider(System.Action! configure) -> void -OpenTelemetry.Logs.OpenTelemetryLoggerOptions.ConfigureResource(System.Action! configure) -> OpenTelemetry.Logs.OpenTelemetryLoggerOptions! *REMOVED*static Microsoft.Extensions.Logging.OpenTelemetryLoggingExtensions.AddOpenTelemetry(this Microsoft.Extensions.Logging.ILoggingBuilder! builder, System.Action? 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? 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 configure) -> OpenTelemetry.Metrics.MeterProviderBuilder ~static OpenTelemetry.Trace.TracerProviderBuilderExtensions.ConfigureResource(this OpenTelemetry.Trace.TracerProviderBuilder tracerProviderBuilder, System.Action configure) -> OpenTelemetry.Trace.TracerProviderBuilder diff --git a/src/OpenTelemetry/.publicApi/net6.0/PublicAPI.Shipped.txt b/src/OpenTelemetry/.publicApi/net6.0/PublicAPI.Shipped.txt index afa4f5a68a7..352877fe2fe 100644 --- a/src/OpenTelemetry/.publicApi/net6.0/PublicAPI.Shipped.txt +++ b/src/OpenTelemetry/.publicApi/net6.0/PublicAPI.Shipped.txt @@ -94,9 +94,9 @@ readonly OpenTelemetry.BaseExportProcessor.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 processor) -> OpenTelemetry.Trace.TracerProviderBuilder ~static OpenTelemetry.Trace.TracerProviderBuilderExtensions.Build(this OpenTelemetry.Trace.TracerProviderBuilder tracerProviderBuilder) -> OpenTelemetry.Trace.TracerProvider diff --git a/src/OpenTelemetry/.publicApi/net6.0/PublicAPI.Unshipped.txt b/src/OpenTelemetry/.publicApi/net6.0/PublicAPI.Unshipped.txt index a045a0ffba5..5f9d0a1ac8c 100644 --- a/src/OpenTelemetry/.publicApi/net6.0/PublicAPI.Unshipped.txt +++ b/src/OpenTelemetry/.publicApi/net6.0/PublicAPI.Unshipped.txt @@ -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() -> OpenTelemetry.Logs.OpenTelemetryLoggerOptions! +OpenTelemetry.Logs.OpenTelemetryLoggerOptions.ConfigureProvider(System.Action! configure) -> OpenTelemetry.Logs.OpenTelemetryLoggerOptions! OpenTelemetry.Logs.OpenTelemetryLoggerOptions.ConfigureResource(System.Action! configure) -> OpenTelemetry.Logs.OpenTelemetryLoggerOptions! +OpenTelemetry.Logs.OpenTelemetryLoggerOptions.ConfigureServices(System.Action! 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! processor) -> OpenTelemetry.Logs.OpenTelemetryLoggerProvider! OpenTelemetry.Logs.OpenTelemetryLoggerProvider.ForceFlush(int timeoutMilliseconds = -1) -> bool OpenTelemetry.Logs.OpenTelemetryLoggerProvider.OpenTelemetryLoggerProvider() -> void -OpenTelemetry.Logs.OpenTelemetryLoggerProvider.OpenTelemetryLoggerProvider(System.Action! configure) -> void *REMOVED*static Microsoft.Extensions.Logging.OpenTelemetryLoggingExtensions.AddOpenTelemetry(this Microsoft.Extensions.Logging.ILoggingBuilder! builder, System.Action? 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? 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 configure) -> OpenTelemetry.Metrics.MeterProviderBuilder ~static OpenTelemetry.Trace.TracerProviderBuilderExtensions.ConfigureResource(this OpenTelemetry.Trace.TracerProviderBuilder tracerProviderBuilder, System.Action configure) -> OpenTelemetry.Trace.TracerProviderBuilder diff --git a/src/OpenTelemetry/.publicApi/netstandard2.0/PublicAPI.Shipped.txt b/src/OpenTelemetry/.publicApi/netstandard2.0/PublicAPI.Shipped.txt index afa4f5a68a7..352877fe2fe 100644 --- a/src/OpenTelemetry/.publicApi/netstandard2.0/PublicAPI.Shipped.txt +++ b/src/OpenTelemetry/.publicApi/netstandard2.0/PublicAPI.Shipped.txt @@ -94,9 +94,9 @@ readonly OpenTelemetry.BaseExportProcessor.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 processor) -> OpenTelemetry.Trace.TracerProviderBuilder ~static OpenTelemetry.Trace.TracerProviderBuilderExtensions.Build(this OpenTelemetry.Trace.TracerProviderBuilder tracerProviderBuilder) -> OpenTelemetry.Trace.TracerProvider diff --git a/src/OpenTelemetry/.publicApi/netstandard2.0/PublicAPI.Unshipped.txt b/src/OpenTelemetry/.publicApi/netstandard2.0/PublicAPI.Unshipped.txt index 791e1c4f84a..a840723ef46 100644 --- a/src/OpenTelemetry/.publicApi/netstandard2.0/PublicAPI.Unshipped.txt +++ b/src/OpenTelemetry/.publicApi/netstandard2.0/PublicAPI.Unshipped.txt @@ -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() -> OpenTelemetry.Logs.OpenTelemetryLoggerOptions! +OpenTelemetry.Logs.OpenTelemetryLoggerOptions.ConfigureProvider(System.Action! configure) -> OpenTelemetry.Logs.OpenTelemetryLoggerOptions! +OpenTelemetry.Logs.OpenTelemetryLoggerOptions.ConfigureServices(System.Action! 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! processor) -> OpenTelemetry.Logs.OpenTelemetryLoggerProvider! OpenTelemetry.Logs.OpenTelemetryLoggerProvider.ForceFlush(int timeoutMilliseconds = -1) -> bool OpenTelemetry.Logs.OpenTelemetryLoggerProvider.OpenTelemetryLoggerProvider() -> void -OpenTelemetry.Logs.OpenTelemetryLoggerProvider.OpenTelemetryLoggerProvider(System.Action! configure) -> void OpenTelemetry.Logs.OpenTelemetryLoggerOptions.ConfigureResource(System.Action! configure) -> OpenTelemetry.Logs.OpenTelemetryLoggerOptions! *REMOVED*static Microsoft.Extensions.Logging.OpenTelemetryLoggingExtensions.AddOpenTelemetry(this Microsoft.Extensions.Logging.ILoggingBuilder! builder, System.Action? 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? 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 configure) -> OpenTelemetry.Metrics.MeterProviderBuilder ~static OpenTelemetry.Trace.TracerProviderBuilderExtensions.ConfigureResource(this OpenTelemetry.Trace.TracerProviderBuilder tracerProviderBuilder, System.Action configure) -> OpenTelemetry.Trace.TracerProviderBuilder diff --git a/src/OpenTelemetry/.publicApi/netstandard2.1/PublicAPI.Shipped.txt b/src/OpenTelemetry/.publicApi/netstandard2.1/PublicAPI.Shipped.txt index afa4f5a68a7..352877fe2fe 100644 --- a/src/OpenTelemetry/.publicApi/netstandard2.1/PublicAPI.Shipped.txt +++ b/src/OpenTelemetry/.publicApi/netstandard2.1/PublicAPI.Shipped.txt @@ -94,9 +94,9 @@ readonly OpenTelemetry.BaseExportProcessor.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 processor) -> OpenTelemetry.Trace.TracerProviderBuilder ~static OpenTelemetry.Trace.TracerProviderBuilderExtensions.Build(this OpenTelemetry.Trace.TracerProviderBuilder tracerProviderBuilder) -> OpenTelemetry.Trace.TracerProvider diff --git a/src/OpenTelemetry/.publicApi/netstandard2.1/PublicAPI.Unshipped.txt b/src/OpenTelemetry/.publicApi/netstandard2.1/PublicAPI.Unshipped.txt index a045a0ffba5..5f9d0a1ac8c 100644 --- a/src/OpenTelemetry/.publicApi/netstandard2.1/PublicAPI.Unshipped.txt +++ b/src/OpenTelemetry/.publicApi/netstandard2.1/PublicAPI.Unshipped.txt @@ -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() -> OpenTelemetry.Logs.OpenTelemetryLoggerOptions! +OpenTelemetry.Logs.OpenTelemetryLoggerOptions.ConfigureProvider(System.Action! configure) -> OpenTelemetry.Logs.OpenTelemetryLoggerOptions! OpenTelemetry.Logs.OpenTelemetryLoggerOptions.ConfigureResource(System.Action! configure) -> OpenTelemetry.Logs.OpenTelemetryLoggerOptions! +OpenTelemetry.Logs.OpenTelemetryLoggerOptions.ConfigureServices(System.Action! 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! processor) -> OpenTelemetry.Logs.OpenTelemetryLoggerProvider! OpenTelemetry.Logs.OpenTelemetryLoggerProvider.ForceFlush(int timeoutMilliseconds = -1) -> bool OpenTelemetry.Logs.OpenTelemetryLoggerProvider.OpenTelemetryLoggerProvider() -> void -OpenTelemetry.Logs.OpenTelemetryLoggerProvider.OpenTelemetryLoggerProvider(System.Action! configure) -> void *REMOVED*static Microsoft.Extensions.Logging.OpenTelemetryLoggingExtensions.AddOpenTelemetry(this Microsoft.Extensions.Logging.ILoggingBuilder! builder, System.Action? 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? 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 configure) -> OpenTelemetry.Metrics.MeterProviderBuilder ~static OpenTelemetry.Trace.TracerProviderBuilderExtensions.ConfigureResource(this OpenTelemetry.Trace.TracerProviderBuilder tracerProviderBuilder, System.Action configure) -> OpenTelemetry.Trace.TracerProviderBuilder diff --git a/src/OpenTelemetry/CHANGELOG.md b/src/OpenTelemetry/CHANGELOG.md index 0d17ce51aac..af068bfd454 100644 --- a/src/OpenTelemetry/CHANGELOG.md +++ b/src/OpenTelemetry/CHANGELOG.md @@ -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 diff --git a/src/OpenTelemetry/Logs/OpenTelemetryLoggerOptions.cs b/src/OpenTelemetry/Logs/OpenTelemetryLoggerOptions.cs deleted file mode 100644 index d20fcce2b47..00000000000 --- a/src/OpenTelemetry/Logs/OpenTelemetryLoggerOptions.cs +++ /dev/null @@ -1,103 +0,0 @@ -// -// 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. -// - -#nullable enable - -using System; -using System.Collections.Generic; -using OpenTelemetry.Internal; -using OpenTelemetry.Resources; - -namespace OpenTelemetry.Logs -{ - /// - /// Contains OpenTelemetry logging options. - /// - public class OpenTelemetryLoggerOptions - { - internal readonly List> Processors = new(); - internal ResourceBuilder ResourceBuilder = ResourceBuilder.CreateDefault(); - - /// - /// Gets or sets a value indicating whether or not log scopes should be - /// included on generated s. Default value: - /// False. - /// - public bool IncludeScopes { get; set; } - - /// - /// Gets or sets a value indicating whether or not formatted log message - /// should be included on generated s. Default - /// value: False. - /// - public bool IncludeFormattedMessage { get; set; } - - /// - /// Gets or sets a value indicating whether or not log state should be - /// parsed into on generated s. Default value: False. - /// - /// - /// Note: When is set to will always be . - /// - public bool ParseStateValues { get; set; } - - /// - /// Adds processor to the options. - /// - /// Log processor to add. - /// Returns for chaining. - public OpenTelemetryLoggerOptions AddProcessor(BaseProcessor processor) - { - Guard.ThrowIfNull(processor); - - this.Processors.Add(processor); - - return this; - } - - /// - /// Sets the from which the Resource associated with - /// this provider is built from. Overwrites currently set ResourceBuilder. - /// You should usually use instead - /// (call if desired). - /// - /// from which Resource will be built. - /// Returns for chaining. - public OpenTelemetryLoggerOptions SetResourceBuilder(ResourceBuilder resourceBuilder) - { - Guard.ThrowIfNull(resourceBuilder); - - this.ResourceBuilder = resourceBuilder; - return this; - } - - /// - /// Modify the from which the Resource associated with - /// this provider is built from in-place. - /// - /// An action which modifies the provided in-place. - /// Returns for chaining. - public OpenTelemetryLoggerOptions ConfigureResource(Action configure) - { - Guard.ThrowIfNull(configure, nameof(configure)); - configure(this.ResourceBuilder); - return this; - } - } -} diff --git a/src/OpenTelemetry/Logs/OpenTelemetryLoggerProvider.cs b/src/OpenTelemetry/Logs/OpenTelemetryLoggerProvider.cs index d94769b7f59..7b438b264ae 100644 --- a/src/OpenTelemetry/Logs/OpenTelemetryLoggerProvider.cs +++ b/src/OpenTelemetry/Logs/OpenTelemetryLoggerProvider.cs @@ -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 @@ public class OpenTelemetryLoggerProvider : BaseProvider, ILoggerProvider, ISuppo internal readonly bool IncludeFormattedMessage; internal readonly bool ParseStateValues; internal BaseProcessor? 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,29 +56,27 @@ static OpenTelemetryLoggerProvider() /// Initializes a new instance of the class. /// /// . + [Obsolete("Use the Sdk.CreateLoggerProviderBuilder method instead")] public OpenTelemetryLoggerProvider(IOptionsMonitor options) - : this(options?.CurrentValue ?? throw new ArgumentNullException(nameof(options))) + : this(options?.CurrentValue ?? throw new ArgumentNullException(nameof(options)), serviceProvider: null, ownsServiceProvider: false) { } /// /// Initializes a new instance of the class. /// - /// configuration callback. - public OpenTelemetryLoggerProvider(Action configure) - : this(BuildOptions(configure ?? throw new ArgumentNullException(nameof(configure)))) + public OpenTelemetryLoggerProvider() + : this(new(), serviceProvider: null, ownsServiceProvider: false) { } - /// - /// Initializes a new instance of the class. - /// - public OpenTelemetryLoggerProvider() - : this(BuildOptions(configure: null)) + // 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) + internal OpenTelemetryLoggerProvider(OpenTelemetryLoggerOptions options, IServiceProvider? serviceProvider, bool ownsServiceProvider) { Guard.ThrowIfNull(options); @@ -82,12 +84,68 @@ internal OpenTelemetryLoggerProvider(OpenTelemetryLoggerOptions options) 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 configuration actions registered and + // execute them. + + var registeredConfigurations = serviceProvider.GetServices>(); + 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>(); + foreach (BaseProcessor processor in registeredProcessors) + { + this.AddProcessor(processor); + } + } + + this.Resource = this.ResourceBuilder.Build(); + this.ResourceBuilder = null; } internal IExternalScopeProvider? ScopeProvider { get; private set; } @@ -158,12 +216,16 @@ public bool ForceFlush(int timeoutMilliseconds = Timeout.Infinite) } /// - /// Create a . + /// Add a processor to the . /// - /// . - internal LogEmitter CreateEmitter() => new(this); - - internal OpenTelemetryLoggerProvider AddProcessor(BaseProcessor processor) + /// + /// Note: The supplied will be + /// automatically disposed when then the is disposed. + /// + /// Log processor to add. + /// The supplied for chaining. + public OpenTelemetryLoggerProvider AddProcessor(BaseProcessor processor) { Guard.ThrowIfNull(processor); @@ -196,6 +258,12 @@ internal OpenTelemetryLoggerProvider AddProcessor(BaseProcessor proce return this; } + /// + /// Create a . + /// + /// . + internal LogEmitter CreateEmitter() => new(this); + internal bool ContainsBatchProcessor(BaseProcessor processor) { if (processor is BatchExportProcessor) @@ -229,6 +297,8 @@ protected override void Dispose(bool disposing) // 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 @@ protected override void Dispose(bool disposing) base.Dispose(disposing); } - - private static OpenTelemetryLoggerOptions BuildOptions(Action? configure) - { - OpenTelemetryLoggerOptions options = new(); - configure?.Invoke(options); - return options; - } } } diff --git a/src/OpenTelemetry/Logs/OpenTelemetryLoggingExtensions.cs b/src/OpenTelemetry/Logs/OpenTelemetryLoggingExtensions.cs index 9687d32eb1b..5a1d80f81e9 100644 --- a/src/OpenTelemetry/Logs/OpenTelemetryLoggingExtensions.cs +++ b/src/OpenTelemetry/Logs/OpenTelemetryLoggingExtensions.cs @@ -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 @@ public static class OpenTelemetryLoggingExtensions /// /// Adds an OpenTelemetry logger named 'OpenTelemetry' to the . /// + /// + /// Note: This is safe to be called more than once and should be used by + /// library authors to ensure at least one is registered. + /// /// The to use. /// The supplied for call chaining. public static ILoggingBuilder AddOpenTelemetry(this ILoggingBuilder builder) @@ -42,6 +49,11 @@ public static ILoggingBuilder AddOpenTelemetry(this ILoggingBuilder builder) /// /// Adds an OpenTelemetry logger named 'OpenTelemetry' to the . /// + /// + /// Note: This is should only be called once during application + /// bootstrap for a given . This should + /// not be used by library authors. + /// /// The to use. /// Optional configuration action. /// The supplied for call chaining. @@ -50,11 +62,54 @@ public static ILoggingBuilder AddOpenTelemetry(this ILoggingBuilder builder, Act Guard.ThrowIfNull(builder); builder.AddConfiguration(); - builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton()); + + builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton(sp => + { + var registeredBuilders = sp.GetServices(); + if (registeredBuilders.Count() > 1) + { + throw new NotSupportedException("Multiple logger provider builders cannot be registered in the same service collection."); + } + + var finalOptions = sp.GetRequiredService>().CurrentValue; + + return new OpenTelemetryLoggerProvider(finalOptions, sp, ownsServiceProvider: false); + })); + + // Note: This will bind logger options element (eg "Logging:OpenTelemetry") to OpenTelemetryLoggerOptions + LoggerProviderOptions.RegisterProviderOptions(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(finalOptions => + { + options.ApplyTo(finalOptions); + }); } return builder; @@ -106,5 +161,15 @@ public static ILoggingBuilder AddOpenTelemetry( return builder; } + + private sealed class TrackedOpenTelemetryLoggerOptions + { + public TrackedOpenTelemetryLoggerOptions(OpenTelemetryLoggerOptions options) + { + this.Options = options; + } + + public OpenTelemetryLoggerOptions Options { get; } + } } } diff --git a/src/OpenTelemetry/Logs/Options/OpenTelemetryLoggerOptions.cs b/src/OpenTelemetry/Logs/Options/OpenTelemetryLoggerOptions.cs new file mode 100644 index 00000000000..6be36c3a2e9 --- /dev/null +++ b/src/OpenTelemetry/Logs/Options/OpenTelemetryLoggerOptions.cs @@ -0,0 +1,334 @@ +// +// 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. +// + +#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 +{ + /// + /// Contains OpenTelemetry logging options. + /// + public class OpenTelemetryLoggerOptions + { + internal readonly List> Processors = new(); + internal ResourceBuilder? ResourceBuilder; + internal List>? 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; + + /// + /// Initializes a new instance of the class. + /// + public OpenTelemetryLoggerOptions() + : this(services: null) + { + } + + internal OpenTelemetryLoggerOptions(IServiceCollection? services) + { + this.services = services; + } + + /// + /// Gets or sets a value indicating whether or not log scopes should be + /// included on generated s. Default value: + /// False. + /// + public bool IncludeScopes + { + get => this.includeScopes ?? DefaultIncludeScopes; + set => this.includeScopes = value; + } + + /// + /// Gets or sets a value indicating whether or not formatted log message + /// should be included on generated s. Default + /// value: False. + /// + public bool IncludeFormattedMessage + { + get => this.includeFormattedMessage ?? DefaultIncludeFormattedMessage; + set => this.includeFormattedMessage = value; + } + + /// + /// Gets or sets a value indicating whether or not log state should be + /// parsed into on generated s. Default value: False. + /// + /// + /// Note: When is set to will always be . + /// + public bool ParseStateValues + { + get => this.parseStateValues ?? DefaultParseStateValues; + set => this.parseStateValues = value; + } + + internal IServiceCollection? Services => this.services; + + /// + /// Adds processor to the options. + /// + /// + /// Note: The supplied will be + /// automatically disposed when then the final built from the options is + /// disposed. + /// + /// Log processor to add. + /// Returns for chaining. + public OpenTelemetryLoggerOptions AddProcessor(BaseProcessor processor) + { + Guard.ThrowIfNull(processor); + + this.Processors.Add(processor); + + return this; + } + + /// + /// Adds a processor to the options which will be retrieved using dependency injection. + /// + /// + /// Note: The type specified by will be + /// registered as a singleton service into application services. + /// + /// Processor type. + /// The supplied for chaining. + public OpenTelemetryLoggerOptions AddProcessor() + where T : BaseProcessor + { + return this + .ConfigureServices(services => services.TryAddSingleton, T>()); + } + + /// + /// Sets the from which the Resource associated with + /// this provider is built from. Overwrites currently set ResourceBuilder. + /// You should usually use instead + /// (call if desired). + /// + /// from which Resource will be built. + /// Returns for chaining. + public OpenTelemetryLoggerOptions SetResourceBuilder(ResourceBuilder resourceBuilder) + { + Guard.ThrowIfNull(resourceBuilder); + + this.ResourceBuilder = resourceBuilder; + + return this; + } + + /// + /// Modify the from which the Resource associated with + /// this provider is built from in-place. + /// + /// An action which modifies the provided in-place. + /// Returns for chaining. + public OpenTelemetryLoggerOptions ConfigureResource( + Action configure) + { + Guard.ThrowIfNull(configure); + + this.ConfigureProvider((sp, provider) => + { + Debug.Assert(provider.ResourceBuilder != null, "provider.ResourceBuilder was null"); + + configure(provider.ResourceBuilder!); + }); + + return this; + } + + /// + /// Register a callback action to configure the where logging services are configured. + /// + /// + /// Note: Logging services are only available during the application + /// configuration phase. When using "Options" pattern via or interfaces such as + /// logging services will be + /// unavailable because "Options" are built after application services + /// have been configured. + /// + /// Configuration callback. + /// The supplied for chaining. + public OpenTelemetryLoggerOptions ConfigureServices( + Action 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; + } + + /// + /// Register a callback action to configure the once the application is available. + /// + /// Configuration callback. + /// The supplied for chaining. + public OpenTelemetryLoggerOptions ConfigureProvider( + Action 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; + } + + /// + /// Sets the value of the options. + /// + /// to enable the option or + /// to disable it. + /// The supplied for + /// chaining. + public OpenTelemetryLoggerOptions SetIncludeFormattedMessage(bool enabled) + { + this.includeFormattedMessage = enabled; + return this; + } + + /// + /// Sets the value of the options. + /// + /// to enable the option or + /// to disable it. + /// The supplied for + /// chaining. + public OpenTelemetryLoggerOptions SetIncludeScopes(bool enabled) + { + this.includeScopes = enabled; + return this; + } + + /// + /// Sets the value of the options. + /// + /// to enable the option or + /// to disable it. + /// The supplied for + /// chaining. + 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>().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); + } + } + } +} diff --git a/src/OpenTelemetry/Logs/Options/OpenTelemetryLoggerOptionsExtensions.cs b/src/OpenTelemetry/Logs/Options/OpenTelemetryLoggerOptionsExtensions.cs new file mode 100644 index 00000000000..eb613e2dee3 --- /dev/null +++ b/src/OpenTelemetry/Logs/Options/OpenTelemetryLoggerOptionsExtensions.cs @@ -0,0 +1,45 @@ +// +// 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. +// + +#nullable enable + +using System; +using OpenTelemetry.Internal; + +namespace OpenTelemetry.Logs; + +/// +/// Contains extension methods for the class. +/// +public static class OpenTelemetryLoggerOptionsExtensions +{ + /// + /// Run the given actions to initialize the . + /// + /// . + /// . + 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(); + } +} diff --git a/src/OpenTelemetry/Logs/Options/OpenTelemetryLoggerOptionsSdk.cs b/src/OpenTelemetry/Logs/Options/OpenTelemetryLoggerOptionsSdk.cs new file mode 100644 index 00000000000..48f2a53b492 --- /dev/null +++ b/src/OpenTelemetry/Logs/Options/OpenTelemetryLoggerOptionsSdk.cs @@ -0,0 +1,30 @@ +// +// 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. +// + +#nullable enable + +using Microsoft.Extensions.DependencyInjection; + +namespace OpenTelemetry.Logs; + +internal sealed class OpenTelemetryLoggerOptionsSdk : OpenTelemetryLoggerOptions +{ + public OpenTelemetryLoggerOptionsSdk() + : base(new ServiceCollection()) + { + this.ConfigureServices(services => services.AddOptions()); + } +} diff --git a/src/OpenTelemetry/Sdk.cs b/src/OpenTelemetry/Sdk.cs index 72b5e8e5913..eb17c6d3631 100644 --- a/src/OpenTelemetry/Sdk.cs +++ b/src/OpenTelemetry/Sdk.cs @@ -14,9 +14,12 @@ // limitations under the License. // +#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 @@ static Sdk() /// TextMapPropagator to be set as default. public static void SetDefaultTextMapPropagator(TextMapPropagator textMapPropagator) { + Guard.ThrowIfNull(textMapPropagator); + Propagators.DefaultTextMapPropagator = textMapPropagator; } + /// + /// Creates a which is used to build + /// an . In a typical application, a single + /// is created at application startup and disposed + /// at application shutdown. It is important to ensure that the provider is not + /// disposed too early. + /// + /// instance, which is used to build a . + public static OpenTelemetryLoggerOptions CreateLoggerProviderBuilder() + { + return new OpenTelemetryLoggerOptionsSdk(); + } + /// /// Creates a which is used to build /// a . In a typical application, a single diff --git a/test/OpenTelemetry.Extensions.EventSource.Tests/OpenTelemetryEventSourceLogEmitterTests.cs b/test/OpenTelemetry.Extensions.EventSource.Tests/OpenTelemetryEventSourceLogEmitterTests.cs index e6a53aa6387..1da0f6ef04e 100644 --- a/test/OpenTelemetry.Extensions.EventSource.Tests/OpenTelemetryEventSourceLogEmitterTests.cs +++ b/test/OpenTelemetry.Extensions.EventSource.Tests/OpenTelemetryEventSourceLogEmitterTests.cs @@ -32,45 +32,41 @@ public class OpenTelemetryEventSourceLogEmitterTests [InlineData(false)] public void OpenTelemetryEventSourceLogEmitterDisposesProviderTests(bool dispose) { - List 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 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 @@ public void OpenTelemetryEventSourceLogEmitterCapturesExistingSourceTest() List 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 @@ public void OpenTelemetryEventSourceLogEmitterSimpleEventTest() List 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 @@ public void OpenTelemetryEventSourceLogEmitterSimpleEventTest() 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 @@ public void OpenTelemetryEventSourceLogEmitterSimpleEventWithActivityTest() List 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 @@ public void OpenTelemetryEventSourceLogEmitterComplexEventTest(bool formatMessag List 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 @@ public void OpenTelemetryEventSourceLogEmitterComplexEventTest(bool formatMessag 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 @@ public void OpenTelemetryEventSourceLogEmitterActivityIdTest(bool enableTplListe List 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 @@ public void OpenTelemetryEventSourceLogEmitterActivityIdTest(bool enableTplListe } } - private sealed class WrappedOpenTelemetryLoggerProvider : OpenTelemetryLoggerProvider + private sealed class DisposeTrackingProcessor : BaseProcessor { - public WrappedOpenTelemetryLoggerProvider(Action configure) - : base(configure) - { - } - public bool Disposed { get; private set; } protected override void Dispose(bool disposing) @@ -310,55 +296,6 @@ protected override void Dispose(bool disposing) } } - [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 eventSources = new(); diff --git a/test/OpenTelemetry.Extensions.EventSource.Tests/OpenTelemetryEventSourceLoggerOptionsExtensionsTests.cs b/test/OpenTelemetry.Extensions.EventSource.Tests/OpenTelemetryEventSourceLoggerOptionsExtensionsTests.cs new file mode 100644 index 00000000000..0dea48dd69f --- /dev/null +++ b/test/OpenTelemetry.Extensions.EventSource.Tests/OpenTelemetryEventSourceLoggerOptionsExtensionsTests.cs @@ -0,0 +1,65 @@ +// +// 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. +// + +#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(); + + 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(); + + eventSourceManager = serviceProvider.GetRequiredService(); + + Assert.Single(eventSourceManager.Emitters); + + TestEventSource.Log.SimpleEvent(); + } + + Assert.Single(exportedItems); + + Assert.Empty(eventSourceManager.Emitters); + } + } +} diff --git a/test/OpenTelemetry.Extensions.EventSource.Tests/TestEventSource.cs b/test/OpenTelemetry.Extensions.EventSource.Tests/TestEventSource.cs new file mode 100644 index 00000000000..aee8f12b888 --- /dev/null +++ b/test/OpenTelemetry.Extensions.EventSource.Tests/TestEventSource.cs @@ -0,0 +1,71 @@ +// +// 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. +// + +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); + } + } +} diff --git a/test/OpenTelemetry.Extensions.Serilog.Tests/OpenTelemetrySerilogSinkTests.cs b/test/OpenTelemetry.Extensions.Serilog.Tests/OpenTelemetrySerilogSinkTests.cs index 000a1e7c1d6..b1978aa5c86 100644 --- a/test/OpenTelemetry.Extensions.Serilog.Tests/OpenTelemetrySerilogSinkTests.cs +++ b/test/OpenTelemetry.Extensions.Serilog.Tests/OpenTelemetrySerilogSinkTests.cs @@ -33,29 +33,24 @@ public class OpenTelemetrySerilogSinkTests [InlineData(false)] public void SerilogDisposesProviderTests(bool dispose) { - List 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(); + using (var openTelemetryLoggerProvider = Sdk.CreateLoggerProviderBuilder() + .AddProcessor(disposeTrackingProcessor) + .Build()) + { + Log.Logger = new LoggerConfiguration() + .WriteTo.OpenTelemetry(openTelemetryLoggerProvider, disposeProvider: dispose) + .CreateLogger(); - Assert.Equal(dispose, openTelemetryLoggerProvider.Disposed); + Log.CloseAndFlush(); - if (!dispose) - { - openTelemetryLoggerProvider.Dispose(); + Assert.Equal(dispose, disposeTrackingProcessor.Disposed); } - Assert.True(openTelemetryLoggerProvider.Disposed); + Assert.True(disposeTrackingProcessor.Disposed); } [Theory] @@ -66,12 +61,10 @@ public void SerilogBasicLogTests(bool includeFormattedMessage) List 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 @@ public void SerilogBasicLogWithActivityTest() List 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 @@ public void SerilogCategoryNameTest() List 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 @@ public void SerilogComplexMessageTemplateTest() List 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 @@ public void SerilogArrayMessageTemplateTest() List 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 @@ public void SerilogExceptionTest() 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 @@ public void SerilogExceptionTest() Assert.Equal(ex, logRecord.Exception); } - private sealed class WrappedOpenTelemetryLoggerProvider : OpenTelemetryLoggerProvider + private sealed class DisposeTrackingProcessor : BaseProcessor { - public WrappedOpenTelemetryLoggerProvider(Action configure) - : base(configure) - { - } - public bool Disposed { get; private set; } protected override void Dispose(bool disposing) diff --git a/test/OpenTelemetry.Tests/Logs/LogEmitterTests.cs b/test/OpenTelemetry.Tests/Logs/LogEmitterTests.cs index 7e086accf00..e8da01b66f0 100644 --- a/test/OpenTelemetry.Tests/Logs/LogEmitterTests.cs +++ b/test/OpenTelemetry.Tests/Logs/LogEmitterTests.cs @@ -29,10 +29,9 @@ public void LogEmitterBasicTest() { var exportedItems = new List(); - 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 @@ public void LogEmitterFromActivityTest() { var exportedItems = new List(); - 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 @@ public void LogEmitterLocalToUtcTimestampTest() { var exportedItems = new List(); - 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 @@ public void LogEmitterUnspecifiedTimestampTest() { var exportedItems = new List(); - using var provider = new OpenTelemetryLoggerProvider(options => - { - options.AddInMemoryExporter(exportedItems); - }); + using var provider = Sdk.CreateLoggerProviderBuilder() + .AddInMemoryExporter(exportedItems) + .Build(); var logEmitter = provider.CreateEmitter(); diff --git a/test/OpenTelemetry.Tests/Logs/OpenTelemetryLoggerOptionsSdkTests.cs b/test/OpenTelemetry.Tests/Logs/OpenTelemetryLoggerOptionsSdkTests.cs new file mode 100644 index 00000000000..b62080951c3 --- /dev/null +++ b/test/OpenTelemetry.Tests/Logs/OpenTelemetryLoggerOptionsSdkTests.cs @@ -0,0 +1,121 @@ +// +// 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. +// + +#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 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() + .ConfigureServices(services => + { + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton, CustomProcessor>(); + services.Configure(o => + { + optionsConfigureInvocations++; + + Assert.Null(o.Services); + + Assert.Throws(() => o.ConfigureServices(s => { })); + + o.ConfigureResource(r => r.AddAttributes(new Dictionary { ["key1"] = "value1" })); + + o.ConfigureProvider((sp, p) => optionsConfigureInvocations++); + }); + }) + .ConfigureProvider((sp, p) => + { + Assert.NotNull(sp); + + providerFromConfigureCallback = p; + + Assert.NotNull(sp.GetService()); + }); + + using var provider = returnedOptions.Build(); + + Assert.NotNull(provider); + + Assert.Throws(() => returnedOptions.ConfigureServices(s => { })); + Assert.Throws(() => returnedOptions.ConfigureResource(r => { })); + Assert.Throws(() => returnedOptions.ConfigureProvider((sp, p) => { })); + Assert.Throws(() => 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; + 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 + { + } +} diff --git a/test/OpenTelemetry.Tests/Logs/OpenTelemetryLoggerProviderTests.cs b/test/OpenTelemetry.Tests/Logs/OpenTelemetryLoggerProviderTests.cs index 6ddd5d7caa4..674e63eaef5 100644 --- a/test/OpenTelemetry.Tests/Logs/OpenTelemetryLoggerProviderTests.cs +++ b/test/OpenTelemetry.Tests/Logs/OpenTelemetryLoggerProviderTests.cs @@ -43,18 +43,15 @@ public void ConfigureCtorTests() { 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("key1", "value1") })); - - options.AddInMemoryExporter(new List()); - }); + .AddAttributes(new[] { new KeyValuePair("key1", "value1") })) + .AddInMemoryExporter(new List()) + .Build(); Assert.Equal(!defaults.IncludeScopes, provider.IncludeScopes); Assert.Equal(!defaults.IncludeFormattedMessage, provider.IncludeFormattedMessage); diff --git a/test/OpenTelemetry.Tests/Logs/OpenTelemetryLoggingExtensionsTests.cs b/test/OpenTelemetry.Tests/Logs/OpenTelemetryLoggingExtensionsTests.cs index 5f07ee7b06f..f4553728681 100644 --- a/test/OpenTelemetry.Tests/Logs/OpenTelemetryLoggingExtensionsTests.cs +++ b/test/OpenTelemetry.Tests/Logs/OpenTelemetryLoggingExtensionsTests.cs @@ -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 void ServiceCollectionAddOpenTelemetryNoParametersTest() } [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 void ServiceCollectionAddOpenTelemetryConfigureActionTests(int numberOfBu 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 void ServiceCollectionAddOpenTelemetryConfigureActionTests(int numberOfBu serviceCollection.Configure(OptionsCallback); } + Assert.NotNull(optionsInstance); + using ServiceProvider serviceProvider = serviceCollection.BuildServiceProvider(); + optionsInstance = null; + ILoggerFactory? loggerFactory = serviceProvider.GetService(); 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 @@ void ConfigureCallback(OpenTelemetryLoggerOptions options) } else { - Assert.Equal(optionsInstance, options); + // Note: In the callback phase each options instance is unique + Assert.NotEqual(optionsInstance, options); } configureCallbackInvocations++; @@ -108,6 +112,7 @@ void OptionsCallback(OpenTelemetryLoggerOptions options) } else { + // Note: In the options phase each instance is the same Assert.Equal(optionsInstance, options); } @@ -116,7 +121,23 @@ void OptionsCallback(OpenTelemetryLoggerOptions options) } [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(() => serviceProvider.GetService()); + } + + [Fact] + public void LoggingBuilderAddOpenTelemetryWithProviderTest() { var provider = new WrappedOpenTelemetryLoggerProvider(); @@ -145,7 +166,7 @@ public void ServiceCollectionAddOpenTelemetryWithProviderTest() [Theory] [InlineData(true)] [InlineData(false)] - public void ServiceCollectionAddOpenTelemetryWithProviderAndDisposeSpecifiedTests(bool dispose) + public void LoggingBuilderAddOpenTelemetryWithProviderAndDisposeSpecifiedTests(bool dispose) { var provider = new WrappedOpenTelemetryLoggerProvider(); @@ -197,6 +218,283 @@ public void LoggerFactoryCreateAddOpenTelemetryWithProviderAndDisposeSpecifiedTe 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(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(); + + 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 = null; + + using (var serviceProvider = services.BuildServiceProvider()) + { + var loggerFactory = serviceProvider.GetRequiredService(); + + customProcessor = serviceProvider.GetRequiredService>() 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(); + + CustomProcessor? customProcessor = null; + + services.AddLogging(configure => + { + configure.AddOpenTelemetry(options => + { + options.ConfigureProvider((sp, provider) => + { + var testClass = sp.GetRequiredService(); + + customProcessor = new CustomProcessor + { + TestClass = testClass, + }; + + provider.AddProcessor(customProcessor); + }); + }); + }); + + using var serviceProvider = services.BuildServiceProvider(); + + var loggerFactory = serviceProvider.GetRequiredService(); + + Assert.NotNull(customProcessor?.TestClass); + } + + [Fact] + public void LoggingBuilderAddOpenTelemetryExternalRegistrationTest() + { + CustomProcessor.InstanceCount = 0; + + var services = new ServiceCollection(); + + services.AddSingleton>(sp => new CustomProcessor()); + services.AddSingleton>(sp => new CustomProcessor()); + + services.AddLogging(configure => + { + configure.AddOpenTelemetry(); + }); + + using var serviceProvider = services.BuildServiceProvider(); + + var loggerFactory = serviceProvider.GetRequiredService(); + + Assert.Equal(2, CustomProcessor.InstanceCount); + } + + [Fact] + public void LoggingBuilderAddOpenTelemetryOptionsOrderingTest() + { + int configureInvocationCount = 0; + + var services = new ServiceCollection(); + + OpenTelemetryLoggerProvider? provider = null; + + services.Configure(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(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(); + + Assert.NotNull(provider); + Assert.Equal(3, configureInvocationCount); + + var finalOptions = serviceProvider.GetRequiredService>().CurrentValue; + + Assert.False(finalOptions.IncludeFormattedMessage); + Assert.True(finalOptions.IncludeScopes); + Assert.True(finalOptions.ParseStateValues); + + var processor = provider!.Processor as CompositeProcessor; + + 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(options => + { + options.ConfigureResource(builder => builder.AddAttributes(new Dictionary { ["key1"] = "value1" })); + }); + + services.Configure(options => + { + options.ConfigureResource(builder => builder.AddAttributes(new Dictionary { ["key2"] = "value2" })); + }); + + using var serviceProvider = services.BuildServiceProvider(); + + var loggerFactory = serviceProvider.GetRequiredService(); + + 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>((sp, provider) => configurationInvocations++); + + using var serviceProvider = services.BuildServiceProvider(); + + var loggerFactory = serviceProvider.GetRequiredService(); + + Assert.Equal(1, configurationInvocations); + } + private sealed class WrappedOpenTelemetryLoggerProvider : OpenTelemetryLoggerProvider { public bool Disposed { get; private set; } @@ -208,4 +506,32 @@ protected override void Dispose(bool disposing) base.Dispose(disposing); } } + + private sealed class CustomProcessor : BaseProcessor + { + 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 + { + } }