diff --git a/src/OpenTelemetry.Api.ProviderBuilderExtensions/Logs/OpenTelemetryDependencyInjectionLoggerProviderBuilderExtensions.cs b/src/OpenTelemetry.Api.ProviderBuilderExtensions/Logs/OpenTelemetryDependencyInjectionLoggerProviderBuilderExtensions.cs index cd28371830a..310d09fc55a 100644 --- a/src/OpenTelemetry.Api.ProviderBuilderExtensions/Logs/OpenTelemetryDependencyInjectionLoggerProviderBuilderExtensions.cs +++ b/src/OpenTelemetry.Api.ProviderBuilderExtensions/Logs/OpenTelemetryDependencyInjectionLoggerProviderBuilderExtensions.cs @@ -106,7 +106,7 @@ public static LoggerProviderBuilder AddInstrumentation( loggerProviderBuilder.ConfigureBuilder((sp, builder) => { - if (loggerProviderBuilder is ILoggerProviderBuilder iLoggerProviderBuilder + if (builder is ILoggerProviderBuilder iLoggerProviderBuilder && iLoggerProviderBuilder.Provider != null) { builder.AddInstrumentation(() => instrumentationFactory(sp, iLoggerProviderBuilder.Provider)); diff --git a/src/OpenTelemetry/ProviderExtensions.cs b/src/OpenTelemetry/ProviderExtensions.cs index b25753e71ef..62ed5e3c03d 100644 --- a/src/OpenTelemetry/ProviderExtensions.cs +++ b/src/OpenTelemetry/ProviderExtensions.cs @@ -73,6 +73,10 @@ public static Resource GetDefaultResource(this BaseProvider baseProvider) { return meterProviderSdk.ServiceProvider; } + else if (baseProvider is LoggerProviderSdk loggerProviderSdk) + { + return loggerProviderSdk.ServiceProvider; + } else if (baseProvider is OpenTelemetryLoggerProvider openTelemetryLoggerProvider) { return openTelemetryLoggerProvider.ServiceProvider; diff --git a/test/OpenTelemetry.Tests/Logs/LoggerProviderBuilderExtensionsTests.cs b/test/OpenTelemetry.Tests/Logs/LoggerProviderBuilderExtensionsTests.cs new file mode 100644 index 00000000000..0783f8b90b8 --- /dev/null +++ b/test/OpenTelemetry.Tests/Logs/LoggerProviderBuilderExtensionsTests.cs @@ -0,0 +1,188 @@ +// +// 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; +using OpenTelemetry.Resources; +using Xunit; + +namespace OpenTelemetry.Logs.Tests; + +public sealed class LoggerProviderBuilderExtensionsTests +{ + [Fact] + public void LoggerProviderBuilderAddInstrumentationTest() + { + List? instrumentation = null; + + using (var provider = Sdk.CreateLoggerProviderBuilder() + .AddInstrumentation() + .AddInstrumentation((sp, provider) => new CustomInstrumentation() { Provider = provider }) + .AddInstrumentation(new CustomInstrumentation()) + .Build() as LoggerProviderSdk) + { + Assert.NotNull(provider); + + Assert.Equal(3, provider.Instrumentations.Count); + + Assert.Null(((CustomInstrumentation)provider.Instrumentations[0]).Provider); + Assert.False(((CustomInstrumentation)provider.Instrumentations[0]).Disposed); + + Assert.NotNull(((CustomInstrumentation)provider.Instrumentations[1]).Provider); + Assert.False(((CustomInstrumentation)provider.Instrumentations[1]).Disposed); + + Assert.Null(((CustomInstrumentation)provider.Instrumentations[2]).Provider); + Assert.False(((CustomInstrumentation)provider.Instrumentations[2]).Disposed); + + instrumentation = new List(provider.Instrumentations); + } + + Assert.True(((CustomInstrumentation)instrumentation[0]).Disposed); + Assert.True(((CustomInstrumentation)instrumentation[1]).Disposed); + Assert.True(((CustomInstrumentation)instrumentation[2]).Disposed); + } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public void LoggerProviderBuilderNestedResolutionUsingBuilderTest(bool callNestedConfigure) + { + bool innerConfigureBuilderTestExecuted = false; + bool innerConfigureOpenTelemetryLoggerProviderTestExecuted = false; + + using var provider = Sdk.CreateLoggerProviderBuilder() + .ConfigureServices(services => + { + if (callNestedConfigure) + { + services.ConfigureOpenTelemetryLoggerProvider( + (sp, builder) => innerConfigureOpenTelemetryLoggerProviderTestExecuted = true); + } + }) + .ConfigureBuilder((sp, builder) => + { + innerConfigureBuilderTestExecuted = true; + Assert.Throws(() => sp.GetService()); + }) + .Build(); + + Assert.True(innerConfigureBuilderTestExecuted); + Assert.Equal(callNestedConfigure, innerConfigureOpenTelemetryLoggerProviderTestExecuted); + + Assert.Throws(() => provider.GetServiceProvider()?.GetService()); + } + + [Fact] + public void LoggerProviderBuilderSetResourceBuilderTests() + { + using var provider = Sdk.CreateLoggerProviderBuilder() + .SetResourceBuilder(ResourceBuilder + .CreateEmpty() + .AddAttributes(new[] { new KeyValuePair("key1", "value1") })) + .Build() as LoggerProviderSdk; + + Assert.NotNull(provider); + + Assert.NotNull(provider.Resource); + Assert.Contains(provider.Resource.Attributes, value => value.Key == "key1" && (string)value.Value == "value1"); + } + + [Fact] + public void LoggerProviderBuilderConfigureResourceBuilderTests() + { + using var provider = Sdk.CreateLoggerProviderBuilder() + .ConfigureResource(resource => resource + .Clear() + .AddAttributes(new[] { new KeyValuePair("key1", "value1") })) + .Build() as LoggerProviderSdk; + + Assert.NotNull(provider); + + Assert.NotNull(provider.Resource); + Assert.Contains(provider.Resource.Attributes, value => value.Key == "key1" && (string)value.Value == "value1"); + } + + [Fact] + public void LoggerProviderBuilderAddProcessorTest() + { + List processors = new(); + + using (var provider = Sdk.CreateLoggerProviderBuilder() + .AddProcessor() + .AddProcessor(sp => new CustomProcessor()) + .AddProcessor(new CustomProcessor()) + .Build() as LoggerProviderSdk) + { + Assert.NotNull(provider); + Assert.NotNull(provider.Processor); + + var compositeProcessor = provider.Processor as CompositeProcessor; + + Assert.NotNull(compositeProcessor); + + var current = compositeProcessor.Head; + while (current != null) + { + var processor = current.Value as CustomProcessor; + Assert.NotNull(processor); + + processors.Add(processor); + Assert.False(processor.Disposed); + + current = current.Next; + } + } + + Assert.Equal(3, processors.Count); + + foreach (var processor in processors) + { + Assert.True(processor.Disposed); + } + } + + private sealed class CustomInstrumentation : IDisposable + { + public bool Disposed; + public LoggerProvider? Provider; + + public void Dispose() + { + this.Disposed = true; + } + } + + private sealed class CustomProcessor : BaseProcessor + { + public bool Disposed; + + protected override void Dispose(bool disposing) + { + this.Disposed = true; + + base.Dispose(disposing); + } + } + + private sealed class CustomExporter : BaseExporter + { + public override ExportResult Export(in Batch batch) + { + return ExportResult.Success; + } + } +} diff --git a/test/OpenTelemetry.Tests/Logs/LoggerProviderSdkTests.cs b/test/OpenTelemetry.Tests/Logs/LoggerProviderSdkTests.cs new file mode 100644 index 00000000000..1f1a377d0a6 --- /dev/null +++ b/test/OpenTelemetry.Tests/Logs/LoggerProviderSdkTests.cs @@ -0,0 +1,142 @@ +// +// 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 OpenTelemetry.Exporter; +using Xunit; + +namespace OpenTelemetry.Logs.Tests; + +public sealed class LoggerProviderSdkTests +{ + [Fact] + public void ForceFlushTest() + { + using var provider = Sdk.CreateLoggerProviderBuilder().Build() as LoggerProviderSdk; + + Assert.NotNull(provider); + + Assert.True(provider.ForceFlush()); + + List exportedItems = new(); + + provider.AddProcessor(new BatchLogRecordExportProcessor(new InMemoryExporter(exportedItems))); + + var logger = provider.GetLogger("TestLogger"); + + logger.EmitLog(new() { Body = "Hello world" }); + + Assert.Empty(exportedItems); + + Assert.True(provider.ForceFlush()); + + Assert.Single(exportedItems); + } + + [Fact] + public void ThreadStaticPoolUsedByProviderTests() + { + using var provider1 = Sdk.CreateLoggerProviderBuilder().Build() as LoggerProviderSdk; + + Assert.NotNull(provider1); + + Assert.Equal(LogRecordThreadStaticPool.Instance, provider1.LogRecordPool); + + using var provider2 = Sdk.CreateLoggerProviderBuilder() + .AddProcessor(new SimpleLogRecordExportProcessor(new NoopExporter())) + .Build() as LoggerProviderSdk; + + Assert.NotNull(provider2); + + Assert.Equal(LogRecordThreadStaticPool.Instance, provider2.LogRecordPool); + + using var provider3 = Sdk.CreateLoggerProviderBuilder() + .AddProcessor(new SimpleLogRecordExportProcessor(new NoopExporter())) + .AddProcessor(new SimpleLogRecordExportProcessor(new NoopExporter())) + .Build() as LoggerProviderSdk; + + Assert.NotNull(provider3); + + Assert.Equal(LogRecordThreadStaticPool.Instance, provider3.LogRecordPool); + } + + [Fact] + public void SharedPoolUsedByProviderTests() + { + using var provider1 = Sdk.CreateLoggerProviderBuilder() + .AddProcessor(new BatchLogRecordExportProcessor(new NoopExporter())) + .Build() as LoggerProviderSdk; + + Assert.NotNull(provider1); + + Assert.Equal(LogRecordSharedPool.Current, provider1.LogRecordPool); + + using var provider2 = Sdk.CreateLoggerProviderBuilder() + .AddProcessor(new SimpleLogRecordExportProcessor(new NoopExporter())) + .AddProcessor(new BatchLogRecordExportProcessor(new NoopExporter())) + .Build() as LoggerProviderSdk; + + Assert.NotNull(provider2); + + Assert.Equal(LogRecordSharedPool.Current, provider2.LogRecordPool); + + using var provider3 = Sdk.CreateLoggerProviderBuilder() + .AddProcessor(new SimpleLogRecordExportProcessor(new NoopExporter())) + .AddProcessor(new CompositeProcessor(new BaseProcessor[] + { + new SimpleLogRecordExportProcessor(new NoopExporter()), + new BatchLogRecordExportProcessor(new NoopExporter()), + })) + .Build() as LoggerProviderSdk; + + Assert.NotNull(provider3); + + Assert.Equal(LogRecordSharedPool.Current, provider3.LogRecordPool); + } + + [Fact] + public void AddProcessorTest() + { + using var provider = Sdk.CreateLoggerProviderBuilder() + .Build() as LoggerProviderSdk; + + Assert.NotNull(provider); + Assert.Null(provider.Processor); + + provider.AddProcessor(new NoopProcessor()); + + Assert.NotNull(provider.Processor); + Assert.True(provider.Processor is NoopProcessor); + + provider.AddProcessor(new NoopProcessor()); + + Assert.NotNull(provider.Processor); + Assert.True(provider.Processor is CompositeProcessor); + } + + private sealed class NoopProcessor : BaseProcessor + { + } + + private sealed class NoopExporter : BaseExporter + { + public override ExportResult Export(in Batch batch) + { + return ExportResult.Success; + } + } +} diff --git a/test/OpenTelemetry.Tests/Metrics/MeterProviderBuilderExtensionsTests.cs b/test/OpenTelemetry.Tests/Metrics/MeterProviderBuilderExtensionsTests.cs index 053310ea2ed..03cea950239 100644 --- a/test/OpenTelemetry.Tests/Metrics/MeterProviderBuilderExtensionsTests.cs +++ b/test/OpenTelemetry.Tests/Metrics/MeterProviderBuilderExtensionsTests.cs @@ -219,24 +219,27 @@ public void ConfigureBuilderIConfigurationModifiableTest() [InlineData(false)] public void MeterProviderNestedResolutionUsingBuilderTest(bool callNestedConfigure) { - bool innerTestExecuted = false; + bool innerConfigureBuilderTestExecuted = false; + bool innerConfigureOpenTelemetryLoggerProviderTestExecuted = false; using var provider = Sdk.CreateMeterProviderBuilder() .ConfigureServices(services => { if (callNestedConfigure) { - services.ConfigureOpenTelemetryMeterProvider((sp, builder) => { }); + services.ConfigureOpenTelemetryMeterProvider( + (sp, builder) => innerConfigureOpenTelemetryLoggerProviderTestExecuted = true); } }) .ConfigureBuilder((sp, builder) => { - innerTestExecuted = true; + innerConfigureBuilderTestExecuted = true; Assert.Throws(() => sp.GetService()); }) .Build(); - Assert.True(innerTestExecuted); + Assert.True(innerConfigureBuilderTestExecuted); + Assert.Equal(callNestedConfigure, innerConfigureOpenTelemetryLoggerProviderTestExecuted); Assert.Throws(() => provider.GetServiceProvider()?.GetService()); } diff --git a/test/OpenTelemetry.Tests/Trace/TracerProviderBuilderExtensionsTest.cs b/test/OpenTelemetry.Tests/Trace/TracerProviderBuilderExtensionsTest.cs index cc678067a3c..85e5b88552f 100644 --- a/test/OpenTelemetry.Tests/Trace/TracerProviderBuilderExtensionsTest.cs +++ b/test/OpenTelemetry.Tests/Trace/TracerProviderBuilderExtensionsTest.cs @@ -309,24 +309,27 @@ public void ConfigureBuilderIConfigurationModifiableTest() [InlineData(false)] public void TracerProviderNestedResolutionUsingBuilderTest(bool callNestedConfigure) { - bool innerTestExecuted = false; + bool innerConfigureBuilderTestExecuted = false; + bool innerConfigureOpenTelemetryLoggerProviderTestExecuted = false; using var provider = Sdk.CreateTracerProviderBuilder() .ConfigureServices(services => { if (callNestedConfigure) { - services.ConfigureOpenTelemetryTracerProvider((sp, builder) => { }); + services.ConfigureOpenTelemetryTracerProvider( + (sp, builder) => innerConfigureOpenTelemetryLoggerProviderTestExecuted = true); } }) .ConfigureBuilder((sp, builder) => { - innerTestExecuted = true; + innerConfigureBuilderTestExecuted = true; Assert.Throws(() => sp.GetService()); }) .Build(); - Assert.True(innerTestExecuted); + Assert.True(innerConfigureBuilderTestExecuted); + Assert.Equal(callNestedConfigure, innerConfigureOpenTelemetryLoggerProviderTestExecuted); Assert.Throws(() => provider.GetServiceProvider()?.GetService()); }