diff --git a/src/OpenTelemetry.Extensions.Hosting/CHANGELOG.md b/src/OpenTelemetry.Extensions.Hosting/CHANGELOG.md index d2f5477ca0d..8cd9562ab8e 100644 --- a/src/OpenTelemetry.Extensions.Hosting/CHANGELOG.md +++ b/src/OpenTelemetry.Extensions.Hosting/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +* If the OpenTelemetry SDK cannot start it will now throw exceptions and prevent + the host from starting. + ([#4006](https://github.com/open-telemetry/opentelemetry-dotnet/pull/4006)) + ## 1.4.0-rc.1 Released 2022-Dec-12 diff --git a/src/OpenTelemetry.Extensions.Hosting/Implementation/HostingExtensionsEventSource.cs b/src/OpenTelemetry.Extensions.Hosting/Implementation/HostingExtensionsEventSource.cs index f23f3a86226..f6f86038cfe 100644 --- a/src/OpenTelemetry.Extensions.Hosting/Implementation/HostingExtensionsEventSource.cs +++ b/src/OpenTelemetry.Extensions.Hosting/Implementation/HostingExtensionsEventSource.cs @@ -15,7 +15,6 @@ // using System.Diagnostics.Tracing; -using OpenTelemetry.Internal; namespace OpenTelemetry.Extensions.Hosting.Implementation { @@ -27,34 +26,16 @@ internal sealed class HostingExtensionsEventSource : EventSource { public static HostingExtensionsEventSource Log = new(); - [NonEvent] - public void FailedInitialize(Exception ex) + [Event(1, Message = "OpenTelemetry TracerProvider was not found in application services. Tracing will remain disabled.", Level = EventLevel.Warning)] + public void TracerProviderNotRegistered() { - if (this.IsEnabled(EventLevel.Error, EventKeywords.All)) - { - this.FailedInitialize(ex.ToInvariantString()); - } + this.WriteEvent(1); } - [NonEvent] - public void FailedOpenTelemetrySDK(Exception ex) + [Event(2, Message = "OpenTelemetry MeterProvider was not found in application services. Metrics will remain disabled.", Level = EventLevel.Warning)] + public void MeterProviderNotRegistered() { - if (this.IsEnabled(EventLevel.Error, EventKeywords.All)) - { - this.FailedOpenTelemetrySDK(ex.ToInvariantString()); - } - } - - [Event(1, Message = "An exception occurred while initializing OpenTelemetry Tracing. OpenTelemetry tracing will remain disabled. Exception: '{0}'.", Level = EventLevel.Error)] - public void FailedInitialize(string exception) - { - this.WriteEvent(1, exception); - } - - [Event(2, Message = "An exception occurred while retrieving OpenTelemetry Tracer. OpenTelemetry tracing will remain disabled. Exception: '{0}'.", Level = EventLevel.Error)] - public void FailedOpenTelemetrySDK(string exception) - { - this.WriteEvent(2, exception); + this.WriteEvent(2); } } } diff --git a/src/OpenTelemetry.Extensions.Hosting/Implementation/TelemetryHostedService.cs b/src/OpenTelemetry.Extensions.Hosting/Implementation/TelemetryHostedService.cs index 612175c1a58..78e50395b59 100644 --- a/src/OpenTelemetry.Extensions.Hosting/Implementation/TelemetryHostedService.cs +++ b/src/OpenTelemetry.Extensions.Hosting/Implementation/TelemetryHostedService.cs @@ -33,16 +33,9 @@ public TelemetryHostedService(IServiceProvider serviceProvider) public Task StartAsync(CancellationToken cancellationToken) { - try - { - // The sole purpose of this HostedService is to ensure all - // instrumentations, exporters, etc., are created and started. - Initialize(this.serviceProvider); - } - catch (Exception ex) - { - HostingExtensionsEventSource.Log.FailedOpenTelemetrySDK(ex); - } + // The sole purpose of this HostedService is to ensure all + // instrumentations, exporters, etc., are created and started. + Initialize(this.serviceProvider); return Task.CompletedTask; } @@ -57,11 +50,15 @@ internal static void Initialize(IServiceProvider serviceProvider) Debug.Assert(serviceProvider != null, "serviceProvider was null"); var meterProvider = serviceProvider.GetService(); - var tracerProvider = serviceProvider.GetService(); + if (meterProvider == null) + { + HostingExtensionsEventSource.Log.MeterProviderNotRegistered(); + } - if (meterProvider == null && tracerProvider == null) + var tracerProvider = serviceProvider.GetService(); + if (tracerProvider == null) { - throw new InvalidOperationException("Could not resolve either MeterProvider or TracerProvider through application ServiceProvider, OpenTelemetry SDK has not been initialized."); + HostingExtensionsEventSource.Log.TracerProviderNotRegistered(); } } } diff --git a/test/OpenTelemetry.Extensions.Hosting.Tests/TelemetryHostedServiceTests.cs b/test/OpenTelemetry.Extensions.Hosting.Tests/TelemetryHostedServiceTests.cs new file mode 100644 index 00000000000..f6b0c6c8ec0 --- /dev/null +++ b/test/OpenTelemetry.Extensions.Hosting.Tests/TelemetryHostedServiceTests.cs @@ -0,0 +1,84 @@ +// +// 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 => + { + builder.ConfigureBuilder((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); + } +}