internal static ElasticOpenTelemetryComponents Bootstrap()

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;
		}
	}