Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[hosting-logs] Add WithLogging #4483

Merged
merged 4 commits into from
May 12, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,6 @@
using System.Runtime.CompilerServices;

[assembly: InternalsVisibleTo("OpenTelemetry" + AssemblyInfo.PublicKey)]
[assembly: InternalsVisibleTo("OpenTelemetry.Extensions.Hosting" + AssemblyInfo.PublicKey)]
[assembly: InternalsVisibleTo("OpenTelemetry.Tests" + AssemblyInfo.PublicKey)]
[assembly: InternalsVisibleTo("OpenTelemetry.Api.ProviderBuilderExtensions.Tests" + AssemblyInfo.PublicKey)]
2 changes: 2 additions & 0 deletions src/OpenTelemetry.Api/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
[assembly: InternalsVisibleTo("OpenTelemetry.Api.ProviderBuilderExtensions" + AssemblyInfo.PublicKey)]
[assembly: InternalsVisibleTo("OpenTelemetry.Api.ProviderBuilderExtensions.Tests" + AssemblyInfo.PublicKey)]
[assembly: InternalsVisibleTo("OpenTelemetry.Api.Tests" + AssemblyInfo.PublicKey)]
[assembly: InternalsVisibleTo("OpenTelemetry.Extensions.Hosting" + AssemblyInfo.PublicKey)]
[assembly: InternalsVisibleTo("OpenTelemetry.Extensions.Hosting.Tests" + AssemblyInfo.PublicKey)]
[assembly: InternalsVisibleTo("OpenTelemetry.Shims.OpenTracing.Tests" + AssemblyInfo.PublicKey)]
[assembly: InternalsVisibleTo("OpenTelemetry.Tests" + AssemblyInfo.PublicKey)]
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2" + AssemblyInfo.MoqPublicKey)]
Expand Down
2 changes: 2 additions & 0 deletions src/OpenTelemetry.Extensions.Hosting/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
[assembly: InternalsVisibleTo("OpenTelemetry.Extensions.Hosting.Tests" + AssemblyInfo.PublicKey)]
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2" + AssemblyInfo.MoqPublicKey)]

/* Note: OpenTelemetry.Extensions.Hosting temporarily sees OpenTelemetry.Api internals for LoggerProvider
#if SIGNED
internal static class AssemblyInfo
{
Expand All @@ -32,3 +33,4 @@ internal static class AssemblyInfo
public const string MoqPublicKey = "";
}
#endif
*/
Original file line number Diff line number Diff line change
Expand Up @@ -37,5 +37,11 @@ public void MeterProviderNotRegistered()
{
this.WriteEvent(2);
}

