diff --git a/src/OpenTelemetry/.publicApi/Stable/PublicAPI.Unshipped.txt b/src/OpenTelemetry/.publicApi/Stable/PublicAPI.Unshipped.txt index e69de29bb2d..7dc29c7de2d 100644 --- a/src/OpenTelemetry/.publicApi/Stable/PublicAPI.Unshipped.txt +++ b/src/OpenTelemetry/.publicApi/Stable/PublicAPI.Unshipped.txt @@ -0,0 +1,8 @@ +OpenTelemetry.OpenTelemetrySdk +OpenTelemetry.OpenTelemetrySdk.Dispose() -> void +OpenTelemetry.OpenTelemetrySdk.LoggerProvider.get -> OpenTelemetry.Logs.LoggerProvider! +OpenTelemetry.OpenTelemetrySdk.MeterProvider.get -> OpenTelemetry.Metrics.MeterProvider! +OpenTelemetry.OpenTelemetrySdk.TracerProvider.get -> OpenTelemetry.Trace.TracerProvider! +OpenTelemetry.OpenTelemetrySdkExtensions +static OpenTelemetry.OpenTelemetrySdk.Create(System.Action! configure) -> OpenTelemetry.OpenTelemetrySdk! +static OpenTelemetry.OpenTelemetrySdkExtensions.GetLoggerFactory(this OpenTelemetry.OpenTelemetrySdk! sdk) -> Microsoft.Extensions.Logging.ILoggerFactory! diff --git a/src/OpenTelemetry/CHANGELOG.md b/src/OpenTelemetry/CHANGELOG.md index ca4d195698d..100ec92d219 100644 --- a/src/OpenTelemetry/CHANGELOG.md +++ b/src/OpenTelemetry/CHANGELOG.md @@ -2,6 +2,12 @@ ## Unreleased +* Added `OpenTelemetrySdk.Create` API for configuring OpenTelemetry .NET signals + (logging, tracing, and metrics) via a single builder. This new API simplifies + bootstrap and teardown, and supports cross-cutting extensions targeting + `IOpenTelemetryBuilder`. + ([#5325](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5325)) + ## 1.9.0 Released 2024-Jun-14 diff --git a/src/OpenTelemetry/OpenTelemetrySdk.cs b/src/OpenTelemetry/OpenTelemetrySdk.cs new file mode 100644 index 00000000000..7020d05ce1c --- /dev/null +++ b/src/OpenTelemetry/OpenTelemetrySdk.cs @@ -0,0 +1,124 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +using System.Diagnostics; +using Microsoft.Extensions.DependencyInjection; +using OpenTelemetry.Internal; +using OpenTelemetry.Logs; +using OpenTelemetry.Metrics; +using OpenTelemetry.Trace; + +namespace OpenTelemetry; + +/// +/// Contains methods for configuring the OpenTelemetry SDK and accessing +/// logging, metrics, and tracing providers. +/// +public sealed class OpenTelemetrySdk : IDisposable +{ + private readonly ServiceProvider serviceProvider; + + private OpenTelemetrySdk( + Action configure) + { + Debug.Assert(configure != null, "configure was null"); + + var services = new ServiceCollection(); + + var builder = new OpenTelemetrySdkBuilder(services); + + configure!(builder); + + this.serviceProvider = services.BuildServiceProvider(); + + this.LoggerProvider = (LoggerProvider?)this.serviceProvider.GetService(typeof(LoggerProvider)) + ?? new NoopLoggerProvider(); + this.MeterProvider = (MeterProvider?)this.serviceProvider.GetService(typeof(MeterProvider)) + ?? new NoopMeterProvider(); + this.TracerProvider = (TracerProvider?)this.serviceProvider.GetService(typeof(TracerProvider)) + ?? new NoopTracerProvider(); + } + + /// + /// Gets the . + /// + /// + /// Note: The default will be a no-op instance. + /// Call to + /// enable logging. + /// + public LoggerProvider LoggerProvider { get; } + + /// + /// Gets the . + /// + /// + /// Note: The default will be a no-op instance. + /// Call + /// to enable metrics. + /// + public MeterProvider MeterProvider { get; } + + /// + /// Gets the . + /// + /// + /// Note: The default will be a no-op instance. + /// Call + /// to enable tracing. + /// + public TracerProvider TracerProvider { get; } + + /// + /// Gets the containing SDK services. + /// + internal IServiceProvider Services => this.serviceProvider; + + /// + /// Create an instance. + /// + /// configuration delegate. + /// Created . + public static OpenTelemetrySdk Create( + Action configure) + { + Guard.ThrowIfNull(configure); + + return new(configure); + } + + /// + public void Dispose() + { + this.serviceProvider.Dispose(); + } + + internal sealed class NoopLoggerProvider : LoggerProvider + { + } + + internal sealed class NoopMeterProvider : MeterProvider + { + } + + internal sealed class NoopTracerProvider : TracerProvider + { + } + + private sealed class OpenTelemetrySdkBuilder : IOpenTelemetryBuilder + { + public OpenTelemetrySdkBuilder(IServiceCollection services) + { + Debug.Assert(services != null, "services was null"); + + services!.AddOpenTelemetrySharedProviderBuilderServices(); + + this.Services = services!; + } + + public IServiceCollection Services { get; } + } +} diff --git a/src/OpenTelemetry/OpenTelemetrySdkExtensions.cs b/src/OpenTelemetry/OpenTelemetrySdkExtensions.cs new file mode 100644 index 00000000000..f1f01e0f20a --- /dev/null +++ b/src/OpenTelemetry/OpenTelemetrySdkExtensions.cs @@ -0,0 +1,36 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using OpenTelemetry.Internal; + +namespace OpenTelemetry; + +/// +/// Contains methods for extending the class. +/// +public static class OpenTelemetrySdkExtensions +{ + private static readonly NullLoggerFactory NoopLoggerFactory = new(); + + /// + /// Gets the contained in an instance. + /// + /// + /// Note: The default will be a no-op instance. + /// Call + /// to enable logging. + /// + /// . + /// . + public static ILoggerFactory GetLoggerFactory(this OpenTelemetrySdk sdk) + { + Guard.ThrowIfNull(sdk); + + return (ILoggerFactory?)sdk.Services.GetService(typeof(ILoggerFactory)) + ?? NoopLoggerFactory; + } +} diff --git a/test/OpenTelemetry.Tests/OpenTelemetrySdkTests.cs b/test/OpenTelemetry.Tests/OpenTelemetrySdkTests.cs new file mode 100644 index 00000000000..3c8522cb554 --- /dev/null +++ b/test/OpenTelemetry.Tests/OpenTelemetrySdkTests.cs @@ -0,0 +1,70 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#nullable enable + +using Microsoft.Extensions.Logging.Abstractions; +using OpenTelemetry.Logs; +using OpenTelemetry.Metrics; +using OpenTelemetry.Trace; +using Xunit; + +namespace OpenTelemetry.Tests; + +public class OpenTelemetrySdkTests +{ + [Fact] + public void BuilderDelegateRequiredTest() + { + Assert.Throws(() => OpenTelemetrySdk.Create(null!)); + } + + [Fact] + public void NoopProvidersReturnedTest() + { + bool builderDelegateInvoked = false; + + using var sdk = OpenTelemetrySdk.Create(builder => + { + builderDelegateInvoked = true; + Assert.NotNull(builder.Services); + }); + + Assert.True(builderDelegateInvoked); + + Assert.NotNull(sdk); + Assert.NotNull(sdk.Services); + Assert.True(sdk.LoggerProvider is OpenTelemetrySdk.NoopLoggerProvider); + Assert.True(sdk.MeterProvider is OpenTelemetrySdk.NoopMeterProvider); + Assert.True(sdk.TracerProvider is OpenTelemetrySdk.NoopTracerProvider); + Assert.True(sdk.GetLoggerFactory() is NullLoggerFactory); + } + + [Fact] + public void ProvidersCreatedAndDisposedTest() + { + var sdk = OpenTelemetrySdk.Create(builder => + { + builder + .WithLogging() + .WithMetrics() + .WithTracing(); + }); + + var loggerProvider = sdk.LoggerProvider as LoggerProviderSdk; + var meterProvider = sdk.MeterProvider as MeterProviderSdk; + var tracerProvider = sdk.TracerProvider as TracerProviderSdk; + + Assert.NotNull(loggerProvider); + Assert.NotNull(meterProvider); + Assert.NotNull(tracerProvider); + + Assert.True(sdk.GetLoggerFactory() is not NullLoggerFactory); + + sdk.Dispose(); + + Assert.True(loggerProvider.Disposed); + Assert.True(meterProvider.Disposed); + Assert.True(tracerProvider.Disposed); + } +}