[Hosting] Throw startup exceptions (#4006)

* Allow the hosting startup to throw for valid issues.

* CHANGELOG stub.

* CHANGELOG patch.

* Warning cleanup.

* Code review.
This commit is contained in:
Mikel Blanchard 2022-12-14 13:19:12 -08:00 committed by GitHub
parent 733a8689ef
commit a44a58bb7e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 105 additions and 39 deletions

View File

@ -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

View File

@ -15,7 +15,6 @@
// </copyright>
using System.Diagnostics.Tracing;
using OpenTelemetry.Internal;
namespace OpenTelemetry.Extensions.Hosting.Implementation
{
@ -27,34 +26,16 @@ namespace OpenTelemetry.Extensions.Hosting.Implementation
{
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);
}
}
}

View File

@ -33,16 +33,9 @@ internal sealed class TelemetryHostedService : IHostedService
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 sealed class TelemetryHostedService : IHostedService
Debug.Assert(serviceProvider != null, "serviceProvider was null");
var meterProvider = serviceProvider.GetService<MeterProvider>();
var tracerProvider = serviceProvider.GetService<TracerProvider>();
if (meterProvider == null && tracerProvider == null)
if (meterProvider == null)
{
throw new InvalidOperationException("Could not resolve either MeterProvider or TracerProvider through application ServiceProvider, OpenTelemetry SDK has not been initialized.");
HostingExtensionsEventSource.Log.MeterProviderNotRegistered();
}
var tracerProvider = serviceProvider.GetService<TracerProvider>();
if (tracerProvider == null)
{
HostingExtensionsEventSource.Log.TracerProviderNotRegistered();
}
}
}

View File

@ -0,0 +1,84 @@
// <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 =>
{
builder.ConfigureBuilder((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);
}
}