internal static MeterProviderBuilder WithElasticDefaults()

in src/Elastic.OpenTelemetry/Extensions/MeterProviderBuilderExtensions.cs [109:248]


	internal static MeterProviderBuilder WithElasticDefaults(
		this MeterProviderBuilder builder,
		ElasticOpenTelemetryComponents components,
		IServiceCollection serviceCollection) =>
			WithElasticDefaultsCore(builder, components.Options, components, serviceCollection);

	[MethodImpl(MethodImplOptions.AggressiveInlining)]
	internal static MeterProviderBuilder WithElasticDefaults(
		this MeterProviderBuilder builder,
		IConfiguration configuration,
		IServiceCollection serviceCollection) =>
			WithElasticDefaultsCore(builder, new(configuration), null, serviceCollection);

	[MethodImpl(MethodImplOptions.AggressiveInlining)]
	internal static MeterProviderBuilder WithElasticDefaults(
		this MeterProviderBuilder builder,
		IServiceCollection serviceCollection) =>
			WithElasticDefaultsCore(builder, null, null, serviceCollection);

	[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026", Justification = "The calls to `AddSqlClientInstrumentation` and `AssemblyScanning.AddInstrumentationViaReflection` " +
		"are guarded by a RuntimeFeature.IsDynamicCodeSupported` check and therefore this method is safe to call in AoT scenarios.")]
	internal static MeterProviderBuilder WithElasticDefaultsCore(
		this MeterProviderBuilder builder,
		CompositeElasticOpenTelemetryOptions? options,
		ElasticOpenTelemetryComponents? components,
		IServiceCollection? services)
	{
		var logger = SignalBuilder.GetLogger(builder, components, options, null);

		var callCount = Interlocked.Increment(ref WithElasticDefaultsCallCount);

		if (callCount > 1)
		{
			logger.LogMultipleWithElasticDefaultsCallsWarning(callCount, nameof(MeterProviderBuilder));
		}
		else
		{
			logger.LogWithElasticDefaultsCallCount(callCount, nameof(MeterProviderBuilder));
		}

		return SignalBuilder.WithElasticDefaults(builder, Signals.Traces, options, components, services, ConfigureBuilder);

		static void ConfigureBuilder(MeterProviderBuilder builder, BuilderState builderState, IServiceCollection? services)
		{
			const string meterProviderBuilderName = nameof(MeterProviderBuilder);
			var components = builderState.Components;
			var logger = components.Logger;

			logger.LogConfiguringBuilder(meterProviderBuilderName, builderState.InstanceIdentifier);

			builder.ConfigureResource(r => r.WithElasticDefaults(builderState, services));

			// When services is not null here, the options will have already been configured by the calling code.
			if (services is null)
				builder.ConfigureServices(sc => sc.Configure<OtlpExporterOptions>(OtlpExporterDefaults.OtlpExporterOptions));

			builder.ConfigureServices(sc => sc.Configure<MetricReaderOptions>(o =>
				o.TemporalityPreference = MetricReaderTemporalityPreference.Delta));

#if NET9_0_OR_GREATER
			// .NET 9 introduced semantic convention compatible instrumentation in System.Net.Http so it's recommended to no longer
			// use the contrib instrumentation. We don't bring in the dependency for .NET 9+. However, if the consuming app depends
			// on it, it will be assumed that the user prefers it and therefore we allow the assembly scanning to add it. We don't
			// add the native meter to avoid doubling up on metrics.
			if (SignalBuilder.InstrumentationAssemblyExists("OpenTelemetry.Instrumentation.Http.dll"))
			{
				logger.LogHttpInstrumentationFound("metric", meterProviderBuilderName, builderState.InstanceIdentifier);

				if (!RuntimeFeature.IsDynamicCodeSupported)
					logger.LogWarning("The OpenTelemetry.Instrumentation.Http.dll was found alongside the executing assembly. " +
						"When using Native AOT publishing on .NET, the metrics instrumentation is not registered automatically. Either register it manually, " +
						"or remove the dependency so that the native `System.Net.Http` instrumentation (available in .NET 9) is observed instead.");
			}
			else
			{
				AddMeterWithLogging(builder, logger, "System.Net.Http", builderState.InstanceIdentifier);
			}

			// On .NET 9, the contrib runtime instrumentation is no longer required. If the dependency exists,
			// it will be registered via the reflection-based assembly scanning.
			if (SignalBuilder.InstrumentationAssemblyExists("OpenTelemetry.Instrumentation.Runtime.dll"))
			{
				logger.LogRuntimeInstrumentationFound();

				// For native AOT scenarios, the reflection-based assembly scanning will not run.
				// Therefore, we log a warning since no runtime metric instrumentation will be automatically registered.
				// In this scenario, the consumer must register the contrib instrumentation manually, or
				// remove the dependency so that the native .NET 9 runtime metric instrumentation source will be added
				// instead.
				if (!RuntimeFeature.IsDynamicCodeSupported)
					logger.LogWarning("The OpenTelemetry.Instrumentation.Runtime.dll was found alongside the executing assembly. " +
						"When using Native AOT publishing on .NET, the metric instrumentation is not registered automatically. Either register it manually, " +
						"or remove the dependency so that the native `System.Runtime` instrumentation (available in .NET 9) is observed instead.");
			}
			else
			{
				AddMeterWithLogging(builder, logger, "System.Runtime", builderState.InstanceIdentifier);
			}
#endif

			if (SignalBuilder.InstrumentationAssemblyExists("OpenTelemetry.Instrumentation.AspNetCore.dll"))
			{
				AddMeterWithLogging(builder, logger, "Microsoft.AspNetCore.Hosting", builderState.InstanceIdentifier);
				AddMeterWithLogging(builder, logger, "Microsoft.AspNetCore.Routing", builderState.InstanceIdentifier);
				AddMeterWithLogging(builder, logger, "Microsoft.AspNetCore.Diagnostics", builderState.InstanceIdentifier);
				AddMeterWithLogging(builder, logger, "Microsoft.AspNetCore.RateLimiting", builderState.InstanceIdentifier);
				AddMeterWithLogging(builder, logger, "Microsoft.AspNetCore.HeaderParsing", builderState.InstanceIdentifier);
				AddMeterWithLogging(builder, logger, "Microsoft.AspNetCore.Server.Kestrel", builderState.InstanceIdentifier);
				AddMeterWithLogging(builder, logger, "Microsoft.AspNetCore.Http.Connections", builderState.InstanceIdentifier);
			}

			AddMeterWithLogging(builder, logger, "System.Net.NameResolution", builderState.InstanceIdentifier);

#if NET
			if (RuntimeFeature.IsDynamicCodeSupported)
#endif
			{
				SignalBuilder.AddInstrumentationViaReflection(builder, builderState.Components,
					ContribMetricsInstrumentation.GetMetricsInstrumentationAssembliesInfo(), builderState.InstanceIdentifier);
			}

			if (components.Options.SkipOtlpExporter)
			{
				logger.LogSkippingOtlpExporter(nameof(Signals.Traces), nameof(MeterProviderBuilder), builderState.InstanceIdentifier);
			}
			else
			{
				builder.AddOtlpExporter();
			}

			logger.LogConfiguredSignalProvider(nameof(Signals.Logs), nameof(MeterProviderBuilder), builderState.InstanceIdentifier);
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		static void AddMeterWithLogging(MeterProviderBuilder builder, ILogger logger, string meterName, string builderIdentifier)
		{
			builder.AddMeter(meterName);
			logger.LogMeterAdded(meterName, builderIdentifier);
		}
	}