-
Notifications
You must be signed in to change notification settings - Fork 862
Add otlp log extension methods for LoggerProviderBuilder #5103
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 32 commits
f206708
aa42126
b0c97f8
e11d4bb
36e8560
6723a04
3653fd3
64f8df9
b15421a
4255964
0b853e7
3e2aff0
ac70c78
06893fe
7f28ee5
5fe64d3
c828fcc
6c4d67b
7276894
7a7e962
df0adcb
6cf59f0
f08622d
35406ae
1f99014
ac0494b
a350e30
fa4d229
04a036c
d09f1b0
fa2857a
604ccf6
c19868d
e208fbc
c4e795f
2f51df2
e17f4e7
66ea5e3
8f15ca0
11d7f4c
df362e4
a332701
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| static OpenTelemetry.Logs.OtlpLogExporterHelperExtensions.AddOtlpExporter(this OpenTelemetry.Logs.LoggerProviderBuilder! builder) -> OpenTelemetry.Logs.LoggerProviderBuilder! | ||
| static OpenTelemetry.Logs.OtlpLogExporterHelperExtensions.AddOtlpExporter(this OpenTelemetry.Logs.LoggerProviderBuilder! builder, string? name, System.Action<OpenTelemetry.Exporter.OtlpExporterOptions!, OpenTelemetry.Logs.LogRecordExportProcessorOptions!>? configureExporterAndProcessor) -> OpenTelemetry.Logs.LoggerProviderBuilder! | ||
| static OpenTelemetry.Logs.OtlpLogExporterHelperExtensions.AddOtlpExporter(this OpenTelemetry.Logs.LoggerProviderBuilder! builder, string? name, System.Action<OpenTelemetry.Exporter.OtlpExporterOptions!>? configureExporter) -> OpenTelemetry.Logs.LoggerProviderBuilder! | ||
| static OpenTelemetry.Logs.OtlpLogExporterHelperExtensions.AddOtlpExporter(this OpenTelemetry.Logs.LoggerProviderBuilder! builder, System.Action<OpenTelemetry.Exporter.OtlpExporterOptions!, OpenTelemetry.Logs.LogRecordExportProcessorOptions!>! configureExporterAndProcessor) -> OpenTelemetry.Logs.LoggerProviderBuilder! | ||
| static OpenTelemetry.Logs.OtlpLogExporterHelperExtensions.AddOtlpExporter(this OpenTelemetry.Logs.LoggerProviderBuilder! builder, System.Action<OpenTelemetry.Exporter.OtlpExporterOptions!>! configureExporter) -> OpenTelemetry.Logs.LoggerProviderBuilder! |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,6 +4,9 @@ | |
| #nullable enable | ||
|
|
||
| using System.Diagnostics; | ||
| #if EXPOSE_EXPERIMENTAL_FEATURES && NET8_0_OR_GREATER | ||
| using System.Diagnostics.CodeAnalysis; | ||
| #endif | ||
| using Microsoft.Extensions.Configuration; | ||
| using Microsoft.Extensions.DependencyInjection; | ||
| using Microsoft.Extensions.Logging; | ||
|
|
@@ -106,29 +109,280 @@ public static OpenTelemetryLoggerOptions AddOtlpExporter( | |
| }); | ||
| } | ||
|
|
||
| #if EXPOSE_EXPERIMENTAL_FEATURES | ||
| /// <summary> | ||
| /// Adds an OTLP exporter to the LoggerProvider. | ||
| /// </summary> | ||
| /// <remarks><b>WARNING</b>: This is an experimental API which might change or be removed in the future. Use at your own risk.</remarks> | ||
| /// <param name="builder"><see cref="LoggerProviderBuilder"/> builder to use.</param> | ||
| /// <returns>The instance of <see cref="LoggerProviderBuilder"/> to chain the calls.</returns> | ||
| #if NET8_0_OR_GREATER | ||
| [Experimental(DiagnosticDefinitions.LoggerProviderExperimentalApi, UrlFormat = DiagnosticDefinitions.ExperimentalApiUrlFormat)] | ||
| #endif | ||
| public | ||
| #else | ||
| /// <summary> | ||
| /// Adds an OTLP exporter to the LoggerProvider. | ||
| /// </summary> | ||
| /// <param name="builder"><see cref="LoggerProviderBuilder"/> builder to use.</param> | ||
| /// <returns>The instance of <see cref="LoggerProviderBuilder"/> to chain the calls.</returns> | ||
| internal | ||
| #endif | ||
| static LoggerProviderBuilder AddOtlpExporter(this LoggerProviderBuilder builder) | ||
| => AddOtlpExporter(builder, name: null, configureExporter: null); | ||
|
|
||
| #if EXPOSE_EXPERIMENTAL_FEATURES | ||
| /// <summary> | ||
| /// Adds an OTLP exporter to the LoggerProvider. | ||
| /// </summary> | ||
| /// <remarks><b>WARNING</b>: This is an experimental API which might change or be removed in the future. Use at your own risk.</remarks> | ||
| /// <param name="builder"><see cref="LoggerProviderBuilder"/> builder to use.</param> | ||
| /// <param name="configureExporter">Callback action for configuring <see cref="OtlpExporterOptions"/>.</param> | ||
| /// <returns>The instance of <see cref="LoggerProviderBuilder"/> to chain the calls.</returns> | ||
| #if NET8_0_OR_GREATER | ||
| [Experimental(DiagnosticDefinitions.LoggerProviderExperimentalApi, UrlFormat = DiagnosticDefinitions.ExperimentalApiUrlFormat)] | ||
| #endif | ||
| public | ||
| #else | ||
| /// <summary> | ||
| /// Adds an OTLP exporter to the LoggerProvider. | ||
| /// </summary> | ||
| /// <param name="builder"><see cref="LoggerProviderBuilder"/> builder to use.</param> | ||
| /// <param name="configureExporter">Callback action for configuring <see cref="OtlpExporterOptions"/>.</param> | ||
| /// <returns>The instance of <see cref="LoggerProviderBuilder"/> to chain the calls.</returns> | ||
| internal | ||
| #endif | ||
| static LoggerProviderBuilder AddOtlpExporter(this LoggerProviderBuilder builder, Action<OtlpExporterOptions> configureExporter) | ||
| => AddOtlpExporter(builder, name: null, configureExporter); | ||
|
|
||
| #if EXPOSE_EXPERIMENTAL_FEATURES | ||
| /// <summary> | ||
| /// Adds an OTLP exporter to the LoggerProvider. | ||
| /// </summary> | ||
| /// <remarks><b>WARNING</b>: This is an experimental API which might change or be removed in the future. Use at your own risk.</remarks> | ||
| /// <param name="builder"><see cref="LoggerProviderBuilder"/> builder to use.</param> | ||
| /// <param name="configureExporterAndProcessor">Callback action for | ||
| /// configuring <see cref="OtlpExporterOptions"/> and <see | ||
| /// cref="LogRecordExportProcessorOptions"/>.</param> | ||
| /// <returns>The instance of <see cref="LoggerProviderBuilder"/> to chain the calls.</returns> | ||
| #if NET8_0_OR_GREATER | ||
| [Experimental(DiagnosticDefinitions.LoggerProviderExperimentalApi, UrlFormat = DiagnosticDefinitions.ExperimentalApiUrlFormat)] | ||
| #endif | ||
| public | ||
| #else | ||
| internal | ||
| #endif | ||
| static LoggerProviderBuilder AddOtlpExporter(this LoggerProviderBuilder builder, Action<OtlpExporterOptions, LogRecordExportProcessorOptions> configureExporterAndProcessor) | ||
| => AddOtlpExporter(builder, name: null, configureExporterAndProcessor); | ||
|
|
||
| #if EXPOSE_EXPERIMENTAL_FEATURES | ||
| /// <summary> | ||
| /// Adds OpenTelemetry Protocol (OTLP) exporter to the LoggerProvider. | ||
| /// </summary> | ||
| /// <remarks><b>WARNING</b>: This is an experimental API which might change or be removed in the future. Use at your own risk.</remarks> | ||
| /// <param name="builder"><see cref="LoggerProviderBuilder"/> builder to use.</param> | ||
| /// <param name="name">Optional name which is used when retrieving options.</param> | ||
| /// <param name="configureExporter">Optional callback action for configuring <see cref="OtlpExporterOptions"/>.</param> | ||
| /// <returns>The instance of <see cref="LoggerProviderBuilder"/> to chain the calls.</returns> | ||
| #if NET8_0_OR_GREATER | ||
| [Experimental(DiagnosticDefinitions.LoggerProviderExperimentalApi, UrlFormat = DiagnosticDefinitions.ExperimentalApiUrlFormat)] | ||
| #endif | ||
| public | ||
| #else | ||
| /// <summary> | ||
| /// Adds OpenTelemetry Protocol (OTLP) exporter to the LoggerProvider. | ||
| /// </summary> | ||
| /// <param name="builder"><see cref="LoggerProviderBuilder"/> builder to use.</param> | ||
| /// <param name="name">Optional name which is used when retrieving options.</param> | ||
| /// <param name="configureExporter">Optional callback action for configuring <see cref="OtlpExporterOptions"/>.</param> | ||
| /// <returns>The instance of <see cref="LoggerProviderBuilder"/> to chain the calls.</returns> | ||
| internal | ||
| #endif | ||
| static LoggerProviderBuilder AddOtlpExporter( | ||
| this LoggerProviderBuilder builder, | ||
| string? name, | ||
| Action<OtlpExporterOptions>? configureExporter) | ||
| { | ||
| var finalOptionsName = name ?? Options.DefaultName; | ||
|
|
||
| builder.ConfigureServices(services => | ||
| { | ||
| if (name != null && configureExporter != null) | ||
| { | ||
| // If we are using named options we register the | ||
| // configuration delegate into options pipeline. | ||
| services.Configure(finalOptionsName, configureExporter); | ||
utpilla marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| RegisterOptions(services); | ||
| }); | ||
|
|
||
| return builder.AddProcessor(sp => | ||
| { | ||
| OtlpExporterOptions exporterOptions; | ||
|
|
||
| if (name == null) | ||
| { | ||
| // If we are NOT using named options we create a new | ||
| // instance always. The reason for this is | ||
| // OtlpExporterOptions is shared by all signals. Without a | ||
| // name, delegates for all signals will mix together. See: | ||
| // https://github.com/open-telemetry/opentelemetry-dotnet/issues/4043 | ||
| exporterOptions = sp.GetRequiredService<IOptionsFactory<OtlpExporterOptions>>().Create(finalOptionsName); | ||
|
|
||
| // Configuration delegate is executed inline on the fresh instance. | ||
| configureExporter?.Invoke(exporterOptions); | ||
| } | ||
| else | ||
| { | ||
| // When using named options we can properly utilize Options | ||
| // API to create or reuse an instance. | ||
| exporterOptions = sp.GetRequiredService<IOptionsMonitor<OtlpExporterOptions>>().Get(finalOptionsName); | ||
| } | ||
|
|
||
| // Note: Not using finalOptionsName here for SdkLimitOptions. | ||
| // There should only be one provider for a given service | ||
| // collection so SdkLimitOptions is treated as a single default | ||
| // instance. | ||
| var sdkLimitOptions = sp.GetRequiredService<IOptionsMonitor<SdkLimitOptions>>().CurrentValue; | ||
|
|
||
| return BuildOtlpLogExporter( | ||
| sp, | ||
| exporterOptions, | ||
| sp.GetRequiredService<IOptionsMonitor<LogRecordExportProcessorOptions>>().Get(finalOptionsName), | ||
| sdkLimitOptions, | ||
| sp.GetRequiredService<IOptionsMonitor<ExperimentalOptions>>().Get(finalOptionsName)); | ||
| }); | ||
| } | ||
|
|
||
| #if EXPOSE_EXPERIMENTAL_FEATURES | ||
| /// <summary> | ||
| /// Adds an OTLP exporter to the LoggerProvider. | ||
| /// </summary> | ||
| /// <remarks><b>WARNING</b>: This is an experimental API which might change or be removed in the future. Use at your own risk.</remarks> | ||
| /// <param name="builder"><see cref="LoggerProviderBuilder"/> builder to use.</param> | ||
| /// <param name="name">Optional name which is used when retrieving options.</param> | ||
| /// <param name="configureExporterAndProcessor">Optional callback action for | ||
| /// configuring <see cref="OtlpExporterOptions"/> and <see | ||
| /// cref="LogRecordExportProcessorOptions"/>.</param> | ||
| /// <returns>The instance of <see cref="LoggerProviderBuilder"/> to chain the calls.</returns> | ||
| #if NET8_0_OR_GREATER | ||
| [Experimental(DiagnosticDefinitions.LoggerProviderExperimentalApi, UrlFormat = DiagnosticDefinitions.ExperimentalApiUrlFormat)] | ||
| #endif | ||
| public | ||
| #else | ||
| /// <summary> | ||
| /// Adds an OTLP exporter to the LoggerProvider. | ||
| /// </summary> | ||
| /// <param name="builder"><see cref="LoggerProviderBuilder"/> builder to use.</param> | ||
| /// <param name="name">Optional name which is used when retrieving options.</param> | ||
| /// <param name="configureExporterAndProcessor">Optional callback action for | ||
| /// configuring <see cref="OtlpExporterOptions"/> and <see | ||
| /// cref="LogRecordExportProcessorOptions"/>.</param> | ||
| /// <returns>The instance of <see cref="LoggerProviderBuilder"/> to chain the calls.</returns> | ||
| internal | ||
| #endif | ||
| static LoggerProviderBuilder AddOtlpExporter( | ||
| this LoggerProviderBuilder builder, | ||
| string? name, | ||
| Action<OtlpExporterOptions, LogRecordExportProcessorOptions>? configureExporterAndProcessor) | ||
| { | ||
| var finalOptionsName = name ?? Options.DefaultName; | ||
|
|
||
| builder.ConfigureServices(RegisterOptions); | ||
|
|
||
| return builder.AddProcessor(sp => | ||
| { | ||
| OtlpExporterOptions exporterOptions; | ||
|
|
||
| if (name == null) | ||
| { | ||
| // If we are NOT using named options we create a new | ||
| // instance always. The reason for this is | ||
| // OtlpExporterOptions is shared by all signals. Without a | ||
| // name, delegates for all signals will mix together. See: | ||
| // https://github.com/open-telemetry/opentelemetry-dotnet/issues/4043 | ||
| exporterOptions = sp.GetRequiredService<IOptionsFactory<OtlpExporterOptions>>().Create(finalOptionsName); | ||
| } | ||
| else | ||
| { | ||
| // When using named options we can properly utilize Options | ||
| // API to create or reuse an instance. | ||
| exporterOptions = sp.GetRequiredService<IOptionsMonitor<OtlpExporterOptions>>().Get(finalOptionsName); | ||
| } | ||
|
|
||
| var processorOptions = sp.GetRequiredService<IOptionsMonitor<LogRecordExportProcessorOptions>>().Get(finalOptionsName); | ||
|
|
||
| // Configuration delegate is executed inline. | ||
| configureExporterAndProcessor?.Invoke(exporterOptions, processorOptions); | ||
|
|
||
| // Note: Not using finalOptionsName here for SdkLimitOptions. | ||
| // There should only be one provider for a given service | ||
| // collection so SdkLimitOptions is treated as a single default | ||
| // instance. | ||
| var sdkLimitOptions = sp.GetRequiredService<IOptionsMonitor<SdkLimitOptions>>().CurrentValue; | ||
|
|
||
| return BuildOtlpLogExporter( | ||
| sp, | ||
| exporterOptions, | ||
| processorOptions, | ||
| sdkLimitOptions, | ||
| sp.GetRequiredService<IOptionsMonitor<ExperimentalOptions>>().Get(finalOptionsName)); | ||
| }); | ||
| } | ||
|
|
||
| internal static BaseProcessor<LogRecord> BuildOtlpLogExporter( | ||
| IServiceProvider sp, | ||
| OtlpExporterOptions exporterOptions, | ||
| LogRecordExportProcessorOptions processorOptions, | ||
| Func<BaseExporter<LogRecord>, BaseExporter<LogRecord>>? configureExporterInstance = null) | ||
| { | ||
| if (sp == null) | ||
| { | ||
| throw new ArgumentNullException(nameof(sp)); | ||
| } | ||
|
|
||
| Debug.Assert(exporterOptions != null, "exporterOptions was null"); | ||
| Debug.Assert(processorOptions != null, "processorOptions was null"); | ||
| Debug.Assert(sp != null, "sp was null"); | ||
|
|
||
| var config = sp.GetRequiredService<IConfiguration>(); | ||
| var config = sp!.GetRequiredService<IConfiguration>(); | ||
|
|
||
| var sdkLimitOptions = new SdkLimitOptions(config); | ||
|
||
| var experimentalOptions = new ExperimentalOptions(config); | ||
|
|
||
| return BuildOtlpLogExporter(sp!, exporterOptions, processorOptions, sdkLimitOptions, experimentalOptions, configureExporterInstance); | ||
| } | ||
|
|
||
| private static BaseProcessor<LogRecord> BuildOtlpLogExporter( | ||
| IServiceProvider sp, | ||
| OtlpExporterOptions exporterOptions, | ||
| LogRecordExportProcessorOptions processorOptions, | ||
| SdkLimitOptions sdkLimitOptions, | ||
| ExperimentalOptions experimentalOptions, | ||
| Func<BaseExporter<LogRecord>, BaseExporter<LogRecord>>? configureExporterInstance = null) | ||
| { | ||
| // Note: sp is not currently used by this method but it should be used | ||
| // at some point for IHttpClientFactory integration. | ||
| Debug.Assert(sp != null, "sp was null"); | ||
| Debug.Assert(exporterOptions != null, "exporterOptions was null"); | ||
| Debug.Assert(processorOptions != null, "processorOptions was null"); | ||
| Debug.Assert(sdkLimitOptions != null, "sdkLimitOptions was null"); | ||
| Debug.Assert(experimentalOptions != null, "experimentalOptions was null"); | ||
|
|
||
| /* | ||
| * Note: | ||
| * | ||
| * We don't currently enable IHttpClientFactory for OtlpLogExporter. | ||
| * | ||
| * The DefaultHttpClientFactory requires the ILoggerFactory in its ctor: | ||
| * https://github.com/dotnet/runtime/blob/fa40ecf7d36bf4e31d7ae968807c1c529bac66d6/src/libraries/Microsoft.Extensions.Http/src/DefaultHttpClientFactory.cs#L64 | ||
| * | ||
| * This creates a circular reference: ILoggerFactory -> | ||
| * OpenTelemetryLoggerProvider -> OtlpLogExporter -> IHttpClientFactory | ||
| * -> ILoggerFactory | ||
| * | ||
| * exporterOptions.TryEnableIHttpClientFactoryIntegration(sp, | ||
| * "OtlpLogExporter"); | ||
| */ | ||
|
|
||
| BaseExporter<LogRecord> otlpExporter = new OtlpLogExporter( | ||
| exporterOptions!, | ||
| sdkLimitOptions, | ||
| experimentalOptions); | ||
| sdkLimitOptions!, | ||
| experimentalOptions!); | ||
|
|
||
| if (configureExporterInstance != null) | ||
| { | ||
|
|
@@ -152,6 +406,13 @@ internal static BaseProcessor<LogRecord> BuildOtlpLogExporter( | |
| } | ||
| } | ||
|
|
||
| private static void RegisterOptions(IServiceCollection services) | ||
| { | ||
| OtlpExporterOptions.RegisterOtlpExporterOptionsFactory(services); | ||
| services.RegisterOptionsFactory(configuration => new SdkLimitOptions(configuration)); | ||
| services.RegisterOptionsFactory(configuration => new ExperimentalOptions(configuration)); | ||
| } | ||
|
|
||
| private static OtlpExporterOptions GetOtlpExporterOptions(IServiceProvider sp, string? name, string finalName) | ||
| { | ||
| // Note: If OtlpExporter has been registered for tracing and/or metrics | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.