[Event(3, Message = "OpenTelemetry LoggerProvider was not found in application services. Logging will remain disabled.", Level = EventLevel.Warning)]
public void LoggerProviderNotRegistered()
{
this.WriteEvent(3);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
using System.Diagnostics;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using OpenTelemetry.Logs;
using OpenTelemetry.Metrics;
using OpenTelemetry.Trace;

Expand Down Expand Up @@ -64,5 +65,11 @@ internal static void Initialize(IServiceProvider serviceProvider)
{
HostingExtensionsEventSource.Log.TracerProviderNotRegistered();
}

var loggerProvider = serviceProvider!.GetService<LoggerProvider>();
if (loggerProvider == null)
{
HostingExtensionsEventSource.Log.LoggerProviderNotRegistered();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@
</ItemGroup>

<ItemGroup>
<!-- Note: OpenTelemetry.Extensions.Hosting temporarily sees OpenTelemetry.Api internals for LoggerProvider
<Compile Include="$(RepoRoot)\src\OpenTelemetry.Api\Internal\ExceptionExtensions.cs" Link="Includes\ExceptionExtensions.cs" />
<Compile Include="$(RepoRoot)\src\OpenTelemetry.Api\Internal\Guard.cs" Link="Includes\Guard.cs" />
-->
</ItemGroup>

</Project>
48 changes: 47 additions & 1 deletion src/OpenTelemetry.Extensions.Hosting/OpenTelemetryBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@
// </copyright>

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using OpenTelemetry.Internal;
using OpenTelemetry.Logs;
using OpenTelemetry.Metrics;
using OpenTelemetry.Resources;
using OpenTelemetry.Trace;
Expand Down Expand Up @@ -44,7 +46,7 @@ internal OpenTelemetryBuilder(IServiceCollection services)

/// <summary>
/// Registers an action to configure the <see cref="ResourceBuilder"/>s used
/// by tracing and metrics.
/// by tracing, metrics, and logging.
/// </summary>
/// <remarks>
/// Note: This is safe to be called multiple times and by library authors.
Expand All @@ -65,6 +67,9 @@ public OpenTelemetryBuilder ConfigureResource(
this.Services.ConfigureOpenTelemetryTracerProvider(
(sp, builder) => builder.ConfigureResource(configure));

this.Services.ConfigureOpenTelemetryLoggerProvider(
(sp, builder) => builder.ConfigureResource(configure));

return this;
}

Expand Down Expand Up @@ -131,4 +136,45 @@ public OpenTelemetryBuilder WithTracing(Action<TracerProviderBuilder> configure)

return this;
}

/// <summary>
/// Adds logging services into the builder.
/// </summary>
/// <remarks>
/// Notes:
/// <list type="bullet">
/// <item>This is safe to be called multiple times and by library authors.
/// Only a single <see cref="LoggerProvider"/> will be created for a given
/// <see cref="IServiceCollection"/>.</item>
/// <item>This operation enables <see cref="ILogger"/> integration
/// automatically by calling <see
/// cref="OpenTelemetryLoggingExtensions.AddOpenTelemetry(ILoggingBuilder)"/>.</item>
/// </list>
/// </remarks>
/// <returns>The supplied <see cref="OpenTelemetryBuilder"/> for chaining
/// calls.</returns>
internal OpenTelemetryBuilder WithLogging()
=> this.WithLogging(b => { });

/// <summary>
/// Adds logging services into the builder.
/// </summary>
/// <remarks><inheritdoc cref="WithLogging()" path="/remarks"/></remarks>
/// <param name="configure"><see cref="LoggerProviderBuilder"/>
/// configuration callback.</param>
/// <returns>The supplied <see cref="OpenTelemetryBuilder"/> for chaining
/// calls.</returns>
internal OpenTelemetryBuilder WithLogging(Action<LoggerProviderBuilder> configure)
{
Guard.ThrowIfNull(configure);

// Note: This enables ILogger integration
this.Services.AddLogging().AddOpenTelemetry();

var builder = new LoggerProviderServiceCollectionBuilder(this.Services);

configure(builder);

return this;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using OpenTelemetry.Logs;
using OpenTelemetry.Metrics;
using OpenTelemetry.Trace;
using Xunit;
Expand Down Expand Up @@ -326,6 +327,129 @@ public void AddOpenTelemetry_WithMetrics_NestedResolutionUsingConfigureTest()
Assert.True(innerTestExecuted);
}

[Fact]
public void AddOpenTelemetry_WithLogging_SingleProviderForServiceCollectionTest()
{
var services = new ServiceCollection();

services.AddOpenTelemetry().WithLogging(builder => { });

services.AddOpenTelemetry().WithLogging(builder => { });

using var serviceProvider = services.BuildServiceProvider();

Assert.NotNull(serviceProvider);

var tracerProviders = serviceProvider.GetServices<LoggerProvider>();

Assert.Single(tracerProviders);
}

[Fact]
public void AddOpenTelemetry_WithLogging_DisposalTest()
{
var services = new ServiceCollection();

bool testRun = false;

services.AddOpenTelemetry().WithLogging(builder =>
{
testRun = true;

// Note: Build can't be called directly on builder tied to external services
Assert.Throws<NotSupportedException>(() => builder.Build());
});

Assert.True(testRun);

var serviceProvider = services.BuildServiceProvider();

var provider = serviceProvider.GetRequiredService<LoggerProvider>() as LoggerProviderSdk;

Assert.NotNull(provider);
Assert.Null(provider.OwnedServiceProvider);

Assert.NotNull(serviceProvider);
Assert.NotNull(provider);

Assert.False(provider.Disposed);

serviceProvider.Dispose();

Assert.True(provider.Disposed);
}

[Fact]
public async Task AddOpenTelemetry_WithLogging_HostConfigurationHonoredTest()
{
bool configureBuilderCalled = false;

var builder = new HostBuilder()
.ConfigureAppConfiguration(builder =>
{
builder.AddInMemoryCollection(new Dictionary<string, string>
{
["TEST_KEY"] = "TEST_KEY_VALUE",
});
})
.ConfigureServices(services =>
{
services.AddOpenTelemetry()
.WithLogging(builder =>
{
if (builder is IDeferredLoggerProviderBuilder deferredLoggerProviderBuilder)
{
deferredLoggerProviderBuilder.Configure((sp, builder) =>
{
configureBuilderCalled = true;

var configuration = sp.GetRequiredService<IConfiguration>();

var testKeyValue = configuration.GetValue<string>("TEST_KEY", null);

Assert.Equal("TEST_KEY_VALUE", testKeyValue);
});
}
});
});

var host = builder.Build();

Assert.False(configureBuilderCalled);

await host.StartAsync().ConfigureAwait(false);

Assert.True(configureBuilderCalled);

await host.StopAsync().ConfigureAwait(false);

host.Dispose();
}

[Fact]
public void AddOpenTelemetry_WithLogging_NestedResolutionUsingConfigureTest()
{
bool innerTestExecuted = false;

var services = new ServiceCollection();

services.AddOpenTelemetry().WithLogging(builder =>
{
if (builder is IDeferredLoggerProviderBuilder deferredLoggerProviderBuilder)
{
deferredLoggerProviderBuilder.Configure((sp, builder) =>
{
innerTestExecuted = true;
Assert.Throws<NotSupportedException>(() => sp.GetService<LoggerProvider>());
});
}
});

using var serviceProvider = services.BuildServiceProvider();
var resolvedProvider = serviceProvider.GetRequiredService<LoggerProvider>();
Assert.True(innerTestExecuted);
}

private sealed class MySampler : Sampler
{
public override SamplingResult ShouldSample(in SamplingParameters samplingParameters)
Expand Down