[hosting-logs] Add WithLogging (#4483)
Co-authored-by: Utkarsh Umesan Pillai <utpilla@microsoft.com>
This commit is contained in:
parent
01f3891b95
commit
5357f17444
|
|
@ -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)]
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@ using System.Runtime.CompilerServices;
|
|||
[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)]
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ using System.Runtime.CompilerServices;
|
|||
[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
|
||||
{
|
||||
|
|
@ -32,3 +33,4 @@ internal static class AssemblyInfo
|
|||
public const string MoqPublicKey = "";
|
||||
}
|
||||
#endif
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -37,5 +37,11 @@ namespace OpenTelemetry.Extensions.Hosting.Implementation
|
|||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
using System.Diagnostics;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using OpenTelemetry.Logs;
|
||||
using OpenTelemetry.Metrics;
|
||||
using OpenTelemetry.Trace;
|
||||
|
||||
|
|
@ -64,5 +65,11 @@ internal sealed class TelemetryHostedService : IHostedService
|
|||
{
|
||||
HostingExtensionsEventSource.Log.TracerProviderNotRegistered();
|
||||
}
|
||||
|
||||
var loggerProvider = serviceProvider!.GetService<LoggerProvider>();
|
||||
if (loggerProvider == null)
|
||||
{
|
||||
HostingExtensionsEventSource.Log.LoggerProviderNotRegistered();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -44,7 +46,7 @@ public sealed class OpenTelemetryBuilder
|
|||
|
||||
/// <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.
|
||||
|
|
@ -65,6 +67,9 @@ public sealed class OpenTelemetryBuilder
|
|||
this.Services.ConfigureOpenTelemetryTracerProvider(
|
||||
(sp, builder) => builder.ConfigureResource(configure));
|
||||
|
||||
this.Services.ConfigureOpenTelemetryLoggerProvider(
|
||||
(sp, builder) => builder.ConfigureResource(configure));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
|
|
@ -131,4 +136,45 @@ public sealed class OpenTelemetryBuilder
|
|||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,54 @@
|
|||
// <copyright file="OpenTelemetryBuilderTests.cs" company="OpenTelemetry Authors">
|
||||
// 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.
|
||||
// </copyright>
|
||||
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using OpenTelemetry.Logs;
|
||||
using OpenTelemetry.Metrics;
|
||||
using OpenTelemetry.Resources;
|
||||
using OpenTelemetry.Trace;
|
||||
using Xunit;
|
||||
|
||||
namespace OpenTelemetry.Extensions.Hosting.Tests;
|
||||
|
||||
public class OpenTelemetryBuilderTests
|
||||
{
|
||||
[Fact]
|
||||
public void ConfigureResourceTest()
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
|
||||
services
|
||||
.AddOpenTelemetry()
|
||||
.ConfigureResource(r => r.AddResource(new Resource(new Dictionary<string, object> { ["key1"] = "value1" })))
|
||||
.WithLogging()
|
||||
.WithMetrics()
|
||||
.WithTracing();
|
||||
|
||||
using var sp = services.BuildServiceProvider();
|
||||
|
||||
var tracerProvider = sp.GetRequiredService<TracerProvider>() as TracerProviderSdk;
|
||||
var meterProvider = sp.GetRequiredService<MeterProvider>() as MeterProviderSdk;
|
||||
var loggerProvider = sp.GetRequiredService<LoggerProvider>() as LoggerProviderSdk;
|
||||
|
||||
Assert.NotNull(tracerProvider);
|
||||
Assert.NotNull(meterProvider);
|
||||
Assert.NotNull(loggerProvider);
|
||||
|
||||
Assert.Contains(tracerProvider.Resource.Attributes, kvp => kvp.Key == "key1" && (string)kvp.Value == "value1");
|
||||
Assert.Contains(meterProvider.Resource.Attributes, kvp => kvp.Key == "key1" && (string)kvp.Value == "value1");
|
||||
Assert.Contains(loggerProvider.Resource.Attributes, kvp => kvp.Key == "key1" && (string)kvp.Value == "value1");
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
@ -326,6 +327,129 @@ public class OpenTelemetryServicesExtensionsTests
|
|||
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 loggerProviders = serviceProvider.GetServices<LoggerProvider>();
|
||||
|
||||
Assert.Single(loggerProviders);
|
||||
}
|
||||
|
||||
[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)
|
||||
|
|
|
|||
Loading…
Reference in New Issue