From d879a290e768f4cb5b553a651d650489507eaf61 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Mon, 6 Feb 2023 15:27:56 -0800 Subject: [PATCH 01/14] Remove StartWithHost extension and using reflection to do the work in SDK. --- .../getting-started-aspnetcore/Program.cs | 3 +- .../netstandard2.0/PublicAPI.Unshipped.txt | 1 - .../HostingExtensionsEventSource.cs | 41 ---- .../Implementation/TelemetryHostedService.cs | 64 ----- .../OpenTelemetryBuilderHostingExtensions.cs | 54 ----- .../OpenTelemetryServicesExtensions.cs | 12 +- src/OpenTelemetry/Internal/HostingHelper.cs | 222 ++++++++++++++++++ .../Internal/OpenTelemetrySdkEventSource.cs | 33 +++ .../Shims/IgnoresAccessChecksToAttribute.cs | 33 +++ src/OpenTelemetry/OpenTelemetry.csproj | 1 + src/OpenTelemetry/OpenTelemetryBuilder.cs | 26 +- ...penTelemetryServiceCollectionExtensions.cs | 19 +- 12 files changed, 312 insertions(+), 197 deletions(-) delete mode 100644 src/OpenTelemetry.Extensions.Hosting/Implementation/HostingExtensionsEventSource.cs delete mode 100644 src/OpenTelemetry.Extensions.Hosting/Implementation/TelemetryHostedService.cs delete mode 100644 src/OpenTelemetry.Extensions.Hosting/OpenTelemetryBuilderHostingExtensions.cs create mode 100644 src/OpenTelemetry/Internal/HostingHelper.cs create mode 100644 src/OpenTelemetry/Internal/Shims/IgnoresAccessChecksToAttribute.cs diff --git a/docs/trace/getting-started-aspnetcore/Program.cs b/docs/trace/getting-started-aspnetcore/Program.cs index 2feba0bd229..a2914077175 100644 --- a/docs/trace/getting-started-aspnetcore/Program.cs +++ b/docs/trace/getting-started-aspnetcore/Program.cs @@ -27,8 +27,7 @@ .AddService(serviceName: "OTel.NET Getting Started")) .WithTracing(builder => builder .AddAspNetCoreInstrumentation() - .AddConsoleExporter()) - .StartWithHost(); + .AddConsoleExporter()); var app = appBuilder.Build(); diff --git a/src/OpenTelemetry.Extensions.Hosting/.publicApi/netstandard2.0/PublicAPI.Unshipped.txt b/src/OpenTelemetry.Extensions.Hosting/.publicApi/netstandard2.0/PublicAPI.Unshipped.txt index dbee0f5b37d..1d523282dda 100644 --- a/src/OpenTelemetry.Extensions.Hosting/.publicApi/netstandard2.0/PublicAPI.Unshipped.txt +++ b/src/OpenTelemetry.Extensions.Hosting/.publicApi/netstandard2.0/PublicAPI.Unshipped.txt @@ -8,6 +8,5 @@ static Microsoft.Extensions.DependencyInjection.OpenTelemetryServicesExtensions. static Microsoft.Extensions.DependencyInjection.OpenTelemetryServicesExtensions.AddOpenTelemetryTracing(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, System.Action configure) -> Microsoft.Extensions.DependencyInjection.IServiceCollection static OpenTelemetry.Metrics.MeterProviderBuilderExtensions.Configure(this OpenTelemetry.Metrics.MeterProviderBuilder meterProviderBuilder, System.Action 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 configure) -> OpenTelemetry.Trace.TracerProviderBuilder static OpenTelemetry.Trace.TracerProviderBuilderExtensions.GetServices(this OpenTelemetry.Trace.TracerProviderBuilder tracerProviderBuilder) -> Microsoft.Extensions.DependencyInjection.IServiceCollection diff --git a/src/OpenTelemetry.Extensions.Hosting/Implementation/HostingExtensionsEventSource.cs b/src/OpenTelemetry.Extensions.Hosting/Implementation/HostingExtensionsEventSource.cs deleted file mode 100644 index f6f86038cfe..00000000000 --- a/src/OpenTelemetry.Extensions.Hosting/Implementation/HostingExtensionsEventSource.cs +++ /dev/null @@ -1,41 +0,0 @@ -// -// 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. -// - -using System.Diagnostics.Tracing; - -namespace OpenTelemetry.Extensions.Hosting.Implementation -{ - /// - /// EventSource events emitted from the project. - /// - [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); - } - } -} diff --git a/src/OpenTelemetry.Extensions.Hosting/Implementation/TelemetryHostedService.cs b/src/OpenTelemetry.Extensions.Hosting/Implementation/TelemetryHostedService.cs deleted file mode 100644 index 78e50395b59..00000000000 --- a/src/OpenTelemetry.Extensions.Hosting/Implementation/TelemetryHostedService.cs +++ /dev/null @@ -1,64 +0,0 @@ -// -// 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. -// - -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(); - if (meterProvider == null) - { - HostingExtensionsEventSource.Log.MeterProviderNotRegistered(); - } - - var tracerProvider = serviceProvider.GetService(); - if (tracerProvider == null) - { - HostingExtensionsEventSource.Log.TracerProviderNotRegistered(); - } - } -} diff --git a/src/OpenTelemetry.Extensions.Hosting/OpenTelemetryBuilderHostingExtensions.cs b/src/OpenTelemetry.Extensions.Hosting/OpenTelemetryBuilderHostingExtensions.cs deleted file mode 100644 index 646ca614f44..00000000000 --- a/src/OpenTelemetry.Extensions.Hosting/OpenTelemetryBuilderHostingExtensions.cs +++ /dev/null @@ -1,54 +0,0 @@ -// -// 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. -// - -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; -using Microsoft.Extensions.Hosting; -using OpenTelemetry.Extensions.Hosting.Implementation; -using OpenTelemetry.Internal; - -namespace OpenTelemetry; - -/// -/// Contains hosting extension methods for the class. -/// -public static class OpenTelemetryBuilderHostingExtensions -{ - /// - /// Registers an to automatically start all - /// configured OpenTelemetry services in the supplied . - /// - /// - /// Note: This is safe to be called multiple times. Only a single will be created for a given . This should generally be called by hosting - /// application code and NOT library authors. - /// - /// . - /// The supplied for chaining - /// calls. - public static OpenTelemetryBuilder StartWithHost(this OpenTelemetryBuilder builder) - { - Guard.ThrowIfNull(builder); - - builder.Services.TryAddEnumerable( - ServiceDescriptor.Singleton()); - - return builder; - } -} diff --git a/src/OpenTelemetry.Extensions.Hosting/OpenTelemetryServicesExtensions.cs b/src/OpenTelemetry.Extensions.Hosting/OpenTelemetryServicesExtensions.cs index 64e837fbf16..ce238c273f7 100644 --- a/src/OpenTelemetry.Extensions.Hosting/OpenTelemetryServicesExtensions.cs +++ b/src/OpenTelemetry.Extensions.Hosting/OpenTelemetryServicesExtensions.cs @@ -51,7 +51,7 @@ public static class OpenTelemetryServicesExtensions /// . /// Supplied for chaining /// calls. - [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 IServiceCollection AddOpenTelemetryTracing(this IServiceCollection /// cref="TracerProviderBuilder"/>. /// Supplied for chaining /// calls. - [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 configure) { - services.AddOpenTelemetry().WithTracing(configure).StartWithHost(); + services.AddOpenTelemetry().WithTracing(configure); return services; } @@ -100,7 +100,7 @@ public static IServiceCollection AddOpenTelemetryTracing(this IServiceCollection /// . /// Supplied for chaining /// calls. - [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 IServiceCollection AddOpenTelemetryMetrics(this IServiceCollection /// cref="TracerProviderBuilder"/>. /// Supplied for chaining /// calls. - [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 configure) { - services.AddOpenTelemetry().WithMetrics(configure).StartWithHost(); + services.AddOpenTelemetry().WithMetrics(configure); return services; } diff --git a/src/OpenTelemetry/Internal/HostingHelper.cs b/src/OpenTelemetry/Internal/HostingHelper.cs new file mode 100644 index 00000000000..f6cda6e7e89 --- /dev/null +++ b/src/OpenTelemetry/Internal/HostingHelper.cs @@ -0,0 +1,222 @@ +// +// 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. +// + +#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; + + public static void AddOpenTelemetryHostedServiceIntoServiceCollection(IServiceCollection services) + { + lock (LockObject) + { + if (!initialized) + { + try + { + if (TryAddOpenTelemetryHostedServiceIntoServiceCollection(services)) + { + OpenTelemetrySdkEventSource.Log.HostedServiceRegistered(); + } + } + catch (Exception ex) + { + OpenTelemetrySdkEventSource.Log.HostedServiceRegistrationFailure(ex); + } + + initialized = true; + } + } + } + + private static bool TryAddOpenTelemetryHostedServiceIntoServiceCollection(IServiceCollection services) + { +#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 + + var iHostedServiceType = isDynamicCodeSupported + ? Type.GetType("Microsoft.Extensions.Hosting.IHostedService, Microsoft.Extensions.Hosting.Abstractions", throwOnError: false) + : null; + + if (iHostedServiceType != null) + { + services.TryAddSingleton(); + services.TryAddSingleton(iHostedServiceType, CreateHostedServiceImplementation(iHostedServiceType)); + return true; + } + + return false; + } + + private static Type CreateHostedServiceImplementation(Type iHostedServiceType) + { + /* + * Note: This code builds a class dynamically that does this... + * + * class OpenTelemetryHostedService : IHostedService + * { + * private readonly TelemetryHostedService telemetryHostedService; + * + * public OpenTelemetryHostedService(TelemetryHostedService telemetryHostedService) + * { + * this.telemetryHostedService = telemetryHostedService; + * } + * + * public Task StartAsync(CancellationToken cancellationToken) + * { + * this.telemetryHostedService.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."); + + var assemblyName = new AssemblyName("OpenTelemetry.Dynamic"); + + 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(TelemetryHostedService).Assembly.GetName().Name! }); + + assemblyBuilder.SetCustomAttribute(ignoresAccessChecksTo); + + var moduleBuilder = assemblyBuilder.DefineDynamicModule(assemblyName.Name!); + + var typeBuilder = moduleBuilder.DefineType("OpenTelemetryHostedService", TypeAttributes.NotPublic); + + typeBuilder.AddInterfaceImplementation(iHostedServiceType); + + var hostedServiceImplementationField = typeBuilder.DefineField( + "telemetryHostedService", + typeof(TelemetryHostedService), + FieldAttributes.Private | FieldAttributes.InitOnly); + + var constructor = typeBuilder.DefineConstructor( + MethodAttributes.Public, + CallingConventions.Standard, + new Type[] { typeof(TelemetryHostedService) }); + + 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(TelemetryHostedService).GetMethod(nameof(TelemetryHostedService.Start)) ?? throw new InvalidOperationException($"{nameof(TelemetryHostedService)}.{nameof(TelemetryHostedService.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 TelemetryHostedService + { + private readonly IServiceProvider serviceProvider; + + public TelemetryHostedService(IServiceProvider serviceProvider) + { + Debug.Assert(serviceProvider != null, "serviceProvider was null"); + + this.serviceProvider = serviceProvider; + } + + public void Start() + { + var serviceProvider = this.serviceProvider; + + var meterProvider = serviceProvider.GetService(); + if (meterProvider == null) + { + OpenTelemetrySdkEventSource.Log.MeterProviderNotRegistered(); + } + + var tracerProvider = serviceProvider.GetService(); + if (tracerProvider == null) + { + OpenTelemetrySdkEventSource.Log.TracerProviderNotRegistered(); + } + } + } +} diff --git a/src/OpenTelemetry/Internal/OpenTelemetrySdkEventSource.cs b/src/OpenTelemetry/Internal/OpenTelemetrySdkEventSource.cs index 848737de80f..1efb04d5d29 100644 --- a/src/OpenTelemetry/Internal/OpenTelemetrySdkEventSource.cs +++ b/src/OpenTelemetry/Internal/OpenTelemetrySdkEventSource.cs @@ -175,6 +175,15 @@ public void DroppedExportProcessorItems(string exportProcessorName, string expor } } + [NonEvent] + public void HostedServiceRegistrationFailure(Exception ex) + { + if (this.IsEnabled(EventLevel.Warning, 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,30 @@ public void InvalidEnvironmentVariable(string key, string value) 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 could not be registered in application services. Error: '{0}'", Level = EventLevel.Warning)] + public void HostedServiceRegistrationFailure(string error) + { + this.WriteEvent(49, error); + } + + [Event(50, Message = "OpenTelemetry TracerProvider was not found in application services. Tracing will remain disabled.", Level = EventLevel.Warning)] + public void TracerProviderNotRegistered() + { + this.WriteEvent(50); + } + + [Event(51, Message = "OpenTelemetry MeterProvider was not found in application services. Metrics will remain disabled.", Level = EventLevel.Warning)] + public void MeterProviderNotRegistered() + { + this.WriteEvent(51); + } + #if DEBUG public class OpenTelemetryEventListener : EventListener { diff --git a/src/OpenTelemetry/Internal/Shims/IgnoresAccessChecksToAttribute.cs b/src/OpenTelemetry/Internal/Shims/IgnoresAccessChecksToAttribute.cs new file mode 100644 index 00000000000..d4fee58cfbd --- /dev/null +++ b/src/OpenTelemetry/Internal/Shims/IgnoresAccessChecksToAttribute.cs @@ -0,0 +1,33 @@ +// +// 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. +// + +#nullable enable + +namespace System.Runtime.CompilerServices; + +/// +/// IgnoresAccessChecksToAttribute tells the runtime to bypass visibility checks +/// to some assembly. See: . +/// +[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] +internal sealed class IgnoresAccessChecksToAttribute : Attribute +{ + public IgnoresAccessChecksToAttribute(string assemblyName) + => this.AssemblyName = assemblyName; + + public string AssemblyName { get; } +} diff --git a/src/OpenTelemetry/OpenTelemetry.csproj b/src/OpenTelemetry/OpenTelemetry.csproj index 65e87e8f969..5decd71ff36 100644 --- a/src/OpenTelemetry/OpenTelemetry.csproj +++ b/src/OpenTelemetry/OpenTelemetry.csproj @@ -20,6 +20,7 @@ + diff --git a/src/OpenTelemetry/OpenTelemetryBuilder.cs b/src/OpenTelemetry/OpenTelemetryBuilder.cs index f817e0bfe68..216343c9f60 100644 --- a/src/OpenTelemetry/OpenTelemetryBuilder.cs +++ b/src/OpenTelemetry/OpenTelemetryBuilder.cs @@ -74,18 +74,9 @@ public OpenTelemetryBuilder ConfigureResource( /// Adds metric services into the builder. /// /// - /// Notes: - /// - /// A will not be created automatically - /// using this method. To begin collecting metrics either use the - /// OpenTelemetryBuilder.StartWithHost extension in the - /// OpenTelemetry.Extensions.Hosting package or access the through the application . - /// 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 will be created for a given - /// . - /// + /// . /// /// The supplied for chaining /// calls. @@ -115,18 +106,9 @@ public OpenTelemetryBuilder WithMetrics(Action configure) /// Adds tracing services into the builder. /// /// - /// Notes: - /// - /// A will not be created automatically - /// using this method. To begin collecting traces either use the - /// OpenTelemetryBuilder.StartWithHost extension in the - /// OpenTelemetry.Extensions.Hosting package or access the through the application . - /// 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 will be created for a given - /// . - /// + /// . /// /// The supplied for chaining /// calls. diff --git a/src/OpenTelemetry/OpenTelemetryServiceCollectionExtensions.cs b/src/OpenTelemetry/OpenTelemetryServiceCollectionExtensions.cs index 2541a85f7bb..0fc0f408976 100644 --- a/src/OpenTelemetry/OpenTelemetryServiceCollectionExtensions.cs +++ b/src/OpenTelemetry/OpenTelemetryServiceCollectionExtensions.cs @@ -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: /// /// A and/or - /// will not be created automatically using this method. To begin collecting - /// traces and/or metrics either use the - /// OpenTelemetryBuilder.StartWithHost extension in the - /// OpenTelemetry.Extensions.Hosting package or access the and/or through the - /// application . + /// will be created automatically using this method if a host supporting + /// Microsoft.Extensions.Hosting.IHostedService is detected at + /// runtime. To begin collecting traces and/or metrics when hosting + /// extensions are not being used access the + /// and/or through the application . /// This is safe to be called multiple times and by library authors. /// Only a single and/or will be created for a given The supplied for chaining /// calls. public static OpenTelemetryBuilder AddOpenTelemetry(this IServiceCollection services) - => new(services); + { + HostingHelper.AddOpenTelemetryHostedServiceIntoServiceCollection(services); + + return new(services); + } } From 77165602ca01550f8aabb70dcc21815e44ee43b5 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Mon, 6 Feb 2023 16:26:30 -0800 Subject: [PATCH 02/14] Warning cleanup. --- .../.publicApi/netstandard2.0/PublicAPI.Unshipped.txt | 1 - src/OpenTelemetry/Internal/HostingHelper.cs | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/OpenTelemetry.Extensions.Hosting/.publicApi/netstandard2.0/PublicAPI.Unshipped.txt b/src/OpenTelemetry.Extensions.Hosting/.publicApi/netstandard2.0/PublicAPI.Unshipped.txt index 1d523282dda..63141a61d05 100644 --- a/src/OpenTelemetry.Extensions.Hosting/.publicApi/netstandard2.0/PublicAPI.Unshipped.txt +++ b/src/OpenTelemetry.Extensions.Hosting/.publicApi/netstandard2.0/PublicAPI.Unshipped.txt @@ -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 configure) -> Microsoft.Extensions.DependencyInjection.IServiceCollection diff --git a/src/OpenTelemetry/Internal/HostingHelper.cs b/src/OpenTelemetry/Internal/HostingHelper.cs index f6cda6e7e89..59fba0be33a 100644 --- a/src/OpenTelemetry/Internal/HostingHelper.cs +++ b/src/OpenTelemetry/Internal/HostingHelper.cs @@ -199,7 +199,7 @@ public TelemetryHostedService(IServiceProvider serviceProvider) { Debug.Assert(serviceProvider != null, "serviceProvider was null"); - this.serviceProvider = serviceProvider; + this.serviceProvider = serviceProvider!; } public void Start() From acb39c22446324106b46a70ba32c0ba4ffab1a23 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Tue, 7 Feb 2023 09:49:35 -0800 Subject: [PATCH 03/14] Fix up. --- examples/AspNetCore/Program.cs | 3 +- examples/GrpcService/Startup.cs | 3 +- .../MicroserviceExample/WebApi/Startup.cs | 3 +- .../WorkerService/Program.cs | 3 +- .../EventSourceTest.cs | 31 ------------------- 5 files changed, 4 insertions(+), 39 deletions(-) delete mode 100644 test/OpenTelemetry.Extensions.Hosting.Tests/EventSourceTest.cs diff --git a/examples/AspNetCore/Program.cs b/examples/AspNetCore/Program.cs index 5bbd283582a..67d36b5bc7e 100644 --- a/examples/AspNetCore/Program.cs +++ b/examples/AspNetCore/Program.cs @@ -119,8 +119,7 @@ builder.AddConsoleExporter(); break; } - }) - .StartWithHost(); + }); // Clear default logging providers used by WebApplication host. appBuilder.Logging.ClearProviders(); diff --git a/examples/GrpcService/Startup.cs b/examples/GrpcService/Startup.cs index 2c875eae04b..32d595ab63d 100644 --- a/examples/GrpcService/Startup.cs +++ b/examples/GrpcService/Startup.cs @@ -61,8 +61,7 @@ public void ConfigureServices(IServiceCollection services) builder.AddConsoleExporter(); break; } - }) - .StartWithHost(); + }); } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) diff --git a/examples/MicroserviceExample/WebApi/Startup.cs b/examples/MicroserviceExample/WebApi/Startup.cs index 90e66783302..d3966c26aa2 100644 --- a/examples/MicroserviceExample/WebApi/Startup.cs +++ b/examples/MicroserviceExample/WebApi/Startup.cs @@ -43,8 +43,7 @@ public void ConfigureServices(IServiceCollection services) { 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) diff --git a/examples/MicroserviceExample/WorkerService/Program.cs b/examples/MicroserviceExample/WorkerService/Program.cs index 665265cedd4..75ad1223e16 100644 --- a/examples/MicroserviceExample/WorkerService/Program.cs +++ b/examples/MicroserviceExample/WorkerService/Program.cs @@ -42,8 +42,7 @@ public static IHostBuilder CreateHostBuilder(string[] args) => { var zipkinHostName = Environment.GetEnvironmentVariable("ZIPKIN_HOSTNAME") ?? "localhost"; b.Endpoint = new Uri($"http://{zipkinHostName}:9411/api/v2/spans"); - })) - .StartWithHost(); + })); }); } } diff --git a/test/OpenTelemetry.Extensions.Hosting.Tests/EventSourceTest.cs b/test/OpenTelemetry.Extensions.Hosting.Tests/EventSourceTest.cs deleted file mode 100644 index 64b4ab634bf..00000000000 --- a/test/OpenTelemetry.Extensions.Hosting.Tests/EventSourceTest.cs +++ /dev/null @@ -1,31 +0,0 @@ -// -// 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. -// - -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); - } - } -} From 1d5df255f2d4bb3421f88b3f48e7b9daf6cce990 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Tue, 7 Feb 2023 09:57:53 -0800 Subject: [PATCH 04/14] Improved logging. --- src/OpenTelemetry/Internal/HostingHelper.cs | 32 +++++++++++++------ .../Internal/OpenTelemetrySdkEventSource.cs | 18 +++++++---- 2 files changed, 34 insertions(+), 16 deletions(-) diff --git a/src/OpenTelemetry/Internal/HostingHelper.cs b/src/OpenTelemetry/Internal/HostingHelper.cs index 59fba0be33a..20dfeab1236 100644 --- a/src/OpenTelemetry/Internal/HostingHelper.cs +++ b/src/OpenTelemetry/Internal/HostingHelper.cs @@ -40,10 +40,14 @@ public static void AddOpenTelemetryHostedServiceIntoServiceCollection(IServiceCo { try { - if (TryAddOpenTelemetryHostedServiceIntoServiceCollection(services)) + if (TryAddOpenTelemetryHostedServiceIntoServiceCollection(services, out var reason)) { OpenTelemetrySdkEventSource.Log.HostedServiceRegistered(); } + else + { + OpenTelemetrySdkEventSource.Log.HostedServiceRegistrationSkipped(reason); + } } catch (Exception ex) { @@ -55,7 +59,7 @@ public static void AddOpenTelemetryHostedServiceIntoServiceCollection(IServiceCo } } - private static bool TryAddOpenTelemetryHostedServiceIntoServiceCollection(IServiceCollection services) + private static bool TryAddOpenTelemetryHostedServiceIntoServiceCollection(IServiceCollection services, out string? reason) { #if NETSTANDARD2_1_OR_GREATER || NET6_0_OR_GREATER bool isDynamicCodeSupported = RuntimeFeature.IsDynamicCodeSupported; @@ -64,18 +68,26 @@ private static bool TryAddOpenTelemetryHostedServiceIntoServiceCollection(IServi bool isDynamicCodeSupported = true; #endif - var iHostedServiceType = isDynamicCodeSupported - ? Type.GetType("Microsoft.Extensions.Hosting.IHostedService, Microsoft.Extensions.Hosting.Abstractions", throwOnError: false) - : null; + 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) + if (iHostedServiceType == null) { - services.TryAddSingleton(); - services.TryAddSingleton(iHostedServiceType, CreateHostedServiceImplementation(iHostedServiceType)); - return true; + reason = "Microsoft.Extensions.Hosting.IHostedService not found"; + return false; } - return false; + services.TryAddSingleton(); + services.TryAddSingleton(iHostedServiceType, CreateHostedServiceImplementation(iHostedServiceType)); + + reason = null; + return true; } private static Type CreateHostedServiceImplementation(Type iHostedServiceType) diff --git a/src/OpenTelemetry/Internal/OpenTelemetrySdkEventSource.cs b/src/OpenTelemetry/Internal/OpenTelemetrySdkEventSource.cs index 1efb04d5d29..0a5acc9a679 100644 --- a/src/OpenTelemetry/Internal/OpenTelemetrySdkEventSource.cs +++ b/src/OpenTelemetry/Internal/OpenTelemetrySdkEventSource.cs @@ -430,22 +430,28 @@ public void HostedServiceRegistered() this.WriteEvent(48); } - [Event(49, Message = "OpenTelemetry IHostedService could not be registered in application services. Error: '{0}'", Level = EventLevel.Warning)] + [Event(49, Message = "OpenTelemetry IHostedService application services registration skipped. Reason: '{0}'", Level = EventLevel.Informational)] + 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.Warning)] public void HostedServiceRegistrationFailure(string error) { - this.WriteEvent(49, error); + this.WriteEvent(50, error); } - [Event(50, Message = "OpenTelemetry TracerProvider was not found in application services. Tracing will remain disabled.", Level = EventLevel.Warning)] + [Event(51, Message = "OpenTelemetry TracerProvider was not found in application services. Tracing will remain disabled.", Level = EventLevel.Warning)] public void TracerProviderNotRegistered() { - this.WriteEvent(50); + this.WriteEvent(51); } - [Event(51, Message = "OpenTelemetry MeterProvider was not found in application services. Metrics will remain disabled.", Level = EventLevel.Warning)] + [Event(52, Message = "OpenTelemetry MeterProvider was not found in application services. Metrics will remain disabled.", Level = EventLevel.Warning)] public void MeterProviderNotRegistered() { - this.WriteEvent(51); + this.WriteEvent(52); } #if DEBUG From 54eef544a7e977f4c3c44489762f2a34869552cd Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Tue, 7 Feb 2023 11:23:47 -0800 Subject: [PATCH 05/14] Remove OpenTelemetry.Extensions.Hosting dependency from examples & docs. --- .../getting-started-aspnetcore/getting-started-aspnetcore.csproj | 1 - examples/AspNetCore/Examples.AspNetCore.csproj | 1 - examples/GrpcService/Examples.GrpcService.csproj | 1 - examples/MicroserviceExample/WebApi/WebApi.csproj | 1 - examples/MicroserviceExample/WorkerService/WorkerService.csproj | 1 - 5 files changed, 5 deletions(-) diff --git a/docs/trace/getting-started-aspnetcore/getting-started-aspnetcore.csproj b/docs/trace/getting-started-aspnetcore/getting-started-aspnetcore.csproj index 1956f427004..321fbcd608b 100644 --- a/docs/trace/getting-started-aspnetcore/getting-started-aspnetcore.csproj +++ b/docs/trace/getting-started-aspnetcore/getting-started-aspnetcore.csproj @@ -8,7 +8,6 @@ - diff --git a/examples/AspNetCore/Examples.AspNetCore.csproj b/examples/AspNetCore/Examples.AspNetCore.csproj index b8b09bcd9d4..7e29680340f 100644 --- a/examples/AspNetCore/Examples.AspNetCore.csproj +++ b/examples/AspNetCore/Examples.AspNetCore.csproj @@ -10,7 +10,6 @@ - diff --git a/examples/GrpcService/Examples.GrpcService.csproj b/examples/GrpcService/Examples.GrpcService.csproj index 2a9028e8650..7123737717a 100644 --- a/examples/GrpcService/Examples.GrpcService.csproj +++ b/examples/GrpcService/Examples.GrpcService.csproj @@ -16,7 +16,6 @@ - diff --git a/examples/MicroserviceExample/WebApi/WebApi.csproj b/examples/MicroserviceExample/WebApi/WebApi.csproj index 876450b7176..5e1695e9c86 100644 --- a/examples/MicroserviceExample/WebApi/WebApi.csproj +++ b/examples/MicroserviceExample/WebApi/WebApi.csproj @@ -10,7 +10,6 @@ - diff --git a/examples/MicroserviceExample/WorkerService/WorkerService.csproj b/examples/MicroserviceExample/WorkerService/WorkerService.csproj index 926e55dd6fd..29d72978cd5 100644 --- a/examples/MicroserviceExample/WorkerService/WorkerService.csproj +++ b/examples/MicroserviceExample/WorkerService/WorkerService.csproj @@ -11,7 +11,6 @@ - From c66f283795256fbce1e46492d75a4aec5a56acfe Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Tue, 7 Feb 2023 13:14:26 -0800 Subject: [PATCH 06/14] Test fixes. --- .../BasicTests.cs | 6 ++---- .../DependencyInjectionConfigTests.cs | 6 ++---- .../IncomingRequestsCollectionsIsAccordingToTheSpecTests.cs | 3 +-- 3 files changed, 5 insertions(+), 10 deletions(-) diff --git a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/BasicTests.cs b/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/BasicTests.cs index cc7ffcee3c4..cb51bd437c4 100644 --- a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/BasicTests.cs +++ b/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/BasicTests.cs @@ -653,8 +653,7 @@ public async Task ActivitiesStartedInMiddlewareBySettingHostActivityToNullShould .WithTracing(builder => builder .AddAspNetCoreInstrumentation() .AddSource(activitySourceName) - .AddInMemoryExporter(exportedItems)) - .StartWithHost(); + .AddInMemoryExporter(exportedItems)); }); builder.ConfigureLogging(loggingBuilder => loggingBuilder.ClearProviders()); }) @@ -692,8 +691,7 @@ void ConfigureTestServices(IServiceCollection services) 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 diff --git a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/DependencyInjectionConfigTests.cs b/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/DependencyInjectionConfigTests.cs index 16136110509..3631ca9a1ef 100644 --- a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/DependencyInjectionConfigTests.cs +++ b/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/DependencyInjectionConfigTests.cs @@ -46,8 +46,7 @@ void ConfigureTestServices(IServiceCollection services) { services.AddOpenTelemetry() .WithTracing(builder => builder - .AddAspNetCoreInstrumentation(name, configureAspNetCoreInstrumentationOptions: null)) - .StartWithHost(); + .AddAspNetCoreInstrumentation(name, configureAspNetCoreInstrumentationOptions: null)); services.Configure(name, options => { @@ -78,8 +77,7 @@ void ConfigureTestServices(IServiceCollection services) { services.AddOpenTelemetry() .WithMetrics(builder => builder - .AddAspNetCoreInstrumentation(name, configureAspNetCoreInstrumentationOptions: null)) - .StartWithHost(); + .AddAspNetCoreInstrumentation(name, configureAspNetCoreInstrumentationOptions: null)); services.Configure(name, options => { diff --git a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/IncomingRequestsCollectionsIsAccordingToTheSpecTests.cs b/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/IncomingRequestsCollectionsIsAccordingToTheSpecTests.cs index 971920474e9..8500ac00a22 100644 --- a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/IncomingRequestsCollectionsIsAccordingToTheSpecTests.cs +++ b/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/IncomingRequestsCollectionsIsAccordingToTheSpecTests.cs @@ -63,8 +63,7 @@ public async Task SuccessfulTemplateControllerCallGeneratesASpan( services.AddOpenTelemetry() .WithTracing(builder => builder .AddAspNetCoreInstrumentation(options => options.RecordException = recordException) - .AddInMemoryExporter(exportedItems)) - .StartWithHost(); + .AddInMemoryExporter(exportedItems)); }); builder.ConfigureLogging(loggingBuilder => loggingBuilder.ClearProviders()); }) From c088a9dc869113c4616179e00f42efc688b9e3a1 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Tue, 7 Feb 2023 13:53:53 -0800 Subject: [PATCH 07/14] Fixes and test updates. --- build/Common.props | 3 +- .../EnvironmentVariablesExtensions.cs | 51 +--- src/OpenTelemetry/Internal/HostingHelper.cs | 56 ++-- .../HostingMeterExtensionTests.cs | 109 -------- .../HostingTracerExtensionTests.cs | 109 -------- .../InMemoryExporterMetricsExtensionsTests.cs | 5 +- .../TelemetryHostedServiceTests.cs | 87 ------ .../OpenTelemetry.Tests.csproj | 3 +- ...lemetryServiceCollectionExtensionsTests.cs | 264 ++++++++++++++++++ 9 files changed, 315 insertions(+), 372 deletions(-) delete mode 100644 test/OpenTelemetry.Extensions.Hosting.Tests/HostingMeterExtensionTests.cs delete mode 100644 test/OpenTelemetry.Extensions.Hosting.Tests/HostingTracerExtensionTests.cs delete mode 100644 test/OpenTelemetry.Extensions.Hosting.Tests/TelemetryHostedServiceTests.cs create mode 100644 test/OpenTelemetry.Tests/OpenTelemetryServiceCollectionExtensionsTests.cs diff --git a/build/Common.props b/build/Common.props index d47bf985f5e..a1d08f1e37d 100644 --- a/build/Common.props +++ b/build/Common.props @@ -37,7 +37,8 @@ [17.4.1] [3.1.0,) $(MicrosoftExtensionsDependencyInjectionPkgVer) - [2.1.0,) + [2.1.0,) + $(MicrosoftExtensionsHostingPkgVer) [3.1.0,) $(MicrosoftExtensionsLoggingPkgVer) [3.1.0,) diff --git a/src/OpenTelemetry/Internal/EnvironmentVariables/EnvironmentVariablesExtensions.cs b/src/OpenTelemetry/Internal/EnvironmentVariables/EnvironmentVariablesExtensions.cs index 5b97e90ce77..02928d154d5 100644 --- a/src/OpenTelemetry/Internal/EnvironmentVariables/EnvironmentVariablesExtensions.cs +++ b/src/OpenTelemetry/Internal/EnvironmentVariables/EnvironmentVariablesExtensions.cs @@ -4,49 +4,24 @@ #nullable enable -using System; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration.EnvironmentVariables; -namespace Microsoft.Extensions.Configuration +namespace OpenTelemetry.Internal; + +/// +/// Extension methods for registering with . +/// +internal static class EnvironmentVariablesExtensions { /// - /// Extension methods for registering with . + /// Adds an that reads configuration values from environment variables. /// - internal static class EnvironmentVariablesExtensions + /// The to add to. + /// The . + public static IConfigurationBuilder AddEnvironmentVariables(this IConfigurationBuilder configurationBuilder) { - /// - /// Adds an that reads configuration values from environment variables. - /// - /// The to add to. - /// The . - public static IConfigurationBuilder AddEnvironmentVariables(this IConfigurationBuilder configurationBuilder) - { - configurationBuilder.Add(new EnvironmentVariablesConfigurationSource()); - return configurationBuilder; - } - - /// - /// Adds an that reads configuration values from environment variables - /// with a specified prefix. - /// - /// The to add to. - /// The prefix that environment variable names must start with. The prefix will be removed from the environment variable names. - /// The . - public static IConfigurationBuilder AddEnvironmentVariables( - this IConfigurationBuilder configurationBuilder, - string? prefix) - { - configurationBuilder.Add(new EnvironmentVariablesConfigurationSource { Prefix = prefix }); - return configurationBuilder; - } - - /// - /// Adds an that reads configuration values from environment variables. - /// - /// The to add to. - /// Configures the source. - /// The . - public static IConfigurationBuilder AddEnvironmentVariables(this IConfigurationBuilder builder, Action? configureSource) - => builder.Add(configureSource); + configurationBuilder.Add(new EnvironmentVariablesConfigurationSource()); + return configurationBuilder; } } diff --git a/src/OpenTelemetry/Internal/HostingHelper.cs b/src/OpenTelemetry/Internal/HostingHelper.cs index 20dfeab1236..40ea54ef626 100644 --- a/src/OpenTelemetry/Internal/HostingHelper.cs +++ b/src/OpenTelemetry/Internal/HostingHelper.cs @@ -31,31 +31,17 @@ internal static class HostingHelper { private static readonly object LockObject = new(); private static bool initialized; + private static Type? hostedServiceImplementation; public static void AddOpenTelemetryHostedServiceIntoServiceCollection(IServiceCollection services) { - lock (LockObject) + if (TryAddOpenTelemetryHostedServiceIntoServiceCollection(services, out var reason)) { - if (!initialized) - { - try - { - if (TryAddOpenTelemetryHostedServiceIntoServiceCollection(services, out var reason)) - { - OpenTelemetrySdkEventSource.Log.HostedServiceRegistered(); - } - else - { - OpenTelemetrySdkEventSource.Log.HostedServiceRegistrationSkipped(reason); - } - } - catch (Exception ex) - { - OpenTelemetrySdkEventSource.Log.HostedServiceRegistrationFailure(ex); - } - - initialized = true; - } + OpenTelemetrySdkEventSource.Log.HostedServiceRegistered(); + } + else + { + OpenTelemetrySdkEventSource.Log.HostedServiceRegistrationSkipped(reason); } } @@ -67,7 +53,6 @@ private static bool TryAddOpenTelemetryHostedServiceIntoServiceCollection(IServi // Note: This is for .NET Framework and/or .NET Standard 2.0 targets. bool isDynamicCodeSupported = true; #endif - if (!isDynamicCodeSupported) { reason = "Dynamic code not supported"; @@ -83,8 +68,33 @@ private static bool TryAddOpenTelemetryHostedServiceIntoServiceCollection(IServi 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(); - services.TryAddSingleton(iHostedServiceType, CreateHostedServiceImplementation(iHostedServiceType)); + services.TryAddSingleton(iHostedServiceType, hostedServiceImplementation); reason = null; return true; diff --git a/test/OpenTelemetry.Extensions.Hosting.Tests/HostingMeterExtensionTests.cs b/test/OpenTelemetry.Extensions.Hosting.Tests/HostingMeterExtensionTests.cs deleted file mode 100644 index fe267d558a5..00000000000 --- a/test/OpenTelemetry.Extensions.Hosting.Tests/HostingMeterExtensionTests.cs +++ /dev/null @@ -1,109 +0,0 @@ -// -// 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. -// - -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 - { - ["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(); - - var testKeyValue = configuration.GetValue("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(); - } - } -} diff --git a/test/OpenTelemetry.Extensions.Hosting.Tests/HostingTracerExtensionTests.cs b/test/OpenTelemetry.Extensions.Hosting.Tests/HostingTracerExtensionTests.cs deleted file mode 100644 index 1122a9a8e86..00000000000 --- a/test/OpenTelemetry.Extensions.Hosting.Tests/HostingTracerExtensionTests.cs +++ /dev/null @@ -1,109 +0,0 @@ -// -// 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. -// - -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 - { - ["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(); - - var testKeyValue = configuration.GetValue("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(); - } - } -} diff --git a/test/OpenTelemetry.Extensions.Hosting.Tests/InMemoryExporterMetricsExtensionsTests.cs b/test/OpenTelemetry.Extensions.Hosting.Tests/InMemoryExporterMetricsExtensionsTests.cs index f32a85aedf3..cb8d05ab9ad 100644 --- a/test/OpenTelemetry.Extensions.Hosting.Tests/InMemoryExporterMetricsExtensionsTests.cs +++ b/test/OpenTelemetry.Extensions.Hosting.Tests/InMemoryExporterMetricsExtensionsTests.cs @@ -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 @@ private static async Task RunMetricsTest(Action configure, 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(); diff --git a/test/OpenTelemetry.Extensions.Hosting.Tests/TelemetryHostedServiceTests.cs b/test/OpenTelemetry.Extensions.Hosting.Tests/TelemetryHostedServiceTests.cs deleted file mode 100644 index 3900a74028f..00000000000 --- a/test/OpenTelemetry.Extensions.Hosting.Tests/TelemetryHostedServiceTests.cs +++ /dev/null @@ -1,87 +0,0 @@ -// -// 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. -// - -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(); - } - catch (NotSupportedException) - { - expectedInnerExceptionThrown = true; - throw; - } - }); - } - }) - .StartWithHost(); - }); - - var host = builder.Build(); - - await Assert.ThrowsAsync(() => 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); - } -} diff --git a/test/OpenTelemetry.Tests/OpenTelemetry.Tests.csproj b/test/OpenTelemetry.Tests/OpenTelemetry.Tests.csproj index c87707d50eb..c2db74d6d20 100644 --- a/test/OpenTelemetry.Tests/OpenTelemetry.Tests.csproj +++ b/test/OpenTelemetry.Tests/OpenTelemetry.Tests.csproj @@ -16,7 +16,8 @@ - + + diff --git a/test/OpenTelemetry.Tests/OpenTelemetryServiceCollectionExtensionsTests.cs b/test/OpenTelemetry.Tests/OpenTelemetryServiceCollectionExtensionsTests.cs new file mode 100644 index 00000000000..fa425c2d470 --- /dev/null +++ b/test/OpenTelemetry.Tests/OpenTelemetryServiceCollectionExtensionsTests.cs @@ -0,0 +1,264 @@ +// +// 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. +// + +#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(); + + 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(); + } + catch (NotSupportedException) + { + expectedInnerExceptionThrown = true; + throw; + } + }); + } + }); + }); + + var host = builder.Build(); + + await Assert.ThrowsAsync(() => 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 + { + ["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(); + + var testKeyValue = configuration.GetValue("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 + { + ["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(); + + var testKeyValue = configuration.GetValue("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); + } +} From c29470e35459c2f8ebb5b30d037ddfce07e3cf0b Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Tue, 7 Feb 2023 14:54:46 -0800 Subject: [PATCH 08/14] Bug fix. --- src/OpenTelemetry/Internal/HostingHelper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OpenTelemetry/Internal/HostingHelper.cs b/src/OpenTelemetry/Internal/HostingHelper.cs index 40ea54ef626..2197e89a95e 100644 --- a/src/OpenTelemetry/Internal/HostingHelper.cs +++ b/src/OpenTelemetry/Internal/HostingHelper.cs @@ -94,7 +94,7 @@ private static bool TryAddOpenTelemetryHostedServiceIntoServiceCollection(IServi } services.TryAddSingleton(); - services.TryAddSingleton(iHostedServiceType, hostedServiceImplementation); + services.TryAddEnumerable(ServiceDescriptor.Singleton(iHostedServiceType, hostedServiceImplementation)); reason = null; return true; From 0cd2c59a7b0a41050fd1df5a8d7e449a03dce2d8 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Tue, 7 Feb 2023 15:28:51 -0800 Subject: [PATCH 09/14] Patch CHANGELOGs. --- src/OpenTelemetry.Extensions.Hosting/CHANGELOG.md | 4 ++++ src/OpenTelemetry/CHANGELOG.md | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/src/OpenTelemetry.Extensions.Hosting/CHANGELOG.md b/src/OpenTelemetry.Extensions.Hosting/CHANGELOG.md index 8e7983ec511..65eaaffaaa7 100644 --- a/src/OpenTelemetry.Extensions.Hosting/CHANGELOG.md +++ b/src/OpenTelemetry.Extensions.Hosting/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +* Removed the `OpenTelemetryBuilder.StartWithHost` extension and moved the + functionality into the SDK `AddOpenTelemetry` extension. + ([#4151](https://github.com/open-telemetry/opentelemetry-dotnet/pull/4151)) + ## 1.4.0-rc.3 Released 2023-Feb-01 diff --git a/src/OpenTelemetry/CHANGELOG.md b/src/OpenTelemetry/CHANGELOG.md index 9940288268f..2b03fe7dcaa 100644 --- a/src/OpenTelemetry/CHANGELOG.md +++ b/src/OpenTelemetry/CHANGELOG.md @@ -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 From 7a5036fd32529745225bab4eefd6abe6e1f850ed Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Tue, 7 Feb 2023 15:33:11 -0800 Subject: [PATCH 10/14] Clean up. --- examples/AspNetCore/Program.cs | 3 +-- src/OpenTelemetry.Exporter.Jaeger/README.md | 3 +-- .../README.md | 3 +-- .../README.md | 3 +-- src/OpenTelemetry.Exporter.Zipkin/README.md | 3 +-- src/OpenTelemetry.Extensions.Hosting/README.md | 18 +++--------------- .../README.md | 12 ++++-------- .../README.md | 3 +-- 8 files changed, 13 insertions(+), 35 deletions(-) diff --git a/examples/AspNetCore/Program.cs b/examples/AspNetCore/Program.cs index 67d36b5bc7e..2958d18750b 100644 --- a/examples/AspNetCore/Program.cs +++ b/examples/AspNetCore/Program.cs @@ -40,8 +40,7 @@ serviceVersion: Assembly.GetExecutingAssembly().GetName().Version?.ToString() ?? "unknown", serviceInstanceId: Environment.MachineName); -// 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 => diff --git a/src/OpenTelemetry.Exporter.Jaeger/README.md b/src/OpenTelemetry.Exporter.Jaeger/README.md index 411e3d40283..643d9874819 100644 --- a/src/OpenTelemetry.Exporter.Jaeger/README.md +++ b/src/OpenTelemetry.Exporter.Jaeger/README.md @@ -98,8 +98,7 @@ services.AddOpenTelemetry() client.DefaultRequestHeaders.Add("X-MyCustomHeader", "value"); return client; }; - })) - .StartWithHost(); + })); ``` For users using diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/README.md b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/README.md index 369b6e1574e..177f9639443 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/README.md +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/README.md @@ -135,8 +135,7 @@ services.AddOpenTelemetry() client.DefaultRequestHeaders.Add("X-MyCustomHeader", "value"); return client; }; - })) - .StartWithHost(); + })); ``` For users using diff --git a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/README.md b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/README.md index 6bcd236b5bb..e6676934961 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/README.md +++ b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/README.md @@ -28,8 +28,7 @@ dotnet add package --prerelease OpenTelemetry.Exporter.Prometheus.AspNetCore ```csharp services.AddOpenTelemetry() .WithMetrics(builder => builder - .AddPrometheusExporter()) - .StartWithHost(); + .AddPrometheusExporter()); ``` * Or configure directly: diff --git a/src/OpenTelemetry.Exporter.Zipkin/README.md b/src/OpenTelemetry.Exporter.Zipkin/README.md index 20d794c051d..aa277b7fdc2 100644 --- a/src/OpenTelemetry.Exporter.Zipkin/README.md +++ b/src/OpenTelemetry.Exporter.Zipkin/README.md @@ -87,8 +87,7 @@ services.AddOpenTelemetry() HttpClient client = new HttpClient(); client.DefaultRequestHeaders.Add("X-MyCustomHeader", "value"); return client; - })) - .StartWithHost(); + })); ``` For users using diff --git a/src/OpenTelemetry.Extensions.Hosting/README.md b/src/OpenTelemetry.Extensions.Hosting/README.md index a1703580f07..59014925824 100644 --- a/src/OpenTelemetry.Extensions.Hosting/README.md +++ b/src/OpenTelemetry.Extensions.Hosting/README.md @@ -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(); diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/README.md b/src/OpenTelemetry.Instrumentation.AspNetCore/README.md index 59e489b8676..fd535b1682c 100644 --- a/src/OpenTelemetry.Instrumentation.AspNetCore/README.md +++ b/src/OpenTelemetry.Instrumentation.AspNetCore/README.md @@ -58,8 +58,7 @@ public void ConfigureServices(IServiceCollection services) services.AddOpenTelemetry() .WithTracing(builder => builder .AddAspNetCoreInstrumentation() - .AddJaegerExporter()) - .StartWithHost(); + .AddJaegerExporter()); } ``` @@ -88,8 +87,7 @@ services.Configure(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), diff --git a/src/OpenTelemetry.Instrumentation.GrpcNetClient/README.md b/src/OpenTelemetry.Instrumentation.GrpcNetClient/README.md index 62bb0853aea..bfb6dfbb701 100644 --- a/src/OpenTelemetry.Instrumentation.GrpcNetClient/README.md +++ b/src/OpenTelemetry.Instrumentation.GrpcNetClient/README.md @@ -120,8 +120,7 @@ services.AddOpenTelemetry() { activity.SetTag("responseVersion", httpResponseMessage.Version); }; - }) - .StartWithHost(); + }); ``` [Processor](../../docs/trace/extending-the-sdk/README.md#processor), From 7be35d7f31e5b5fe0f798e0389e4c62dc6ea2411 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Wed, 8 Feb 2023 13:18:48 -0800 Subject: [PATCH 11/14] Support Azure Functions TelemetryHostedService dependency inspection logic. --- src/OpenTelemetry/Internal/HostingHelper.cs | 39 +++++++++++++-------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/src/OpenTelemetry/Internal/HostingHelper.cs b/src/OpenTelemetry/Internal/HostingHelper.cs index 2197e89a95e..58708f0aad6 100644 --- a/src/OpenTelemetry/Internal/HostingHelper.cs +++ b/src/OpenTelemetry/Internal/HostingHelper.cs @@ -93,7 +93,7 @@ private static bool TryAddOpenTelemetryHostedServiceIntoServiceCollection(IServi return false; } - services.TryAddSingleton(); + services.TryAddSingleton(); services.TryAddEnumerable(ServiceDescriptor.Singleton(iHostedServiceType, hostedServiceImplementation)); reason = null; @@ -105,18 +105,20 @@ private static Type CreateHostedServiceImplementation(Type iHostedServiceType) /* * Note: This code builds a class dynamically that does this... * - * class OpenTelemetryHostedService : IHostedService + * namespace OpenTelemetry.Extensions.Hosting.Implementation; + * + * class TelemetryHostedService : IHostedService * { - * private readonly TelemetryHostedService telemetryHostedService; + * private readonly TelemetryHostedServiceHelper telemetryHostedServiceHelper; * - * public OpenTelemetryHostedService(TelemetryHostedService telemetryHostedService) + * public TelemetryHostedService(TelemetryHostedServiceHelper telemetryHostedServiceHelper) * { - * this.telemetryHostedService = telemetryHostedService; + * this.telemetryHostedServiceHelper = telemetryHostedServiceHelper; * } * * public Task StartAsync(CancellationToken cancellationToken) * { - * this.telemetryHostedService.Start(); + * this.telemetryHostedServiceHelper.Start(); * return Task.CompletedTask; * } * @@ -130,7 +132,14 @@ private static Type CreateHostedServiceImplementation(Type iHostedServiceType) var getCompletedTaskMethod = typeof(Task).GetProperty(nameof(Task.CompletedTask), BindingFlags.Static | BindingFlags.Public)?.GetMethod ?? throw new InvalidOperationException("Task.CompletedTask could not be found reflectively."); - var assemblyName = new AssemblyName("OpenTelemetry.Dynamic"); + // 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); @@ -139,25 +148,25 @@ private static Type CreateHostedServiceImplementation(Type iHostedServiceType) // 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(TelemetryHostedService).Assembly.GetName().Name! }); + new object[] { typeof(TelemetryHostedServiceHelper).Assembly.GetName().Name! }); assemblyBuilder.SetCustomAttribute(ignoresAccessChecksTo); var moduleBuilder = assemblyBuilder.DefineDynamicModule(assemblyName.Name!); - var typeBuilder = moduleBuilder.DefineType("OpenTelemetryHostedService", TypeAttributes.NotPublic); + var typeBuilder = moduleBuilder.DefineType("OpenTelemetry.Extensions.Hosting.Implementation.TelemetryHostedService", TypeAttributes.NotPublic); typeBuilder.AddInterfaceImplementation(iHostedServiceType); var hostedServiceImplementationField = typeBuilder.DefineField( - "telemetryHostedService", - typeof(TelemetryHostedService), + "telemetryHostedServiceHelper", + typeof(TelemetryHostedServiceHelper), FieldAttributes.Private | FieldAttributes.InitOnly); var constructor = typeBuilder.DefineConstructor( MethodAttributes.Public, CallingConventions.Standard, - new Type[] { typeof(TelemetryHostedService) }); + new Type[] { typeof(TelemetryHostedServiceHelper) }); var constructorGenerator = constructor.GetILGenerator(); @@ -182,7 +191,7 @@ private static Type CreateHostedServiceImplementation(Type iHostedServiceType) startAsyncMethodGenerator.Emit(OpCodes.Ldfld, hostedServiceImplementationField); startAsyncMethodGenerator.Emit( OpCodes.Call, - typeof(TelemetryHostedService).GetMethod(nameof(TelemetryHostedService.Start)) ?? throw new InvalidOperationException($"{nameof(TelemetryHostedService)}.{nameof(TelemetryHostedService.Start)} could not be found reflectively.")); + 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); @@ -213,11 +222,11 @@ private static Type CreateHostedServiceImplementation(Type iHostedServiceType) ?? throw new InvalidOperationException("IHostedService implementation could not be created dynamically."); } - private sealed class TelemetryHostedService + private sealed class TelemetryHostedServiceHelper { private readonly IServiceProvider serviceProvider; - public TelemetryHostedService(IServiceProvider serviceProvider) + public TelemetryHostedServiceHelper(IServiceProvider serviceProvider) { Debug.Assert(serviceProvider != null, "serviceProvider was null"); From f368c21104c361c62b3d538ccaccbff6d0d96494 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Wed, 8 Feb 2023 14:10:46 -0800 Subject: [PATCH 12/14] Code review. --- src/OpenTelemetry.Extensions.Hosting/CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/OpenTelemetry.Extensions.Hosting/CHANGELOG.md b/src/OpenTelemetry.Extensions.Hosting/CHANGELOG.md index 65eaaffaaa7..b31b6c4ccb3 100644 --- a/src/OpenTelemetry.Extensions.Hosting/CHANGELOG.md +++ b/src/OpenTelemetry.Extensions.Hosting/CHANGELOG.md @@ -3,7 +3,9 @@ ## Unreleased * Removed the `OpenTelemetryBuilder.StartWithHost` extension and moved the - functionality into the SDK `AddOpenTelemetry` extension. + 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 From b340749343befb7fde513e5cd0e09c8d0fa1c401 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Wed, 8 Feb 2023 14:17:02 -0800 Subject: [PATCH 13/14] Code review. --- src/OpenTelemetry/Internal/OpenTelemetrySdkEventSource.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/OpenTelemetry/Internal/OpenTelemetrySdkEventSource.cs b/src/OpenTelemetry/Internal/OpenTelemetrySdkEventSource.cs index 0a5acc9a679..82a2bdd23ca 100644 --- a/src/OpenTelemetry/Internal/OpenTelemetrySdkEventSource.cs +++ b/src/OpenTelemetry/Internal/OpenTelemetrySdkEventSource.cs @@ -178,7 +178,7 @@ public void DroppedExportProcessorItems(string exportProcessorName, string expor [NonEvent] public void HostedServiceRegistrationFailure(Exception ex) { - if (this.IsEnabled(EventLevel.Warning, EventKeywords.All)) + if (this.IsEnabled(EventLevel.Error, EventKeywords.All)) { this.HostedServiceRegistrationFailure(ex.ToInvariantString()); } @@ -436,7 +436,7 @@ 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.Warning)] + [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); From dd2f8c320d5f9f0eb0e7342d2128953c4e65a9ef Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Wed, 8 Feb 2023 14:25:16 -0800 Subject: [PATCH 14/14] Code review. --- src/OpenTelemetry/Internal/OpenTelemetrySdkEventSource.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OpenTelemetry/Internal/OpenTelemetrySdkEventSource.cs b/src/OpenTelemetry/Internal/OpenTelemetrySdkEventSource.cs index 82a2bdd23ca..68a756d0d7b 100644 --- a/src/OpenTelemetry/Internal/OpenTelemetrySdkEventSource.cs +++ b/src/OpenTelemetry/Internal/OpenTelemetrySdkEventSource.cs @@ -430,7 +430,7 @@ public void HostedServiceRegistered() this.WriteEvent(48); } - [Event(49, Message = "OpenTelemetry IHostedService application services registration skipped. Reason: '{0}'", Level = EventLevel.Informational)] + [Event(49, Message = "OpenTelemetry IHostedService application services registration skipped. Reason: '{0}'", Level = EventLevel.Warning)] public void HostedServiceRegistrationSkipped(string reason) { this.WriteEvent(49, reason);