in src/Elastic.OpenTelemetry.Core/ElasticOpenTelemetry.cs [24:130]
internal static ElasticOpenTelemetryComponents Bootstrap(SdkActivationMethod activationMethod) =>
Bootstrap(activationMethod, CompositeElasticOpenTelemetryOptions.DefaultOptions, null);
internal static ElasticOpenTelemetryComponents Bootstrap(CompositeElasticOpenTelemetryOptions options, IServiceCollection? services) =>
Bootstrap(SdkActivationMethod.NuGet, options, services);
/// <summary>
/// Shared bootstrap routine for the Elastic Distribution of OpenTelemetry .NET.
/// Used to ensure auto instrumentation and manual instrumentation bootstrap the same way.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static ElasticOpenTelemetryComponents Bootstrap(
SdkActivationMethod activationMethod,
CompositeElasticOpenTelemetryOptions options,
IServiceCollection? services)
{
ActivationMethod = activationMethod;
ElasticOpenTelemetryComponents components;
// We only expect this to be allocated a handful of times, generally once.
var stackTrace = new StackTrace(true);
var invocationCount = Interlocked.Increment(ref BootstrapCounter);
// Strictly speaking, we probably don't require locking here as the registration of
// OpenTelemetry is expected to run sequentially. That said, the overhead is low
// since this is called infrequently.
using (var scope = Lock.EnterScope())
{
// If an IServiceCollection is provided, we attempt to access any existing
// components to reuse them before accessing any potential shared components.
if (services is not null)
{
if (TryGetExistingComponentsFromServiceCollection(services, out var existingComponents))
{
existingComponents.Logger.LogBootstrapInvoked(invocationCount);
return existingComponents;
}
}
// We don't have components assigned for this IServiceCollection, attempt to use
// the existing SharedComponents, or create components.
if (TryGetSharedComponents(options, out var sharedComponents))
{
components = sharedComponents;
}
else
{
components = CreateComponents(activationMethod, options, stackTrace);
components.Logger.LogSharedComponentsNotReused();
SharedComponents.Add(components);
}
components.Logger.LogBootstrapInvoked(invocationCount);
services?.AddSingleton(components);
return components;
}
static bool TryGetExistingComponentsFromServiceCollection(IServiceCollection? services, [NotNullWhen(true)] out ElasticOpenTelemetryComponents? components)
{
components = null;
if (services?.FirstOrDefault(s => s.ServiceType == typeof(ElasticOpenTelemetryComponents))
?.ImplementationInstance as ElasticOpenTelemetryComponents is not { } existingComponents)
return false;
existingComponents.Logger.LogServiceCollectionComponentsReused();
components = existingComponents;
return true;
}
static bool TryGetSharedComponents(CompositeElasticOpenTelemetryOptions options,
[NotNullWhen(true)] out ElasticOpenTelemetryComponents? sharedComponents)
{
sharedComponents = null;
foreach (var cachedComponents in SharedComponents)
{
if (cachedComponents.Options.Equals(options))
{
sharedComponents = cachedComponents;
cachedComponents.Logger.LogSharedComponentsReused();
return true;
}
}
return false;
}
static ElasticOpenTelemetryComponents CreateComponents(
SdkActivationMethod activationMethod,
CompositeElasticOpenTelemetryOptions options,
StackTrace stackTrace)
{
var logger = new CompositeLogger(options);
var eventListener = new LoggingEventListener(logger, options);
var components = new ElasticOpenTelemetryComponents(logger, eventListener, options);
logger.LogDistroPreamble(activationMethod, components);
logger.LogComponentsCreated(Environment.NewLine, stackTrace);
return components;
}
}