[sdk] Use reflection and dynamic types to start providers (#4151)
* Remove StartWithHost extension and using reflection to do the work in SDK. * Warning cleanup. * Fix up. * Improved logging. * Remove OpenTelemetry.Extensions.Hosting dependency from examples & docs. * Test fixes. * Fixes and test updates. * Bug fix. * Patch CHANGELOGs. * Clean up. * Support Azure Functions TelemetryHostedService dependency inspection logic. * Code review. * Code review. * Code review. --------- Co-authored-by: Cijo Thomas <cijo.thomas@gmail.com>
This commit is contained in:
parent
1a9a492e02
commit
b549e12148
|
|
@ -37,7 +37,8 @@
|
|||
<MicrosoftCodeCoveragePkgVer>[17.4.1]</MicrosoftCodeCoveragePkgVer>
|
||||
<MicrosoftExtensionsDependencyInjectionPkgVer>[3.1.0,)</MicrosoftExtensionsDependencyInjectionPkgVer>
|
||||
<MicrosoftExtensionsDependencyInjectionAbstractionsPkgVer>$(MicrosoftExtensionsDependencyInjectionPkgVer)</MicrosoftExtensionsDependencyInjectionAbstractionsPkgVer>
|
||||
<MicrosoftExtensionsHostingAbstractionsPkgVer>[2.1.0,)</MicrosoftExtensionsHostingAbstractionsPkgVer>
|
||||
<MicrosoftExtensionsHostingPkgVer>[2.1.0,)</MicrosoftExtensionsHostingPkgVer>
|
||||
<MicrosoftExtensionsHostingAbstractionsPkgVer>$(MicrosoftExtensionsHostingPkgVer)</MicrosoftExtensionsHostingAbstractionsPkgVer>
|
||||
<MicrosoftExtensionsLoggingPkgVer>[3.1.0,)</MicrosoftExtensionsLoggingPkgVer>
|
||||
<MicrosoftExtensionsLoggingConfigurationPkgVer>$(MicrosoftExtensionsLoggingPkgVer)</MicrosoftExtensionsLoggingConfigurationPkgVer>
|
||||
<MicrosoftExtensionsOptionsPkgVer>[3.1.0,)</MicrosoftExtensionsOptionsPkgVer>
|
||||
|
|
|
|||
|
|
@ -27,8 +27,7 @@ appBuilder.Services.AddOpenTelemetry()
|
|||
.AddService(serviceName: "OTel.NET Getting Started"))
|
||||
.WithTracing(builder => builder
|
||||
.AddAspNetCoreInstrumentation()
|
||||
.AddConsoleExporter())
|
||||
.StartWithHost();
|
||||
.AddConsoleExporter());
|
||||
|
||||
var app = appBuilder.Build();
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@
|
|||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="$(RepoRoot)\src\OpenTelemetry.Exporter.Console\OpenTelemetry.Exporter.Console.csproj" />
|
||||
<ProjectReference Include="$(RepoRoot)\src\OpenTelemetry.Extensions.Hosting\OpenTelemetry.Extensions.Hosting.csproj" />
|
||||
<ProjectReference Include="$(RepoRoot)\src\OpenTelemetry.Instrumentation.AspNetCore\OpenTelemetry.Instrumentation.AspNetCore.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="$(RepoRoot)\src\OpenTelemetry.Extensions.Hosting\OpenTelemetry.Extensions.Hosting.csproj" />
|
||||
<ProjectReference Include="$(RepoRoot)\src\OpenTelemetry.Exporter.Console\OpenTelemetry.Exporter.Console.csproj" />
|
||||
<ProjectReference Include="$(RepoRoot)\src\OpenTelemetry.Exporter.OpenTelemetryProtocol\OpenTelemetry.Exporter.OpenTelemetryProtocol.csproj" />
|
||||
<ProjectReference Include="$(RepoRoot)\src\OpenTelemetry.Exporter.OpenTelemetryProtocol.Logs\OpenTelemetry.Exporter.OpenTelemetryProtocol.Logs.csproj" />
|
||||
|
|
|
|||
|
|
@ -44,8 +44,7 @@ Action<ResourceBuilder> configureResource = r => r.AddService(
|
|||
// for manual instrumentation
|
||||
appBuilder.Services.AddSingleton<Instrumentation>();
|
||||
|
||||
// Configure OpenTelemetry tracing & metrics with auto-start using the
|
||||
// StartWithHost extension from OpenTelemetry.Extensions.Hosting.
|
||||
// Configure OpenTelemetry tracing & metrics.
|
||||
appBuilder.Services.AddOpenTelemetry()
|
||||
.ConfigureResource(configureResource)
|
||||
.WithTracing(builder =>
|
||||
|
|
@ -127,8 +126,7 @@ appBuilder.Services.AddOpenTelemetry()
|
|||
builder.AddConsoleExporter();
|
||||
break;
|
||||
}
|
||||
})
|
||||
.StartWithHost();
|
||||
});
|
||||
|
||||
// Clear default logging providers used by WebApplication host.
|
||||
appBuilder.Logging.ClearProviders();
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@
|
|||
<ProjectReference Include="$(RepoRoot)\src\OpenTelemetry.Exporter.Console\OpenTelemetry.Exporter.Console.csproj" />
|
||||
<ProjectReference Include="$(RepoRoot)\src\OpenTelemetry.Exporter.Jaeger\OpenTelemetry.Exporter.Jaeger.csproj" />
|
||||
<ProjectReference Include="$(RepoRoot)\src\OpenTelemetry.Exporter.Zipkin\OpenTelemetry.Exporter.Zipkin.csproj" />
|
||||
<ProjectReference Include="$(RepoRoot)\src\OpenTelemetry.Extensions.Hosting\OpenTelemetry.Extensions.Hosting.csproj" />
|
||||
<ProjectReference Include="$(RepoRoot)\src\OpenTelemetry.Instrumentation.AspNetCore\OpenTelemetry.Instrumentation.AspNetCore.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
|
|
|
|||
|
|
@ -61,8 +61,7 @@ namespace Examples.GrpcService
|
|||
builder.AddConsoleExporter();
|
||||
break;
|
||||
}
|
||||
})
|
||||
.StartWithHost();
|
||||
});
|
||||
}
|
||||
|
||||
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
|
||||
|
|
|
|||
|
|
@ -43,8 +43,7 @@ namespace WebApi
|
|||
{
|
||||
var zipkinHostName = Environment.GetEnvironmentVariable("ZIPKIN_HOSTNAME") ?? "localhost";
|
||||
b.Endpoint = new Uri($"http://{zipkinHostName}:9411/api/v2/spans");
|
||||
}))
|
||||
.StartWithHost();
|
||||
}));
|
||||
}
|
||||
|
||||
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@
|
|||
<ItemGroup>
|
||||
<ProjectReference Include="$(RepoRoot)\src\OpenTelemetry.Instrumentation.AspNetCore\OpenTelemetry.Instrumentation.AspNetCore.csproj" />
|
||||
<ProjectReference Include="$(RepoRoot)\src\OpenTelemetry.Exporter.Zipkin\OpenTelemetry.Exporter.Zipkin.csproj" />
|
||||
<ProjectReference Include="$(RepoRoot)\src\OpenTelemetry.Extensions.Hosting\OpenTelemetry.Extensions.Hosting.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
|||
|
|
@ -42,8 +42,7 @@ namespace WorkerService
|
|||
{
|
||||
var zipkinHostName = Environment.GetEnvironmentVariable("ZIPKIN_HOSTNAME") ?? "localhost";
|
||||
b.Endpoint = new Uri($"http://{zipkinHostName}:9411/api/v2/spans");
|
||||
}))
|
||||
.StartWithHost();
|
||||
}));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@
|
|||
<ItemGroup>
|
||||
<ProjectReference Include="$(RepoRoot)\src\OpenTelemetry\OpenTelemetry.csproj" />
|
||||
<ProjectReference Include="$(RepoRoot)\src\OpenTelemetry.Exporter.Zipkin\OpenTelemetry.Exporter.Zipkin.csproj" />
|
||||
<ProjectReference Include="$(RepoRoot)\src\OpenTelemetry.Extensions.Hosting\OpenTelemetry.Extensions.Hosting.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
|||
|
|
@ -98,8 +98,7 @@ services.AddOpenTelemetry()
|
|||
client.DefaultRequestHeaders.Add("X-MyCustomHeader", "value");
|
||||
return client;
|
||||
};
|
||||
}))
|
||||
.StartWithHost();
|
||||
}));
|
||||
```
|
||||
|
||||
For users using
|
||||
|
|
|
|||
|
|
@ -135,8 +135,7 @@ services.AddOpenTelemetry()
|
|||
client.DefaultRequestHeaders.Add("X-MyCustomHeader", "value");
|
||||
return client;
|
||||
};
|
||||
}))
|
||||
.StartWithHost();
|
||||
}));
|
||||
```
|
||||
|
||||
For users using
|
||||
|
|
|
|||
|
|
@ -28,8 +28,7 @@ dotnet add package --prerelease OpenTelemetry.Exporter.Prometheus.AspNetCore
|
|||
```csharp
|
||||
services.AddOpenTelemetry()
|
||||
.WithMetrics(builder => builder
|
||||
.AddPrometheusExporter())
|
||||
.StartWithHost();
|
||||
.AddPrometheusExporter());
|
||||
```
|
||||
|
||||
* Or configure directly:
|
||||
|
|
|
|||
|
|
@ -87,8 +87,7 @@ services.AddOpenTelemetry()
|
|||
HttpClient client = new HttpClient();
|
||||
client.DefaultRequestHeaders.Add("X-MyCustomHeader", "value");
|
||||
return client;
|
||||
}))
|
||||
.StartWithHost();
|
||||
}));
|
||||
```
|
||||
|
||||
For users using
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
Microsoft.Extensions.DependencyInjection.OpenTelemetryServicesExtensions
|
||||
OpenTelemetry.Metrics.MeterProviderBuilderExtensions
|
||||
OpenTelemetry.OpenTelemetryBuilderHostingExtensions
|
||||
OpenTelemetry.Trace.TracerProviderBuilderExtensions
|
||||
static Microsoft.Extensions.DependencyInjection.OpenTelemetryServicesExtensions.AddOpenTelemetryMetrics(this Microsoft.Extensions.DependencyInjection.IServiceCollection services) -> Microsoft.Extensions.DependencyInjection.IServiceCollection
|
||||
static Microsoft.Extensions.DependencyInjection.OpenTelemetryServicesExtensions.AddOpenTelemetryMetrics(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, System.Action<OpenTelemetry.Metrics.MeterProviderBuilder> configure) -> Microsoft.Extensions.DependencyInjection.IServiceCollection
|
||||
|
|
@ -8,6 +7,5 @@ static Microsoft.Extensions.DependencyInjection.OpenTelemetryServicesExtensions.
|
|||
static Microsoft.Extensions.DependencyInjection.OpenTelemetryServicesExtensions.AddOpenTelemetryTracing(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, System.Action<OpenTelemetry.Trace.TracerProviderBuilder> configure) -> Microsoft.Extensions.DependencyInjection.IServiceCollection
|
||||
static OpenTelemetry.Metrics.MeterProviderBuilderExtensions.Configure(this OpenTelemetry.Metrics.MeterProviderBuilder meterProviderBuilder, System.Action<System.IServiceProvider, OpenTelemetry.Metrics.MeterProviderBuilder> configure) -> OpenTelemetry.Metrics.MeterProviderBuilder
|
||||
static OpenTelemetry.Metrics.MeterProviderBuilderExtensions.GetServices(this OpenTelemetry.Metrics.MeterProviderBuilder meterProviderBuilder) -> Microsoft.Extensions.DependencyInjection.IServiceCollection
|
||||
static OpenTelemetry.OpenTelemetryBuilderHostingExtensions.StartWithHost(this OpenTelemetry.OpenTelemetryBuilder builder) -> OpenTelemetry.OpenTelemetryBuilder
|
||||
static OpenTelemetry.Trace.TracerProviderBuilderExtensions.Configure(this OpenTelemetry.Trace.TracerProviderBuilder tracerProviderBuilder, System.Action<System.IServiceProvider, OpenTelemetry.Trace.TracerProviderBuilder> configure) -> OpenTelemetry.Trace.TracerProviderBuilder
|
||||
static OpenTelemetry.Trace.TracerProviderBuilderExtensions.GetServices(this OpenTelemetry.Trace.TracerProviderBuilder tracerProviderBuilder) -> Microsoft.Extensions.DependencyInjection.IServiceCollection
|
||||
|
|
|
|||
|
|
@ -2,6 +2,12 @@
|
|||
|
||||
## Unreleased
|
||||
|
||||
* Removed the `OpenTelemetryBuilder.StartWithHost` extension and moved the
|
||||
functionality into the SDK `AddOpenTelemetry` extension. With this change
|
||||
`OpenTelemetry.Extensions.Hosting` is no longer needed and will be marked as
|
||||
deprecated.
|
||||
([#4151](https://github.com/open-telemetry/opentelemetry-dotnet/pull/4151))
|
||||
|
||||
## 1.4.0-rc.3
|
||||
|
||||
Released 2023-Feb-01
|
||||
|
|
|
|||
|
|
@ -1,41 +0,0 @@
|
|||
// <copyright file="HostingExtensionsEventSource.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 System.Diagnostics.Tracing;
|
||||
|
||||
namespace OpenTelemetry.Extensions.Hosting.Implementation
|
||||
{
|
||||
/// <summary>
|
||||
/// EventSource events emitted from the project.
|
||||
/// </summary>
|
||||
[EventSource(Name = "OpenTelemetry-Extensions-Hosting")]
|
||||
internal sealed class HostingExtensionsEventSource : EventSource
|
||||
{
|
||||
public static HostingExtensionsEventSource Log = new();
|
||||
|
||||
[Event(1, Message = "OpenTelemetry TracerProvider was not found in application services. Tracing will remain disabled.", Level = EventLevel.Warning)]
|
||||
public void TracerProviderNotRegistered()
|
||||
{
|
||||
this.WriteEvent(1);
|
||||
}
|
||||
|
||||
[Event(2, Message = "OpenTelemetry MeterProvider was not found in application services. Metrics will remain disabled.", Level = EventLevel.Warning)]
|
||||
public void MeterProviderNotRegistered()
|
||||
{
|
||||
this.WriteEvent(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,64 +0,0 @@
|
|||
// <copyright file="TelemetryHostedService.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 System.Diagnostics;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using OpenTelemetry.Metrics;
|
||||
using OpenTelemetry.Trace;
|
||||
|
||||
namespace OpenTelemetry.Extensions.Hosting.Implementation;
|
||||
|
||||
internal sealed class TelemetryHostedService : IHostedService
|
||||
{
|
||||
private readonly IServiceProvider serviceProvider;
|
||||
|
||||
public TelemetryHostedService(IServiceProvider serviceProvider)
|
||||
{
|
||||
this.serviceProvider = serviceProvider;
|
||||
}
|
||||
|
||||
public Task StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
// The sole purpose of this HostedService is to ensure all
|
||||
// instrumentations, exporters, etc., are created and started.
|
||||
Initialize(this.serviceProvider);
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task StopAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
internal static void Initialize(IServiceProvider serviceProvider)
|
||||
{
|
||||
Debug.Assert(serviceProvider != null, "serviceProvider was null");
|
||||
|
||||
var meterProvider = serviceProvider.GetService<MeterProvider>();
|
||||
if (meterProvider == null)
|
||||
{
|
||||
HostingExtensionsEventSource.Log.MeterProviderNotRegistered();
|
||||
}
|
||||
|
||||
var tracerProvider = serviceProvider.GetService<TracerProvider>();
|
||||
if (tracerProvider == null)
|
||||
{
|
||||
HostingExtensionsEventSource.Log.TracerProviderNotRegistered();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
// <copyright file="OpenTelemetryBuilderHostingExtensions.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 Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using OpenTelemetry.Extensions.Hosting.Implementation;
|
||||
using OpenTelemetry.Internal;
|
||||
|
||||
namespace OpenTelemetry;
|
||||
|
||||
/// <summary>
|
||||
/// Contains hosting extension methods for the <see
|
||||
/// cref="OpenTelemetryBuilder"/> class.
|
||||
/// </summary>
|
||||
public static class OpenTelemetryBuilderHostingExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Registers an <see cref="IHostedService"/> to automatically start all
|
||||
/// configured OpenTelemetry services in the supplied <see
|
||||
/// cref="IServiceCollection" />.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Note: This is safe to be called multiple times. Only a single <see
|
||||
/// cref="IHostedService"/> will be created for a given <see
|
||||
/// cref="IServiceCollection"/>. This should generally be called by hosting
|
||||
/// application code and NOT library authors.
|
||||
/// </remarks>
|
||||
/// <param name="builder"><see cref="OpenTelemetryBuilder"/>.</param>
|
||||
/// <returns>The supplied <see cref="OpenTelemetryBuilder"/> for chaining
|
||||
/// calls.</returns>
|
||||
public static OpenTelemetryBuilder StartWithHost(this OpenTelemetryBuilder builder)
|
||||
{
|
||||
Guard.ThrowIfNull(builder);
|
||||
|
||||
builder.Services.TryAddEnumerable(
|
||||
ServiceDescriptor.Singleton<IHostedService, TelemetryHostedService>());
|
||||
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
|
|
@ -51,7 +51,7 @@ public static class OpenTelemetryServicesExtensions
|
|||
/// <param name="services"><see cref="IServiceCollection"/>.</param>
|
||||
/// <returns>Supplied <see cref="IServiceCollection"/> for chaining
|
||||
/// calls.</returns>
|
||||
[Obsolete("Use the AddOpenTelemetry().WithTracing(configure).StartWithHost() pattern instead. This method will be removed in a future version.")]
|
||||
[Obsolete("Use the AddOpenTelemetry().WithTracing(configure) pattern instead. This method will be removed in a future version.")]
|
||||
public static IServiceCollection AddOpenTelemetryTracing(this IServiceCollection services)
|
||||
=> AddOpenTelemetryTracing(services, b => { });
|
||||
|
||||
|
|
@ -68,10 +68,10 @@ public static class OpenTelemetryServicesExtensions
|
|||
/// cref="TracerProviderBuilder"/>.</param>
|
||||
/// <returns>Supplied <see cref="IServiceCollection"/> for chaining
|
||||
/// calls.</returns>
|
||||
[Obsolete("Use the AddOpenTelemetry().WithTracing(configure).StartWithHost() pattern instead. This method will be removed in a future version.")]
|
||||
[Obsolete("Use the AddOpenTelemetry().WithTracing(configure) pattern instead. This method will be removed in a future version.")]
|
||||
public static IServiceCollection AddOpenTelemetryTracing(this IServiceCollection services, Action<TracerProviderBuilder> configure)
|
||||
{
|
||||
services.AddOpenTelemetry().WithTracing(configure).StartWithHost();
|
||||
services.AddOpenTelemetry().WithTracing(configure);
|
||||
|
||||
return services;
|
||||
}
|
||||
|
|
@ -100,7 +100,7 @@ public static class OpenTelemetryServicesExtensions
|
|||
/// <param name="services"><see cref="IServiceCollection"/>.</param>
|
||||
/// <returns>Supplied <see cref="IServiceCollection"/> for chaining
|
||||
/// calls.</returns>
|
||||
[Obsolete("Use the AddOpenTelemetry().WithMetrics(configure).StartWithHost() pattern instead. This method will be removed in a future version.")]
|
||||
[Obsolete("Use the AddOpenTelemetry().WithMetrics(configure) pattern instead. This method will be removed in a future version.")]
|
||||
public static IServiceCollection AddOpenTelemetryMetrics(this IServiceCollection services)
|
||||
=> AddOpenTelemetryMetrics(services, b => { });
|
||||
|
||||
|
|
@ -117,10 +117,10 @@ public static class OpenTelemetryServicesExtensions
|
|||
/// cref="TracerProviderBuilder"/>.</param>
|
||||
/// <returns>Supplied <see cref="IServiceCollection"/> for chaining
|
||||
/// calls.</returns>
|
||||
[Obsolete("Use the AddOpenTelemetry().WithMetrics(configure).StartWithHost() pattern instead. This method will be removed in a future version.")]
|
||||
[Obsolete("Use the AddOpenTelemetry().WithMetrics(configure) pattern instead. This method will be removed in a future version.")]
|
||||
public static IServiceCollection AddOpenTelemetryMetrics(this IServiceCollection services, Action<MeterProviderBuilder> configure)
|
||||
{
|
||||
services.AddOpenTelemetry().WithMetrics(configure).StartWithHost();
|
||||
services.AddOpenTelemetry().WithMetrics(configure);
|
||||
|
||||
return services;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,17 +21,6 @@ and metrics (`MeterProvider`) in [ASP.NET
|
|||
|
||||
## Extension method reference
|
||||
|
||||
### Current OpenTelemetry SDK v1.4.0 and newer extensions
|
||||
|
||||
Targeting `OpenTelemetry.OpenTelemetryBuilder`:
|
||||
|
||||
* `StartWithHost`: Registers an
|
||||
[IHostedService](https://learn.microsoft.com/dotnet/api/microsoft.extensions.hosting.ihostedservice)
|
||||
to automatically start tracing and/or metric services in the supplied
|
||||
[IServiceCollection](https://learn.microsoft.com/dotnet/api/microsoft.extensions.dependencyinjection.iservicecollection).
|
||||
|
||||
### Obsolete OpenTelemetry SDK pre-1.4.0 extensions
|
||||
|
||||
> **Note**
|
||||
> The below extension methods should be called by application host code
|
||||
only. Library authors see: [Registration extension method guidance for library
|
||||
|
|
@ -72,10 +61,9 @@ using OpenTelemetry.Trace;
|
|||
|
||||
var appBuilder = WebApplication.CreateBuilder(args);
|
||||
|
||||
appBuilder.Services.AddOpenTelemetry()
|
||||
.WithTracing(builder => builder.AddConsoleExporter())
|
||||
.WithMetrics(builder => builder.AddConsoleExporter())
|
||||
.StartWithHost();
|
||||
appBuilder.Services.AddOpenTelemetryTracing(builder => builder.AddConsoleExporter());
|
||||
|
||||
appBuilder.Services.AddOpenTelemetryMetrics(builder => builder.AddConsoleExporter());
|
||||
|
||||
var app = appBuilder.Build();
|
||||
|
||||
|
|
|
|||
|
|
@ -58,8 +58,7 @@ public void ConfigureServices(IServiceCollection services)
|
|||
services.AddOpenTelemetry()
|
||||
.WithTracing(builder => builder
|
||||
.AddAspNetCoreInstrumentation()
|
||||
.AddJaegerExporter())
|
||||
.StartWithHost();
|
||||
.AddJaegerExporter());
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -88,8 +87,7 @@ services.Configure<AspNetCoreInstrumentationOptions>(options =>
|
|||
services.AddOpenTelemetry()
|
||||
.WithTracing(builder => builder
|
||||
.AddAspNetCoreInstrumentation()
|
||||
.AddJaegerExporter())
|
||||
.StartWithHost();
|
||||
.AddJaegerExporter());
|
||||
```
|
||||
|
||||
### Filter
|
||||
|
|
@ -112,8 +110,7 @@ services.AddOpenTelemetry()
|
|||
// only collect telemetry about HTTP GET requests
|
||||
return httpContext.Request.Method.Equals("GET");
|
||||
})
|
||||
.AddJaegerExporter())
|
||||
.StartWithHost();
|
||||
.AddJaegerExporter());
|
||||
```
|
||||
|
||||
It is important to note that this `Filter` option is specific to this
|
||||
|
|
@ -150,8 +147,7 @@ services.AddOpenTelemetry()
|
|||
{
|
||||
activity.SetTag("exceptionType", exception.GetType().ToString());
|
||||
};
|
||||
}))
|
||||
.StartWithHost();
|
||||
}));
|
||||
```
|
||||
|
||||
[Processor](../../docs/trace/extending-the-sdk/README.md#processor),
|
||||
|
|
|
|||
|
|
@ -120,8 +120,7 @@ services.AddOpenTelemetry()
|
|||
{
|
||||
activity.SetTag("responseVersion", httpResponseMessage.Version);
|
||||
};
|
||||
})
|
||||
.StartWithHost();
|
||||
});
|
||||
```
|
||||
|
||||
[Processor](../../docs/trace/extending-the-sdk/README.md#processor),
|
||||
|
|
|
|||
|
|
@ -5,6 +5,10 @@
|
|||
* Removed the dependency on System.Reflection.Emit.Lightweight
|
||||
([#4140](https://github.com/open-telemetry/opentelemetry-dotnet/pull/4140))
|
||||
|
||||
* The `AddOpenTelemetry` extension will now register an `IHostedService` if
|
||||
`Microsoft.Extensions.Hosting.Abstractions` is detected.
|
||||
([#4151](https://github.com/open-telemetry/opentelemetry-dotnet/pull/4151))
|
||||
|
||||
## 1.4.0-rc.3
|
||||
|
||||
Released 2023-Feb-01
|
||||
|
|
|
|||
|
|
@ -4,49 +4,24 @@
|
|||
|
||||
#nullable enable
|
||||
|
||||
using System;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Configuration.EnvironmentVariables;
|
||||
|
||||
namespace Microsoft.Extensions.Configuration
|
||||
namespace OpenTelemetry.Internal;
|
||||
|
||||
/// <summary>
|
||||
/// Extension methods for registering <see cref="EnvironmentVariablesConfigurationProvider"/> with <see cref="IConfigurationBuilder"/>.
|
||||
/// </summary>
|
||||
internal static class EnvironmentVariablesExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Extension methods for registering <see cref="EnvironmentVariablesConfigurationProvider"/> with <see cref="IConfigurationBuilder"/>.
|
||||
/// Adds an <see cref="IConfigurationProvider"/> that reads configuration values from environment variables.
|
||||
/// </summary>
|
||||
internal static class EnvironmentVariablesExtensions
|
||||
/// <param name="configurationBuilder">The <see cref="IConfigurationBuilder"/> to add to.</param>
|
||||
/// <returns>The <see cref="IConfigurationBuilder"/>.</returns>
|
||||
public static IConfigurationBuilder AddEnvironmentVariables(this IConfigurationBuilder configurationBuilder)
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds an <see cref="IConfigurationProvider"/> that reads configuration values from environment variables.
|
||||
/// </summary>
|
||||
/// <param name="configurationBuilder">The <see cref="IConfigurationBuilder"/> to add to.</param>
|
||||
/// <returns>The <see cref="IConfigurationBuilder"/>.</returns>
|
||||
public static IConfigurationBuilder AddEnvironmentVariables(this IConfigurationBuilder configurationBuilder)
|
||||
{
|
||||
configurationBuilder.Add(new EnvironmentVariablesConfigurationSource());
|
||||
return configurationBuilder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds an <see cref="IConfigurationProvider"/> that reads configuration values from environment variables
|
||||
/// with a specified prefix.
|
||||
/// </summary>
|
||||
/// <param name="configurationBuilder">The <see cref="IConfigurationBuilder"/> to add to.</param>
|
||||
/// <param name="prefix">The prefix that environment variable names must start with. The prefix will be removed from the environment variable names.</param>
|
||||
/// <returns>The <see cref="IConfigurationBuilder"/>.</returns>
|
||||
public static IConfigurationBuilder AddEnvironmentVariables(
|
||||
this IConfigurationBuilder configurationBuilder,
|
||||
string? prefix)
|
||||
{
|
||||
configurationBuilder.Add(new EnvironmentVariablesConfigurationSource { Prefix = prefix });
|
||||
return configurationBuilder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds an <see cref="IConfigurationProvider"/> that reads configuration values from environment variables.
|
||||
/// </summary>
|
||||
/// <param name="builder">The <see cref="IConfigurationBuilder"/> to add to.</param>
|
||||
/// <param name="configureSource">Configures the source.</param>
|
||||
/// <returns>The <see cref="IConfigurationBuilder"/>.</returns>
|
||||
public static IConfigurationBuilder AddEnvironmentVariables(this IConfigurationBuilder builder, Action<EnvironmentVariablesConfigurationSource>? configureSource)
|
||||
=> builder.Add(configureSource);
|
||||
configurationBuilder.Add(new EnvironmentVariablesConfigurationSource());
|
||||
return configurationBuilder;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,253 @@
|
|||
// <copyright file="HostingHelper.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>
|
||||
|
||||
#nullable enable
|
||||
|
||||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
using OpenTelemetry.Metrics;
|
||||
using OpenTelemetry.Trace;
|
||||
|
||||
namespace OpenTelemetry.Internal;
|
||||
|
||||
internal static class HostingHelper
|
||||
{
|
||||
private static readonly object LockObject = new();
|
||||
private static bool initialized;
|
||||
private static Type? hostedServiceImplementation;
|
||||
|
||||
public static void AddOpenTelemetryHostedServiceIntoServiceCollection(IServiceCollection services)
|
||||
{
|
||||
if (TryAddOpenTelemetryHostedServiceIntoServiceCollection(services, out var reason))
|
||||
{
|
||||
OpenTelemetrySdkEventSource.Log.HostedServiceRegistered();
|
||||
}
|
||||
else
|
||||
{
|
||||
OpenTelemetrySdkEventSource.Log.HostedServiceRegistrationSkipped(reason);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool TryAddOpenTelemetryHostedServiceIntoServiceCollection(IServiceCollection services, out string? reason)
|
||||
{
|
||||
#if NETSTANDARD2_1_OR_GREATER || NET6_0_OR_GREATER
|
||||
bool isDynamicCodeSupported = RuntimeFeature.IsDynamicCodeSupported;
|
||||
#else
|
||||
// Note: This is for .NET Framework and/or .NET Standard 2.0 targets.
|
||||
bool isDynamicCodeSupported = true;
|
||||
#endif
|
||||
if (!isDynamicCodeSupported)
|
||||
{
|
||||
reason = "Dynamic code not supported";
|
||||
return false;
|
||||
}
|
||||
|
||||
var iHostedServiceType = Type.GetType(
|
||||
"Microsoft.Extensions.Hosting.IHostedService, Microsoft.Extensions.Hosting.Abstractions", throwOnError: false);
|
||||
|
||||
if (iHostedServiceType == null)
|
||||
{
|
||||
reason = "Microsoft.Extensions.Hosting.IHostedService not found";
|
||||
return false;
|
||||
}
|
||||
|
||||
lock (LockObject)
|
||||
{
|
||||
if (!initialized)
|
||||
{
|
||||
try
|
||||
{
|
||||
hostedServiceImplementation = CreateHostedServiceImplementation(iHostedServiceType);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
OpenTelemetrySdkEventSource.Log.HostedServiceRegistrationFailure(ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
initialized = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hostedServiceImplementation == null)
|
||||
{
|
||||
reason = "Initialization failure";
|
||||
return false;
|
||||
}
|
||||
|
||||
services.TryAddSingleton<TelemetryHostedServiceHelper>();
|
||||
services.TryAddEnumerable(ServiceDescriptor.Singleton(iHostedServiceType, hostedServiceImplementation));
|
||||
|
||||
reason = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
private static Type CreateHostedServiceImplementation(Type iHostedServiceType)
|
||||
{
|
||||
/*
|
||||
* Note: This code builds a class dynamically that does this...
|
||||
*
|
||||
* namespace OpenTelemetry.Extensions.Hosting.Implementation;
|
||||
*
|
||||
* class TelemetryHostedService : IHostedService
|
||||
* {
|
||||
* private readonly TelemetryHostedServiceHelper telemetryHostedServiceHelper;
|
||||
*
|
||||
* public TelemetryHostedService(TelemetryHostedServiceHelper telemetryHostedServiceHelper)
|
||||
* {
|
||||
* this.telemetryHostedServiceHelper = telemetryHostedServiceHelper;
|
||||
* }
|
||||
*
|
||||
* public Task StartAsync(CancellationToken cancellationToken)
|
||||
* {
|
||||
* this.telemetryHostedServiceHelper.Start();
|
||||
* return Task.CompletedTask;
|
||||
* }
|
||||
*
|
||||
* public Task StopAsync(CancellationToken cancellationToken)
|
||||
* {
|
||||
* return Task.CompletedTask;
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
|
||||
var getCompletedTaskMethod = typeof(Task).GetProperty(nameof(Task.CompletedTask), BindingFlags.Static | BindingFlags.Public)?.GetMethod
|
||||
?? throw new InvalidOperationException("Task.CompletedTask could not be found reflectively.");
|
||||
|
||||
// Note: It is important that the assembly is named
|
||||
// OpenTelemetry.Extensions.Hosting and the type is named
|
||||
// OpenTelemetry.Extensions.Hosting.Implementation.TelemetryHostedService
|
||||
// to preserve compatibility with Azure Functions:
|
||||
// https://github.com/Azure/azure-functions-host/blob/d4655cc4fbb34fc14e6861731991118a9acd02ed/src/WebJobs.Script.WebHost/DependencyInjection/DependencyValidator/DependencyValidator.cs#L57.
|
||||
var assemblyName = new AssemblyName("OpenTelemetry.Extensions.Hosting");
|
||||
|
||||
assemblyName.SetPublicKey(typeof(HostingHelper).Assembly.GetName().GetPublicKey());
|
||||
|
||||
var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
|
||||
|
||||
// Note: We use IgnoresAccessChecksToAttribute to allow the dynamic
|
||||
// assembly to call TelemetryHostedService which is internal to
|
||||
// OpenTelemetry.dll.
|
||||
var ignoresAccessChecksTo = new CustomAttributeBuilder(
|
||||
typeof(IgnoresAccessChecksToAttribute).GetConstructor(new Type[] { typeof(string) }) ?? throw new InvalidOperationException("IgnoresAccessChecksToAttribute constructor could not be found reflectively."),
|
||||
new object[] { typeof(TelemetryHostedServiceHelper).Assembly.GetName().Name! });
|
||||
|
||||
assemblyBuilder.SetCustomAttribute(ignoresAccessChecksTo);
|
||||
|
||||
var moduleBuilder = assemblyBuilder.DefineDynamicModule(assemblyName.Name!);
|
||||
|
||||
var typeBuilder = moduleBuilder.DefineType("OpenTelemetry.Extensions.Hosting.Implementation.TelemetryHostedService", TypeAttributes.NotPublic);
|
||||
|
||||
typeBuilder.AddInterfaceImplementation(iHostedServiceType);
|
||||
|
||||
var hostedServiceImplementationField = typeBuilder.DefineField(
|
||||
"telemetryHostedServiceHelper",
|
||||
typeof(TelemetryHostedServiceHelper),
|
||||
FieldAttributes.Private | FieldAttributes.InitOnly);
|
||||
|
||||
var constructor = typeBuilder.DefineConstructor(
|
||||
MethodAttributes.Public,
|
||||
CallingConventions.Standard,
|
||||
new Type[] { typeof(TelemetryHostedServiceHelper) });
|
||||
|
||||
var constructorGenerator = constructor.GetILGenerator();
|
||||
|
||||
constructorGenerator.Emit(OpCodes.Ldarg_0);
|
||||
constructorGenerator.Emit(
|
||||
OpCodes.Call,
|
||||
typeof(object).GetConstructor(Type.EmptyTypes) ?? throw new InvalidOperationException("Object constructor could not be found reflectively."));
|
||||
constructorGenerator.Emit(OpCodes.Ldarg_0);
|
||||
constructorGenerator.Emit(OpCodes.Ldarg_1);
|
||||
constructorGenerator.Emit(OpCodes.Stfld, hostedServiceImplementationField);
|
||||
constructorGenerator.Emit(OpCodes.Ret);
|
||||
|
||||
var startAsyncMethodBuilder = typeBuilder.DefineMethod(
|
||||
"StartAsync",
|
||||
MethodAttributes.Public | MethodAttributes.Virtual,
|
||||
typeof(Task),
|
||||
new Type[] { typeof(CancellationToken) });
|
||||
|
||||
var startAsyncMethodGenerator = startAsyncMethodBuilder.GetILGenerator();
|
||||
|
||||
startAsyncMethodGenerator.Emit(OpCodes.Ldarg_0);
|
||||
startAsyncMethodGenerator.Emit(OpCodes.Ldfld, hostedServiceImplementationField);
|
||||
startAsyncMethodGenerator.Emit(
|
||||
OpCodes.Call,
|
||||
typeof(TelemetryHostedServiceHelper).GetMethod(nameof(TelemetryHostedServiceHelper.Start)) ?? throw new InvalidOperationException($"{nameof(TelemetryHostedServiceHelper)}.{nameof(TelemetryHostedServiceHelper.Start)} could not be found reflectively."));
|
||||
startAsyncMethodGenerator.Emit(OpCodes.Call, getCompletedTaskMethod);
|
||||
startAsyncMethodGenerator.Emit(OpCodes.Ret);
|
||||
|
||||
typeBuilder.DefineMethodOverride(
|
||||
startAsyncMethodBuilder,
|
||||
iHostedServiceType.GetMethod("StartAsync") ?? throw new InvalidOperationException("IHostedService.StartAsync could not be found reflectively."));
|
||||
|
||||
var stopAsyncMethodBuilder = typeBuilder.DefineMethod(
|
||||
"StopAsync",
|
||||
MethodAttributes.Public | MethodAttributes.Virtual,
|
||||
typeof(Task),
|
||||
new Type[] { typeof(CancellationToken) });
|
||||
|
||||
var stopAsyncMethodGenerator = stopAsyncMethodBuilder.GetILGenerator();
|
||||
|
||||
stopAsyncMethodGenerator.Emit(OpCodes.Call, getCompletedTaskMethod);
|
||||
stopAsyncMethodGenerator.Emit(OpCodes.Ret);
|
||||
|
||||
typeBuilder.DefineMethodOverride(
|
||||
stopAsyncMethodBuilder,
|
||||
iHostedServiceType.GetMethod("StopAsync") ?? throw new InvalidOperationException("IHostedService.StopAsync could not be found reflectively."));
|
||||
|
||||
#if !NETSTANDARD2_0
|
||||
return typeBuilder.CreateType()
|
||||
#else
|
||||
return typeBuilder.CreateTypeInfo()
|
||||
#endif
|
||||
?? throw new InvalidOperationException("IHostedService implementation could not be created dynamically.");
|
||||
}
|
||||
|
||||
private sealed class TelemetryHostedServiceHelper
|
||||
{
|
||||
private readonly IServiceProvider serviceProvider;
|
||||
|
||||
public TelemetryHostedServiceHelper(IServiceProvider serviceProvider)
|
||||
{
|
||||
Debug.Assert(serviceProvider != null, "serviceProvider was null");
|
||||
|
||||
this.serviceProvider = serviceProvider!;
|
||||
}
|
||||
|
||||
public void Start()
|
||||
{
|
||||
var serviceProvider = this.serviceProvider;
|
||||
|
||||
var meterProvider = serviceProvider.GetService<MeterProvider>();
|
||||
if (meterProvider == null)
|
||||
{
|
||||
OpenTelemetrySdkEventSource.Log.MeterProviderNotRegistered();
|
||||
}
|
||||
|
||||
var tracerProvider = serviceProvider.GetService<TracerProvider>();
|
||||
if (tracerProvider == null)
|
||||
{
|
||||
OpenTelemetrySdkEventSource.Log.TracerProviderNotRegistered();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -175,6 +175,15 @@ namespace OpenTelemetry.Internal
|
|||
}
|
||||
}
|
||||
|
||||
[NonEvent]
|
||||
public void HostedServiceRegistrationFailure(Exception ex)
|
||||
{
|
||||
if (this.IsEnabled(EventLevel.Error, EventKeywords.All))
|
||||
{
|
||||
this.HostedServiceRegistrationFailure(ex.ToInvariantString());
|
||||
}
|
||||
}
|
||||
|
||||
[Event(1, Message = "Span processor queue size reached maximum. Throttling spans.", Level = EventLevel.Warning)]
|
||||
public void SpanProcessorQueueIsExhausted()
|
||||
{
|
||||
|
|
@ -415,6 +424,36 @@ namespace OpenTelemetry.Internal
|
|||
this.WriteEvent(47, key, value);
|
||||
}
|
||||
|
||||
[Event(48, Message = "OpenTelemetry IHostedService registered in application services.", Level = EventLevel.Informational)]
|
||||
public void HostedServiceRegistered()
|
||||
{
|
||||
this.WriteEvent(48);
|
||||
}
|
||||
|
||||
[Event(49, Message = "OpenTelemetry IHostedService application services registration skipped. Reason: '{0}'", Level = EventLevel.Warning)]
|
||||
public void HostedServiceRegistrationSkipped(string reason)
|
||||
{
|
||||
this.WriteEvent(49, reason);
|
||||
}
|
||||
|
||||
[Event(50, Message = "OpenTelemetry IHostedService could not be registered in application services. Error: '{0}'", Level = EventLevel.Error)]
|
||||
public void HostedServiceRegistrationFailure(string error)
|
||||
{
|
||||
this.WriteEvent(50, error);
|
||||
}
|
||||
|
||||
[Event(51, Message = "OpenTelemetry TracerProvider was not found in application services. Tracing will remain disabled.", Level = EventLevel.Warning)]
|
||||
public void TracerProviderNotRegistered()
|
||||
{
|
||||
this.WriteEvent(51);
|
||||
}
|
||||
|
||||
[Event(52, Message = "OpenTelemetry MeterProvider was not found in application services. Metrics will remain disabled.", Level = EventLevel.Warning)]
|
||||
public void MeterProviderNotRegistered()
|
||||
{
|
||||
this.WriteEvent(52);
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
public class OpenTelemetryEventListener : EventListener
|
||||
{
|
||||
|
|
|
|||
|
|
@ -0,0 +1,33 @@
|
|||
// <copyright file="IgnoresAccessChecksToAttribute.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>
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace System.Runtime.CompilerServices;
|
||||
|
||||
/// <summary>
|
||||
/// IgnoresAccessChecksToAttribute tells the runtime to bypass visibility checks
|
||||
/// to some assembly. See: <see
|
||||
/// href="https://github.com/dotnet/runtime/discussions/48024" />.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
|
||||
internal sealed class IgnoresAccessChecksToAttribute : Attribute
|
||||
{
|
||||
public IgnoresAccessChecksToAttribute(string assemblyName)
|
||||
=> this.AssemblyName = assemblyName;
|
||||
|
||||
public string AssemblyName { get; }
|
||||
}
|
||||
|
|
@ -20,6 +20,7 @@
|
|||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Configuration" Version="$(MicrosoftExtensionsLoggingConfigurationPkgVer)" />
|
||||
<PackageReference Include="System.Reflection.Emit" Version="$(SystemReflectionEmitLightweightPkgVer)" Condition="'$(TargetFramework)' != 'net6.0'" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
|||
|
|
@ -74,18 +74,9 @@ public class OpenTelemetryBuilder
|
|||
/// Adds metric services into the builder.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Notes:
|
||||
/// <list type="bullet">
|
||||
/// <item>A <see cref="MeterProvider"/> will not be created automatically
|
||||
/// using this method. To begin collecting metrics either use the
|
||||
/// <c>OpenTelemetryBuilder.StartWithHost</c> extension in the
|
||||
/// <c>OpenTelemetry.Extensions.Hosting</c> package or access the <see
|
||||
/// cref="MeterProvider"/> through the application <see
|
||||
/// cref="IServiceProvider"/>.</item>
|
||||
/// <item>This is safe to be called multiple times and by library authors.
|
||||
/// Note: This is safe to be called multiple times and by library authors.
|
||||
/// Only a single <see cref="MeterProvider"/> will be created for a given
|
||||
/// <see cref="IServiceCollection"/>.</item>
|
||||
/// </list>
|
||||
/// <see cref="IServiceCollection"/>.
|
||||
/// </remarks>
|
||||
/// <returns>The supplied <see cref="OpenTelemetryBuilder"/> for chaining
|
||||
/// calls.</returns>
|
||||
|
|
@ -115,18 +106,9 @@ public class OpenTelemetryBuilder
|
|||
/// Adds tracing services into the builder.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Notes:
|
||||
/// <list type="bullet">
|
||||
/// <item>A <see cref="TracerProvider"/> will not be created automatically
|
||||
/// using this method. To begin collecting traces either use the
|
||||
/// <c>OpenTelemetryBuilder.StartWithHost</c> extension in the
|
||||
/// <c>OpenTelemetry.Extensions.Hosting</c> package or access the <see
|
||||
/// cref="TracerProvider"/> through the application <see
|
||||
/// cref="IServiceProvider"/>.</item>
|
||||
/// <item>This is safe to be called multiple times and by library authors.
|
||||
/// Note: This is safe to be called multiple times and by library authors.
|
||||
/// Only a single <see cref="TracerProvider"/> will be created for a given
|
||||
/// <see cref="IServiceCollection"/>.</item>
|
||||
/// </list>
|
||||
/// <see cref="IServiceCollection"/>.
|
||||
/// </remarks>
|
||||
/// <returns>The supplied <see cref="OpenTelemetryBuilder"/> for chaining
|
||||
/// calls.</returns>
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
#nullable enable
|
||||
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using OpenTelemetry.Internal;
|
||||
using OpenTelemetry.Metrics;
|
||||
using OpenTelemetry.Trace;
|
||||
|
||||
|
|
@ -35,12 +36,12 @@ public static class OpenTelemetryServiceCollectionExtensions
|
|||
/// Notes:
|
||||
/// <list type="bullet">
|
||||
/// <item>A <see cref="TracerProvider"/> and/or <see cref="MeterProvider"/>
|
||||
/// will not be created automatically using this method. To begin collecting
|
||||
/// traces and/or metrics either use the
|
||||
/// <c>OpenTelemetryBuilder.StartWithHost</c> extension in the
|
||||
/// <c>OpenTelemetry.Extensions.Hosting</c> package or access the <see
|
||||
/// cref="TracerProvider"/> and/or <see cref="MeterProvider"/> through the
|
||||
/// application <see cref="IServiceProvider"/>.</item>
|
||||
/// will be created automatically using this method if a host supporting
|
||||
/// <c>Microsoft.Extensions.Hosting.IHostedService</c> is detected at
|
||||
/// runtime. To begin collecting traces and/or metrics when hosting
|
||||
/// extensions are not being used access the <see cref="TracerProvider"/>
|
||||
/// and/or <see cref="MeterProvider"/> through the application <see
|
||||
/// cref="IServiceProvider"/>.</item>
|
||||
/// <item>This is safe to be called multiple times and by library authors.
|
||||
/// Only a single <see cref="TracerProvider"/> and/or <see
|
||||
/// cref="MeterProvider"/> will be created for a given <see
|
||||
|
|
@ -51,5 +52,9 @@ public static class OpenTelemetryServiceCollectionExtensions
|
|||
/// <returns>The supplied <see cref="OpenTelemetryBuilder"/> for chaining
|
||||
/// calls.</returns>
|
||||
public static OpenTelemetryBuilder AddOpenTelemetry(this IServiceCollection services)
|
||||
=> new(services);
|
||||
{
|
||||
HostingHelper.AddOpenTelemetryHostedServiceIntoServiceCollection(services);
|
||||
|
||||
return new(services);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,31 +0,0 @@
|
|||
// <copyright file="EventSourceTest.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 OpenTelemetry.Extensions.Hosting.Implementation;
|
||||
using OpenTelemetry.Tests;
|
||||
using Xunit;
|
||||
|
||||
namespace OpenTelemetry.Extensions.Hosting.Tests
|
||||
{
|
||||
public class EventSourceTest
|
||||
{
|
||||
[Fact]
|
||||
public void EventSourceTest_HostingExtensionsEventSource()
|
||||
{
|
||||
EventSourceTestHelper.MethodsAreImplementedConsistentlyWithTheirAttributes(HostingExtensionsEventSource.Log);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,109 +0,0 @@
|
|||
// <copyright file="HostingMeterExtensionTests.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.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using OpenTelemetry.Metrics;
|
||||
using Xunit;
|
||||
|
||||
namespace OpenTelemetry.Extensions.Hosting.Tests
|
||||
{
|
||||
public class HostingMeterExtensionTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task AddOpenTelemetry_StartWithHost_CreationAndDisposal()
|
||||
{
|
||||
var callbackRun = false;
|
||||
|
||||
var builder = new HostBuilder().ConfigureServices(services =>
|
||||
{
|
||||
services.AddOpenTelemetry()
|
||||
.WithMetrics(builder => builder
|
||||
.AddInstrumentation(() =>
|
||||
{
|
||||
callbackRun = true;
|
||||
return new object();
|
||||
}))
|
||||
.StartWithHost();
|
||||
});
|
||||
|
||||
var host = builder.Build();
|
||||
|
||||
Assert.False(callbackRun);
|
||||
|
||||
await host.StartAsync().ConfigureAwait(false);
|
||||
|
||||
Assert.True(callbackRun);
|
||||
|
||||
await host.StopAsync().ConfigureAwait(false);
|
||||
|
||||
Assert.True(callbackRun);
|
||||
|
||||
host.Dispose();
|
||||
|
||||
Assert.True(callbackRun);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AddOpenTelemetry_StartWithHost_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()
|
||||
.WithMetrics(builder =>
|
||||
{
|
||||
if (builder is IDeferredMeterProviderBuilder deferredMeterProviderBuilder)
|
||||
{
|
||||
deferredMeterProviderBuilder.Configure((sp, builder) =>
|
||||
{
|
||||
configureBuilderCalled = true;
|
||||
|
||||
var configuration = sp.GetRequiredService<IConfiguration>();
|
||||
|
||||
var testKeyValue = configuration.GetValue<string>("TEST_KEY", null);
|
||||
|
||||
Assert.Equal("TEST_KEY_VALUE", testKeyValue);
|
||||
});
|
||||
}
|
||||
})
|
||||
.StartWithHost();
|
||||
});
|
||||
|
||||
var host = builder.Build();
|
||||
|
||||
Assert.False(configureBuilderCalled);
|
||||
|
||||
await host.StartAsync().ConfigureAwait(false);
|
||||
|
||||
Assert.True(configureBuilderCalled);
|
||||
|
||||
await host.StopAsync().ConfigureAwait(false);
|
||||
|
||||
host.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,109 +0,0 @@
|
|||
// <copyright file="HostingTracerExtensionTests.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.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using OpenTelemetry.Trace;
|
||||
using Xunit;
|
||||
|
||||
namespace OpenTelemetry.Extensions.Hosting.Tests
|
||||
{
|
||||
public class HostingTracerExtensionTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task AddOpenTelemetry_StartWithHost_CreationAndDisposal()
|
||||
{
|
||||
var callbackRun = false;
|
||||
|
||||
var builder = new HostBuilder().ConfigureServices(services =>
|
||||
{
|
||||
services.AddOpenTelemetry()
|
||||
.WithTracing(builder => builder
|
||||
.AddInstrumentation(() =>
|
||||
{
|
||||
callbackRun = true;
|
||||
return new object();
|
||||
}))
|
||||
.StartWithHost();
|
||||
});
|
||||
|
||||
var host = builder.Build();
|
||||
|
||||
Assert.False(callbackRun);
|
||||
|
||||
await host.StartAsync().ConfigureAwait(false);
|
||||
|
||||
Assert.True(callbackRun);
|
||||
|
||||
await host.StopAsync().ConfigureAwait(false);
|
||||
|
||||
Assert.True(callbackRun);
|
||||
|
||||
host.Dispose();
|
||||
|
||||
Assert.True(callbackRun);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AddOpenTelemetry_StartWithHost_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()
|
||||
.WithTracing(builder =>
|
||||
{
|
||||
if (builder is IDeferredTracerProviderBuilder deferredTracerProviderBuilder)
|
||||
{
|
||||
deferredTracerProviderBuilder.Configure((sp, builder) =>
|
||||
{
|
||||
configureBuilderCalled = true;
|
||||
|
||||
var configuration = sp.GetRequiredService<IConfiguration>();
|
||||
|
||||
var testKeyValue = configuration.GetValue<string>("TEST_KEY", null);
|
||||
|
||||
Assert.Equal("TEST_KEY_VALUE", testKeyValue);
|
||||
});
|
||||
}
|
||||
})
|
||||
.StartWithHost();
|
||||
});
|
||||
|
||||
var host = builder.Build();
|
||||
|
||||
Assert.False(configureBuilderCalled);
|
||||
|
||||
await host.StartAsync().ConfigureAwait(false);
|
||||
|
||||
Assert.True(configureBuilderCalled);
|
||||
|
||||
await host.StopAsync().ConfigureAwait(false);
|
||||
|
||||
host.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -16,11 +16,8 @@
|
|||
|
||||
#if NET6_0_OR_GREATER
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.Metrics;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
|
|
@ -89,7 +86,7 @@ namespace OpenTelemetry.Extensions.Hosting.Tests
|
|||
using var host = await new HostBuilder()
|
||||
.ConfigureWebHost(webBuilder => webBuilder
|
||||
.UseTestServer()
|
||||
.ConfigureServices(services => services.AddOpenTelemetry().WithMetrics(configure).StartWithHost())
|
||||
.ConfigureServices(services => services.AddOpenTelemetry().WithMetrics(configure))
|
||||
.Configure(app => app.Run(httpContext =>
|
||||
{
|
||||
testAction.Invoke();
|
||||
|
|
|
|||
|
|
@ -1,87 +0,0 @@
|
|||
// <copyright file="TelemetryHostedServiceTests.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.Hosting;
|
||||
using OpenTelemetry.Trace;
|
||||
using Xunit;
|
||||
|
||||
namespace OpenTelemetry.Extensions.Hosting.Tests;
|
||||
|
||||
public class TelemetryHostedServiceTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task StartWithoutProvidersDoesNotThrow()
|
||||
{
|
||||
var builder = new HostBuilder().ConfigureServices(services =>
|
||||
{
|
||||
services.AddOpenTelemetry()
|
||||
.StartWithHost();
|
||||
});
|
||||
|
||||
var host = builder.Build();
|
||||
|
||||
await host.StartAsync().ConfigureAwait(false);
|
||||
|
||||
await host.StopAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task StartWithExceptionsThrows()
|
||||
{
|
||||
bool expectedInnerExceptionThrown = false;
|
||||
|
||||
var builder = new HostBuilder().ConfigureServices(services =>
|
||||
{
|
||||
services.AddOpenTelemetry()
|
||||
.WithTracing(builder =>
|
||||
{
|
||||
if (builder is IDeferredTracerProviderBuilder deferredTracerProviderBuilder)
|
||||
{
|
||||
deferredTracerProviderBuilder.Configure((sp, sdkBuilder) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
// Note: This throws because services cannot be
|
||||
// registered after IServiceProvider has been
|
||||
// created.
|
||||
sdkBuilder.SetSampler<MySampler>();
|
||||
}
|
||||
catch (NotSupportedException)
|
||||
{
|
||||
expectedInnerExceptionThrown = true;
|
||||
throw;
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
.StartWithHost();
|
||||
});
|
||||
|
||||
var host = builder.Build();
|
||||
|
||||
await Assert.ThrowsAsync<NotSupportedException>(() => host.StartAsync()).ConfigureAwait(false);
|
||||
|
||||
await host.StopAsync().ConfigureAwait(false);
|
||||
|
||||
Assert.True(expectedInnerExceptionThrown);
|
||||
}
|
||||
|
||||
private sealed class MySampler : Sampler
|
||||
{
|
||||
public override SamplingResult ShouldSample(in SamplingParameters samplingParameters)
|
||||
=> new SamplingResult(SamplingDecision.RecordAndSample);
|
||||
}
|
||||
}
|
||||
|
|
@ -653,8 +653,7 @@ namespace OpenTelemetry.Instrumentation.AspNetCore.Tests
|
|||
.WithTracing(builder => builder
|
||||
.AddAspNetCoreInstrumentation()
|
||||
.AddSource(activitySourceName)
|
||||
.AddInMemoryExporter(exportedItems))
|
||||
.StartWithHost();
|
||||
.AddInMemoryExporter(exportedItems));
|
||||
});
|
||||
builder.ConfigureLogging(loggingBuilder => loggingBuilder.ClearProviders());
|
||||
})
|
||||
|
|
@ -692,8 +691,7 @@ namespace OpenTelemetry.Instrumentation.AspNetCore.Tests
|
|||
services.AddOpenTelemetry()
|
||||
.WithTracing(builder => builder
|
||||
.AddAspNetCoreInstrumentation()
|
||||
.AddInMemoryExporter(exportedItems))
|
||||
.StartWithHost();
|
||||
.AddInMemoryExporter(exportedItems));
|
||||
|
||||
// Register ActivitySource here so that it will be used
|
||||
// by ASP.NET Core to create activities
|
||||
|
|
|
|||
|
|
@ -46,8 +46,7 @@ namespace OpenTelemetry.Instrumentation.AspNetCore.Tests
|
|||
{
|
||||
services.AddOpenTelemetry()
|
||||
.WithTracing(builder => builder
|
||||
.AddAspNetCoreInstrumentation(name, configureAspNetCoreInstrumentationOptions: null))
|
||||
.StartWithHost();
|
||||
.AddAspNetCoreInstrumentation(name, configureAspNetCoreInstrumentationOptions: null));
|
||||
|
||||
services.Configure<AspNetCoreInstrumentationOptions>(name, options =>
|
||||
{
|
||||
|
|
@ -78,8 +77,7 @@ namespace OpenTelemetry.Instrumentation.AspNetCore.Tests
|
|||
{
|
||||
services.AddOpenTelemetry()
|
||||
.WithMetrics(builder => builder
|
||||
.AddAspNetCoreInstrumentation(name, configureAspNetCoreInstrumentationOptions: null))
|
||||
.StartWithHost();
|
||||
.AddAspNetCoreInstrumentation(name, configureAspNetCoreInstrumentationOptions: null));
|
||||
|
||||
services.Configure<AspNetCoreMetricsInstrumentationOptions>(name, options =>
|
||||
{
|
||||
|
|
|
|||
|
|
@ -63,8 +63,7 @@ namespace OpenTelemetry.Instrumentation.AspNetCore.Tests
|
|||
services.AddOpenTelemetry()
|
||||
.WithTracing(builder => builder
|
||||
.AddAspNetCoreInstrumentation(options => options.RecordException = recordException)
|
||||
.AddInMemoryExporter(exportedItems))
|
||||
.StartWithHost();
|
||||
.AddInMemoryExporter(exportedItems));
|
||||
});
|
||||
builder.ConfigureLogging(loggingBuilder => loggingBuilder.ClearProviders());
|
||||
})
|
||||
|
|
|
|||
|
|
@ -16,7 +16,8 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="$(MicrosoftExtensionsLoggingPkgVer)" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="$(MicrosoftExtensionsHostingPkgVer)" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="$(MicrosoftExtensionsLoggingPkgVer)" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,264 @@
|
|||
// <copyright file="OpenTelemetryServiceCollectionExtensionsTests.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>
|
||||
|
||||
#nullable enable
|
||||
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using OpenTelemetry.Metrics;
|
||||
using OpenTelemetry.Trace;
|
||||
using Xunit;
|
||||
|
||||
namespace OpenTelemetry.Tests;
|
||||
|
||||
public class OpenTelemetryServiceCollectionExtensionsTests
|
||||
{
|
||||
[Fact]
|
||||
public void AddOpenTelemetry_HostedService_Registered()
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
|
||||
services.AddOpenTelemetry();
|
||||
|
||||
using var serviceProvider = services.BuildServiceProvider();
|
||||
|
||||
var hostedServices = serviceProvider.GetServices<IHostedService>();
|
||||
|
||||
Assert.NotEmpty(hostedServices);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AddOpenTelemetry_HostedService_WithoutProvidersDoesNotThrow()
|
||||
{
|
||||
var builder = new HostBuilder().ConfigureServices(services =>
|
||||
{
|
||||
services.AddOpenTelemetry();
|
||||
});
|
||||
|
||||
var host = builder.Build();
|
||||
|
||||
await host.StartAsync().ConfigureAwait(false);
|
||||
|
||||
await host.StopAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AddOpenTelemetry_HostedService_StartWithExceptionsThrows()
|
||||
{
|
||||
bool expectedInnerExceptionThrown = false;
|
||||
|
||||
var builder = new HostBuilder().ConfigureServices(services =>
|
||||
{
|
||||
services.AddOpenTelemetry()
|
||||
.WithTracing(builder =>
|
||||
{
|
||||
if (builder is IDeferredTracerProviderBuilder deferredTracerProviderBuilder)
|
||||
{
|
||||
deferredTracerProviderBuilder.Configure((sp, sdkBuilder) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
// Note: This throws because services cannot be
|
||||
// registered after IServiceProvider has been
|
||||
// created.
|
||||
sdkBuilder.SetSampler<MySampler>();
|
||||
}
|
||||
catch (NotSupportedException)
|
||||
{
|
||||
expectedInnerExceptionThrown = true;
|
||||
throw;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
var host = builder.Build();
|
||||
|
||||
await Assert.ThrowsAsync<NotSupportedException>(() => host.StartAsync()).ConfigureAwait(false);
|
||||
|
||||
await host.StopAsync().ConfigureAwait(false);
|
||||
|
||||
Assert.True(expectedInnerExceptionThrown);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AddOpenTelemetry_WithMetrics_CreationAndDisposal()
|
||||
{
|
||||
var callbackRun = false;
|
||||
|
||||
var builder = new HostBuilder().ConfigureServices(services =>
|
||||
{
|
||||
services.AddOpenTelemetry()
|
||||
.WithMetrics(builder => builder
|
||||
.AddInstrumentation(() =>
|
||||
{
|
||||
callbackRun = true;
|
||||
return new object();
|
||||
}));
|
||||
});
|
||||
|
||||
var host = builder.Build();
|
||||
|
||||
Assert.False(callbackRun);
|
||||
|
||||
await host.StartAsync().ConfigureAwait(false);
|
||||
|
||||
Assert.True(callbackRun);
|
||||
|
||||
await host.StopAsync().ConfigureAwait(false);
|
||||
|
||||
Assert.True(callbackRun);
|
||||
|
||||
host.Dispose();
|
||||
|
||||
Assert.True(callbackRun);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AddOpenTelemetry_WithMetrics_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()
|
||||
.WithMetrics(builder =>
|
||||
{
|
||||
if (builder is IDeferredMeterProviderBuilder deferredMeterProviderBuilder)
|
||||
{
|
||||
deferredMeterProviderBuilder.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 async Task AddOpenTelemetry_WithTracing_CreationAndDisposal()
|
||||
{
|
||||
var callbackRun = false;
|
||||
|
||||
var builder = new HostBuilder().ConfigureServices(services =>
|
||||
{
|
||||
services.AddOpenTelemetry()
|
||||
.WithTracing(builder => builder
|
||||
.AddInstrumentation(() =>
|
||||
{
|
||||
callbackRun = true;
|
||||
return new object();
|
||||
}));
|
||||
});
|
||||
|
||||
var host = builder.Build();
|
||||
|
||||
Assert.False(callbackRun);
|
||||
|
||||
await host.StartAsync().ConfigureAwait(false);
|
||||
|
||||
Assert.True(callbackRun);
|
||||
|
||||
await host.StopAsync().ConfigureAwait(false);
|
||||
|
||||
Assert.True(callbackRun);
|
||||
|
||||
host.Dispose();
|
||||
|
||||
Assert.True(callbackRun);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AddOpenTelemetry_WithTracing_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()
|
||||
.WithTracing(builder =>
|
||||
{
|
||||
if (builder is IDeferredTracerProviderBuilder deferredTracerProviderBuilder)
|
||||
{
|
||||
deferredTracerProviderBuilder.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();
|
||||
}
|
||||
|
||||
private sealed class MySampler : Sampler
|
||||
{
|
||||
public override SamplingResult ShouldSample(in SamplingParameters samplingParameters)
|
||||
=> new SamplingResult(SamplingDecision.RecordAndSample);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue