Skip to content

Commit

Permalink
[sdk] Add OpenTelemetrySdk builder pattern (#5325)
Browse files Browse the repository at this point in the history
  • Loading branch information
CodeBlanch committed Jul 19, 2024
1 parent 36c586d commit 05bb05a
Show file tree
Hide file tree
Showing 5 changed files with 244 additions and 0 deletions.
8 changes: 8 additions & 0 deletions src/OpenTelemetry/.publicApi/Stable/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
@@ -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<OpenTelemetry.IOpenTelemetryBuilder!>! configure) -> OpenTelemetry.OpenTelemetrySdk!
static OpenTelemetry.OpenTelemetrySdkExtensions.GetLoggerFactory(this OpenTelemetry.OpenTelemetrySdk! sdk) -> Microsoft.Extensions.Logging.ILoggerFactory!
6 changes: 6 additions & 0 deletions src/OpenTelemetry/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
124 changes: 124 additions & 0 deletions src/OpenTelemetry/OpenTelemetrySdk.cs
Original file line number Diff line number Diff line change
@@ -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;

/// <summary>
/// Contains methods for configuring the OpenTelemetry SDK and accessing
/// logging, metrics, and tracing providers.
/// </summary>
public sealed class OpenTelemetrySdk : IDisposable
{
private readonly ServiceProvider serviceProvider;

private OpenTelemetrySdk(
Action<IOpenTelemetryBuilder> 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();
}

/// <summary>
/// Gets the <see cref="Logs.LoggerProvider"/>.
/// </summary>
/// <remarks>
/// Note: The default <see cref="LoggerProvider"/> will be a no-op instance.
/// Call <see
/// cref="OpenTelemetryBuilderSdkExtensions.WithLogging(IOpenTelemetryBuilder)"/> to
/// enable logging.
/// </remarks>
public LoggerProvider LoggerProvider { get; }

/// <summary>
/// Gets the <see cref="Metrics.MeterProvider"/>.
/// </summary>
/// <remarks>
/// Note: The default <see cref="MeterProvider"/> will be a no-op instance.
/// Call <see
/// cref="OpenTelemetryBuilderSdkExtensions.WithMetrics(IOpenTelemetryBuilder)"/>
/// to enable metrics.
/// </remarks>
public MeterProvider MeterProvider { get; }

/// <summary>
/// Gets the <see cref="Trace.TracerProvider"/>.
/// </summary>
/// <remarks>
/// Note: The default <see cref="TracerProvider"/> will be a no-op instance.
/// Call <see
/// cref="OpenTelemetryBuilderSdkExtensions.WithTracing(IOpenTelemetryBuilder)"/>
/// to enable tracing.
/// </remarks>
public TracerProvider TracerProvider { get; }

/// <summary>
/// Gets the <see cref="IServiceProvider"/> containing SDK services.
/// </summary>
internal IServiceProvider Services => this.serviceProvider;

/// <summary>
/// Create an <see cref="OpenTelemetrySdk"/> instance.
/// </summary>
/// <param name="configure"><see cref="IOpenTelemetryBuilder"/> configuration delegate.</param>
/// <returns>Created <see cref="OpenTelemetrySdk"/>.</returns>
public static OpenTelemetrySdk Create(
Action<IOpenTelemetryBuilder> configure)
{
Guard.ThrowIfNull(configure);

return new(configure);
}

/// <inheritdoc/>
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; }
}
}
36 changes: 36 additions & 0 deletions src/OpenTelemetry/OpenTelemetrySdkExtensions.cs
Original file line number Diff line number Diff line change
@@ -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;

/// <summary>
/// Contains methods for extending the <see cref="OpenTelemetrySdk"/> class.
/// </summary>
public static class OpenTelemetrySdkExtensions
{
private static readonly NullLoggerFactory NoopLoggerFactory = new();

/// <summary>
/// Gets the <see cref="ILoggerFactory"/> contained in an <see
/// cref="OpenTelemetrySdk"/> instance.
/// </summary>
/// <remarks>
/// Note: The default <see cref="ILoggerFactory"/> will be a no-op instance.
/// Call <see
/// cref="OpenTelemetryBuilderSdkExtensions.WithLogging(IOpenTelemetryBuilder)"/>
/// to enable logging.
/// </remarks>
/// <param name="sdk"><see cref="OpenTelemetrySdk"/>.</param>
/// <returns><see cref="ILoggerFactory"/>.</returns>
public static ILoggerFactory GetLoggerFactory(this OpenTelemetrySdk sdk)
{
Guard.ThrowIfNull(sdk);

return (ILoggerFactory?)sdk.Services.GetService(typeof(ILoggerFactory))
?? NoopLoggerFactory;
}
}
70 changes: 70 additions & 0 deletions test/OpenTelemetry.Tests/OpenTelemetrySdkTests.cs
Original file line number Diff line number Diff line change
@@ -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<ArgumentNullException>(() => 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);
}
}

0 comments on commit 05bb05a

Please sign in to comment.