From b060f5175cb4ce72aaff3e51a2e2a97fce232ef6 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Fri, 23 Feb 2024 22:18:35 -0800 Subject: [PATCH 01/48] OtlpExporter cross-cutting registration extension rough draft. --- ...nTelemetryBuilderOtlpExporterExtensions.cs | 111 +++++++++++++++++ .../OtlpExporterBuilderOptions.cs | 82 +++++++++++++ .../OtlpExporterOptions.cs | 115 +++++++++++++++--- ...viderBuilderServiceCollectionExtensions.cs | 8 +- .../OpenTelemetryBuilderSdkLogsExtensions.cs | 28 +++++ ...penTelemetryBuilderSdkMetricsExtensions.cs | 28 +++++ .../Metrics/MetricReaderOptions.cs | 11 +- .../Trace/ActivityExportProcessorOptions.cs | 49 ++++++++ .../OpenTelemetryBuilderSdkTraceExtensions.cs | 28 +++++ src/Shared/Options/ConfigurationExtensions.cs | 4 +- .../Options/DelegatingOptionsFactory.cs | 2 +- 11 files changed, 444 insertions(+), 22 deletions(-) create mode 100644 src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OpenTelemetryBuilderOtlpExporterExtensions.cs create mode 100644 src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterBuilderOptions.cs create mode 100644 src/OpenTelemetry/Logs/Builder/OpenTelemetryBuilderSdkLogsExtensions.cs create mode 100644 src/OpenTelemetry/Metrics/Builder/OpenTelemetryBuilderSdkMetricsExtensions.cs create mode 100644 src/OpenTelemetry/Trace/ActivityExportProcessorOptions.cs create mode 100644 src/OpenTelemetry/Trace/Builder/OpenTelemetryBuilderSdkTraceExtensions.cs diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OpenTelemetryBuilderOtlpExporterExtensions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OpenTelemetryBuilderOtlpExporterExtensions.cs new file mode 100644 index 00000000000..35495522a61 --- /dev/null +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OpenTelemetryBuilderOtlpExporterExtensions.cs @@ -0,0 +1,111 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#nullable enable + +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using OpenTelemetry.Exporter; +using OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation; +using OpenTelemetry.Internal; +using OpenTelemetry.Logs; +using OpenTelemetry.Metrics; +using OpenTelemetry.Trace; + +namespace OpenTelemetry; + +public static class OpenTelemetryBuilderOtlpExporterExtensions +{ + public static IOpenTelemetryBuilder AddOtlpExporter( + this IOpenTelemetryBuilder builder) + => AddOtlpExporter(builder, name: null, configure: null); + + public static IOpenTelemetryBuilder AddOtlpExporter( + this IOpenTelemetryBuilder builder, + Action? configure) + => AddOtlpExporter(builder, name: null, configure); + + public static IOpenTelemetryBuilder AddOtlpExporter( + this IOpenTelemetryBuilder builder, + string? name, + Action? configure) + { + Guard.ThrowIfNull(builder); + + builder.Services.RegisterOptionsFactory(configuration => new SdkLimitOptions(configuration)); + builder.Services.RegisterOptionsFactory(configuration => new ExperimentalOptions(configuration)); + + builder.Services.RegisterOptionsFactory( + (sp, config, name) => + new OtlpExporterBuilderOptions( + config, + sp.GetRequiredService>().Get(name))); + + name ??= Options.DefaultName; + + if (configure != null) + { + builder.Services.Configure(name, configure); + } + + builder.Services.ConfigureOpenTelemetryLoggerProvider( + (sp, logging) => + { + var builderOptions = GetOptions(sp, name); + + if (!builderOptions.AddToLoggerProvider) + { + return; + } + + logging.AddProcessor( + OtlpLogExporterHelperExtensions.BuildOtlpLogExporter( + sp, + OtlpExporterOptions.Merge(builderOptions.DefaultOptions, builderOptions.LoggingOptions), + sp.GetRequiredService>().Get(name), + sp.GetRequiredService>().CurrentValue, + sp.GetRequiredService>().Get(name))); + }); + + builder.Services.ConfigureOpenTelemetryMeterProvider( + (sp, metrics) => + { + var builderOptions = GetOptions(sp, name); + + if (!builderOptions.AddToMeterProvider) + { + return; + } + + metrics.AddReader( + OtlpMetricExporterExtensions.BuildOtlpExporterMetricReader( + OtlpExporterOptions.Merge(builderOptions.DefaultOptions, builderOptions.MetricsOptions), + sp.GetRequiredService>().Get(name), + sp)); + }); + + builder.Services.ConfigureOpenTelemetryTracerProvider( + (sp, tracing) => + { + var builderOptions = GetOptions(sp, name); + + if (!builderOptions.AddToTracerProvider) + { + return; + } + + tracing.AddProcessor( + OtlpTraceExporterHelperExtensions.BuildOtlpExporterProcessor( + OtlpExporterOptions.Merge(builderOptions.DefaultOptions, builderOptions.TracingOptions), + sp.GetRequiredService>().CurrentValue, + sp)); + }); + + return builder; + + static OtlpExporterBuilderOptions GetOptions(IServiceProvider sp, string name) + { + return sp.GetRequiredService>().Get(name); + } + } +} diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterBuilderOptions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterBuilderOptions.cs new file mode 100644 index 00000000000..e29c46a5531 --- /dev/null +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterBuilderOptions.cs @@ -0,0 +1,82 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#nullable enable + +using Microsoft.Extensions.Configuration; +using OpenTelemetry.Exporter; +using OpenTelemetry.Internal; +using OpenTelemetry.Trace; + +namespace OpenTelemetry; + +public sealed class OtlpExporterBuilderOptions +{ + internal OtlpExporterBuilderOptions( + IConfiguration configuration, + ActivityExportProcessorOptions defaultExportProcessorOptions) + { + this.DefaultOptions = new OtlpExporterOptions(configuration, defaultExportProcessorOptions); + + var emptyConfiguration = new ConfigurationBuilder().Build(); + + this.LoggingOptions = new OtlpExporterOptions(emptyConfiguration, defaultExportProcessorOptions: null); + + this.MetricsOptions = new OtlpExporterOptions(emptyConfiguration, defaultExportProcessorOptions: null); + + this.TracingOptions = new OtlpExporterOptions(emptyConfiguration, defaultExportProcessorOptions); + + if (configuration.TryGetUriValue("OTEL_EXPORTER_OTLP_LOGS_ENDPOINT", out var endpoint)) + { + this.LoggingOptions.Endpoint = endpoint; + } + + if (configuration.TryGetValue( + "OTEL_EXPORTER_OTLP_LOGS_PROTOCOL", + OtlpExportProtocolParser.TryParse, + out var protocol)) + { + this.LoggingOptions.Protocol = protocol; + } + + if (configuration.TryGetUriValue("OTEL_EXPORTER_OTLP_METRICS_ENDPOINT", out endpoint)) + { + this.MetricsOptions.Endpoint = endpoint; + } + + if (configuration.TryGetValue( + "OTEL_EXPORTER_OTLP_METRICS_PROTOCOL", + OtlpExportProtocolParser.TryParse, + out protocol)) + { + this.MetricsOptions.Protocol = protocol; + } + + if (configuration.TryGetUriValue("OTEL_EXPORTER_OTLP_TRACES_ENDPOINT", out endpoint)) + { + this.TracingOptions.Endpoint = endpoint; + } + + if (configuration.TryGetValue( + "OTEL_EXPORTER_OTLP_TRACES_PROTOCOL", + OtlpExportProtocolParser.TryParse, + out protocol)) + { + this.TracingOptions.Protocol = protocol; + } + } + + public bool AddToLoggerProvider { get; set; } = true; + + public bool AddToMeterProvider { get; set; } = true; + + public bool AddToTracerProvider { get; set; } = true; + + public OtlpExporterOptions DefaultOptions { get; } + + public OtlpExporterOptions LoggingOptions { get; } + + public OtlpExporterOptions MetricsOptions { get; } + + public OtlpExporterOptions TracingOptions { get; } +} diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterOptions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterOptions.cs index e4c820cf58a..373a0415b55 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterOptions.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterOptions.cs @@ -1,6 +1,8 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 +#nullable enable + using System.Diagnostics; using System.Reflection; #if NETFRAMEWORK @@ -39,22 +41,27 @@ public class OtlpExporterOptions private const OtlpExportProtocol DefaultOtlpExportProtocol = OtlpExportProtocol.Grpc; private const string UserAgentProduct = "OTel-OTLP-Exporter-Dotnet"; - private Uri endpoint; + private Uri? endpoint; + private OtlpExportProtocol? protocol; + private int? timeoutMilliseconds; + private ExportProcessorType? exportProcessorType; + private ActivityExportProcessorOptions? defaultProcessorOptions; + private BatchExportProcessorOptions? batchExportProcessorOptions; + private Func? httpClientFactory; /// /// Initializes a new instance of the class. /// public OtlpExporterOptions() - : this(new ConfigurationBuilder().AddEnvironmentVariables().Build(), new()) + : this(new ConfigurationBuilder().AddEnvironmentVariables().Build(), defaultExportProcessorOptions: null) { } internal OtlpExporterOptions( IConfiguration configuration, - BatchExportActivityProcessorOptions defaultBatchOptions) + ActivityExportProcessorOptions? defaultExportProcessorOptions) { Debug.Assert(configuration != null, "configuration was null"); - Debug.Assert(defaultBatchOptions != null, "defaultBatchOptions was null"); if (configuration.TryGetUriValue(EndpointEnvVarName, out var endpoint)) { @@ -79,7 +86,7 @@ internal OtlpExporterOptions( this.Protocol = protocol; } - this.HttpClientFactory = this.DefaultHttpClientFactory = () => + this.DefaultHttpClientFactory = () => { return new HttpClient { @@ -87,7 +94,7 @@ internal OtlpExporterOptions( }; }; - this.BatchExportProcessorOptions = defaultBatchOptions; + this.defaultProcessorOptions = defaultExportProcessorOptions; } /// @@ -122,29 +129,54 @@ public Uri Endpoint /// Gets or sets optional headers for the connection. Refer to the /// specification for information on the expected format for Headers. /// - public string Headers { get; set; } + public string? Headers { get; set; } /// /// Gets or sets the max waiting time (in milliseconds) for the backend to process each batch. The default value is 10000. /// - public int TimeoutMilliseconds { get; set; } = 10000; + public int TimeoutMilliseconds + { + get => this.timeoutMilliseconds ?? 10000; + set => this.timeoutMilliseconds = value; + } /// /// Gets or sets the the OTLP transport protocol. Supported values: Grpc and HttpProtobuf. /// - public OtlpExportProtocol Protocol { get; set; } = DefaultOtlpExportProtocol; + public OtlpExportProtocol Protocol + { + get => this.protocol ?? DefaultOtlpExportProtocol; + set => this.protocol = value; + } /// /// Gets or sets the export processor type to be used with the OpenTelemetry Protocol Exporter. The default value is . /// /// Note: This only applies when exporting traces. - public ExportProcessorType ExportProcessorType { get; set; } = ExportProcessorType.Batch; + public ExportProcessorType ExportProcessorType + { + get => this.defaultProcessorOptions?.ExportProcessorType ?? ExportProcessorType.Batch; + set => (this.defaultProcessorOptions ??= new()).ExportProcessorType = value; + } /// /// Gets or sets the BatchExportProcessor options. Ignored unless ExportProcessorType is Batch. /// /// Note: This only applies when exporting traces. - public BatchExportProcessorOptions BatchExportProcessorOptions { get; set; } + public BatchExportProcessorOptions BatchExportProcessorOptions + { + get + { + if (this.batchExportProcessorOptions != null) + { + return this.batchExportProcessorOptions; + } + + return (this.defaultProcessorOptions ??= new()).BatchExportProcessorOptions; + } + + set => this.batchExportProcessorOptions = value; + } /// /// Gets or sets the factory function called to create the /// /// - public Func HttpClientFactory { get; set; } + public Func HttpClientFactory + { + get => this.httpClientFactory ??= this.DefaultHttpClientFactory; + set + { + this.httpClientFactory = value ?? NullHttpClientFactory; + + static HttpClient NullHttpClientFactory() + { + return null!; + } + } + } /// /// Gets a value indicating whether was modified via its setter. @@ -195,14 +239,57 @@ internal static OtlpExporterOptions CreateOtlpExporterOptions( string name) => new( configuration, - serviceProvider.GetRequiredService>().Get(name)); + serviceProvider.GetRequiredService>().Get(name)); + + internal static OtlpExporterOptions Merge(OtlpExporterOptions defaultInstance, OtlpExporterOptions signalInstance) + { + if (signalInstance.protocol == null) + { + signalInstance.protocol = defaultInstance.protocol; + } + + if (signalInstance.endpoint == null) + { + signalInstance.endpoint = defaultInstance.endpoint; + + // Note: We don't set ProgrammaticallyModifiedEndpoint because we + // want to append the signal to the default endpoint. + } + + if (signalInstance.Headers == null) + { + signalInstance.Headers = defaultInstance.Headers; + } + + if (!signalInstance.timeoutMilliseconds.HasValue) + { + signalInstance.timeoutMilliseconds = defaultInstance.timeoutMilliseconds; + } + + if (!signalInstance.exportProcessorType.HasValue) + { + signalInstance.exportProcessorType = defaultInstance.exportProcessorType; + } + + if (signalInstance.defaultProcessorOptions == null) + { + signalInstance.defaultProcessorOptions = defaultInstance.defaultProcessorOptions; + } + + if (signalInstance.httpClientFactory == null) + { + signalInstance.httpClientFactory = defaultInstance.httpClientFactory; + } + + return signalInstance; + } private static string GetUserAgentString() { try { var assemblyVersion = typeof(OtlpExporterOptions).Assembly.GetCustomAttribute(); - var informationalVersion = assemblyVersion.InformationalVersion; + var informationalVersion = assemblyVersion?.InformationalVersion; return string.IsNullOrEmpty(informationalVersion) ? UserAgentProduct : $"{UserAgentProduct}/{informationalVersion}"; } catch (Exception) diff --git a/src/OpenTelemetry/Internal/Builder/ProviderBuilderServiceCollectionExtensions.cs b/src/OpenTelemetry/Internal/Builder/ProviderBuilderServiceCollectionExtensions.cs index 1666291bb52..c0c3922b9f6 100644 --- a/src/OpenTelemetry/Internal/Builder/ProviderBuilderServiceCollectionExtensions.cs +++ b/src/OpenTelemetry/Internal/Builder/ProviderBuilderServiceCollectionExtensions.cs @@ -40,7 +40,10 @@ public static IServiceCollection AddOpenTelemetryMeterProviderBuilderServices(th Debug.Assert(services != null, "services was null"); services!.TryAddSingleton(); - services!.RegisterOptionsFactory(configuration => new MetricReaderOptions(configuration)); + services!.RegisterOptionsFactory(configuration => new PeriodicExportingMetricReaderOptions(configuration)); + services!.RegisterOptionsFactory( + (sp, configuration, name) => new MetricReaderOptions( + sp.GetRequiredService>().Get(name))); return services!; } @@ -51,6 +54,9 @@ public static IServiceCollection AddOpenTelemetryTracerProviderBuilderServices(t services!.TryAddSingleton(); services!.RegisterOptionsFactory(configuration => new BatchExportActivityProcessorOptions(configuration)); + services!.RegisterOptionsFactory( + (sp, configuration, name) => new ActivityExportProcessorOptions( + sp.GetRequiredService>().Get(name))); return services!; } diff --git a/src/OpenTelemetry/Logs/Builder/OpenTelemetryBuilderSdkLogsExtensions.cs b/src/OpenTelemetry/Logs/Builder/OpenTelemetryBuilderSdkLogsExtensions.cs new file mode 100644 index 00000000000..26dfb72e58b --- /dev/null +++ b/src/OpenTelemetry/Logs/Builder/OpenTelemetryBuilderSdkLogsExtensions.cs @@ -0,0 +1,28 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +using Microsoft.Extensions.DependencyInjection; +using OpenTelemetry.Internal; +using OpenTelemetry.Logs; + +namespace OpenTelemetry; + +public static class OpenTelemetryBuilderSdkLogsExtensions +{ + public static IOpenTelemetryBuilder ConfigureLoggingExportProcessorOptions( + this IOpenTelemetryBuilder builder, + Action configure) + => ConfigureLoggingExportProcessorOptions(builder, name: null, configure); + + public static IOpenTelemetryBuilder ConfigureLoggingExportProcessorOptions( + this IOpenTelemetryBuilder builder, + string? name, + Action configure) + { + Guard.ThrowIfNull(configure); + + builder.Services.Configure(name, configure); + + return builder; + } +} diff --git a/src/OpenTelemetry/Metrics/Builder/OpenTelemetryBuilderSdkMetricsExtensions.cs b/src/OpenTelemetry/Metrics/Builder/OpenTelemetryBuilderSdkMetricsExtensions.cs new file mode 100644 index 00000000000..40adcbb9bb9 --- /dev/null +++ b/src/OpenTelemetry/Metrics/Builder/OpenTelemetryBuilderSdkMetricsExtensions.cs @@ -0,0 +1,28 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +using Microsoft.Extensions.DependencyInjection; +using OpenTelemetry.Internal; +using OpenTelemetry.Metrics; + +namespace OpenTelemetry; + +public static class OpenTelemetryBuilderSdkMetricsExtensions +{ + public static IOpenTelemetryBuilder ConfigureMetricsReaderOptions( + this IOpenTelemetryBuilder builder, + Action configure) + => ConfigureMetricsReaderOptions(builder, name: null, configure); + + public static IOpenTelemetryBuilder ConfigureMetricsReaderOptions( + this IOpenTelemetryBuilder builder, + string? name, + Action configure) + { + Guard.ThrowIfNull(configure); + + builder.Services.Configure(name, configure); + + return builder; + } +} diff --git a/src/OpenTelemetry/Metrics/MetricReaderOptions.cs b/src/OpenTelemetry/Metrics/MetricReaderOptions.cs index 6123dc2c2cd..59b850e88aa 100644 --- a/src/OpenTelemetry/Metrics/MetricReaderOptions.cs +++ b/src/OpenTelemetry/Metrics/MetricReaderOptions.cs @@ -1,7 +1,7 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 -using Microsoft.Extensions.Configuration; +using System.Diagnostics; using OpenTelemetry.Internal; namespace OpenTelemetry.Metrics; @@ -17,13 +17,16 @@ public class MetricReaderOptions /// Initializes a new instance of the class. /// public MetricReaderOptions() - : this(new ConfigurationBuilder().AddEnvironmentVariables().Build()) + : this(new()) { } - internal MetricReaderOptions(IConfiguration configuration) + internal MetricReaderOptions( + PeriodicExportingMetricReaderOptions defaultPeriodicExportingMetricReaderOptions) { - this.periodicExportingMetricReaderOptions = new PeriodicExportingMetricReaderOptions(configuration); + Debug.Assert(defaultPeriodicExportingMetricReaderOptions != null, "defaultPeriodicExportingMetricReaderOptions was null"); + + this.periodicExportingMetricReaderOptions = defaultPeriodicExportingMetricReaderOptions ?? new(); } /// diff --git a/src/OpenTelemetry/Trace/ActivityExportProcessorOptions.cs b/src/OpenTelemetry/Trace/ActivityExportProcessorOptions.cs new file mode 100644 index 00000000000..86acb043015 --- /dev/null +++ b/src/OpenTelemetry/Trace/ActivityExportProcessorOptions.cs @@ -0,0 +1,49 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +using System.Diagnostics; +using OpenTelemetry.Internal; + +namespace OpenTelemetry.Trace; + +/// +/// Options for configuring either a or . +/// +public class ActivityExportProcessorOptions +{ + private BatchExportActivityProcessorOptions batchExportProcessorOptions; + + /// + /// Initializes a new instance of the class. + /// + public ActivityExportProcessorOptions() + : this(new()) + { + } + + internal ActivityExportProcessorOptions( + BatchExportActivityProcessorOptions defaultBatchExportActivityProcessorOptions) + { + Debug.Assert(defaultBatchExportActivityProcessorOptions != null, "defaultBatchExportActivityProcessorOptions was null"); + + this.batchExportProcessorOptions = defaultBatchExportActivityProcessorOptions ?? new(); + } + + /// + /// Gets or sets the export processor type to be used. The default value is . + /// + public ExportProcessorType ExportProcessorType { get; set; } = ExportProcessorType.Batch; + + /// + /// Gets or sets the batch export options. Ignored unless is . + /// + public BatchExportActivityProcessorOptions BatchExportProcessorOptions + { + get => this.batchExportProcessorOptions; + set + { + Guard.ThrowIfNull(value); + this.batchExportProcessorOptions = value; + } + } +} diff --git a/src/OpenTelemetry/Trace/Builder/OpenTelemetryBuilderSdkTraceExtensions.cs b/src/OpenTelemetry/Trace/Builder/OpenTelemetryBuilderSdkTraceExtensions.cs new file mode 100644 index 00000000000..2ce96c3fc35 --- /dev/null +++ b/src/OpenTelemetry/Trace/Builder/OpenTelemetryBuilderSdkTraceExtensions.cs @@ -0,0 +1,28 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +using Microsoft.Extensions.DependencyInjection; +using OpenTelemetry.Internal; +using OpenTelemetry.Trace; + +namespace OpenTelemetry; + +public static class OpenTelemetryBuilderSdkTraceExtensions +{ + public static IOpenTelemetryBuilder ConfigureTracingExportProcessorOptions( + this IOpenTelemetryBuilder builder, + Action configure) + => ConfigureTracingExportProcessorOptions(builder, name: null, configure); + + public static IOpenTelemetryBuilder ConfigureTracingExportProcessorOptions( + this IOpenTelemetryBuilder builder, + string? name, + Action configure) + { + Guard.ThrowIfNull(configure); + + builder.Services.Configure(name, configure); + + return builder; + } +} diff --git a/src/Shared/Options/ConfigurationExtensions.cs b/src/Shared/Options/ConfigurationExtensions.cs index 82e06158810..1d867e7167d 100644 --- a/src/Shared/Options/ConfigurationExtensions.cs +++ b/src/Shared/Options/ConfigurationExtensions.cs @@ -129,7 +129,7 @@ public static bool TryGetValue( public static IServiceCollection RegisterOptionsFactory( this IServiceCollection services, Func optionsFactoryFunc) - where T : class, new() + where T : class { Debug.Assert(services != null, "services was null"); Debug.Assert(optionsFactoryFunc != null, "optionsFactoryFunc was null"); @@ -150,7 +150,7 @@ public static IServiceCollection RegisterOptionsFactory( public static IServiceCollection RegisterOptionsFactory( this IServiceCollection services, Func optionsFactoryFunc) - where T : class, new() + where T : class { Debug.Assert(services != null, "services was null"); Debug.Assert(optionsFactoryFunc != null, "optionsFactoryFunc was null"); diff --git a/src/Shared/Options/DelegatingOptionsFactory.cs b/src/Shared/Options/DelegatingOptionsFactory.cs index 6d987c3dd4d..1abd015dcad 100644 --- a/src/Shared/Options/DelegatingOptionsFactory.cs +++ b/src/Shared/Options/DelegatingOptionsFactory.cs @@ -29,7 +29,7 @@ namespace Microsoft.Extensions.Options /// The type of options being requested. internal sealed class DelegatingOptionsFactory : IOptionsFactory - where TOptions : class, new() + where TOptions : class { private readonly Func optionsFactoryFunc; private readonly IConfiguration configuration; From c729fd7124de67cfda8f1d89920de34660fae867 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Mon, 26 Feb 2024 09:33:54 -0800 Subject: [PATCH 02/48] Tweaks and evolving design. --- ...nTelemetryBuilderOtlpExporterExtensions.cs | 164 ++++++++++++ .../Builder/OtlpExporterBuilder.cs | 154 ++++++++++++ .../Builder/OtlpExporterBuilderOptions.cs | 59 +++++ ...nTelemetryBuilderOtlpExporterExtensions.cs | 111 --------- .../OtlpExporterBuilderOptions.cs | 82 ------ .../OtlpExporterOptions.cs | 229 +---------------- .../OtlpExporterOptionsBase.cs | 234 ++++++++++++++++++ .../OtlpExporterOptionsExtensions.cs | 22 +- .../OtlpExporterSignals.cs | 16 ++ .../OtlpLogExporterHelperExtensions.cs | 2 +- .../OtlpMetricExporterExtensions.cs | 2 +- .../OtlpTraceExporterHelperExtensions.cs | 19 +- .../OpenTelemetryBuilderSdkLogsExtensions.cs | 28 --- ...penTelemetryBuilderSdkMetricsExtensions.cs | 28 --- .../OpenTelemetryBuilderSdkTraceExtensions.cs | 28 --- 15 files changed, 666 insertions(+), 512 deletions(-) create mode 100644 src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderOtlpExporterExtensions.cs create mode 100644 src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OtlpExporterBuilder.cs create mode 100644 src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OtlpExporterBuilderOptions.cs delete mode 100644 src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OpenTelemetryBuilderOtlpExporterExtensions.cs delete mode 100644 src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterBuilderOptions.cs create mode 100644 src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterOptionsBase.cs create mode 100644 src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterSignals.cs delete mode 100644 src/OpenTelemetry/Logs/Builder/OpenTelemetryBuilderSdkLogsExtensions.cs delete mode 100644 src/OpenTelemetry/Metrics/Builder/OpenTelemetryBuilderSdkMetricsExtensions.cs delete mode 100644 src/OpenTelemetry/Trace/Builder/OpenTelemetryBuilderSdkTraceExtensions.cs diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderOtlpExporterExtensions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderOtlpExporterExtensions.cs new file mode 100644 index 00000000000..f691162d808 --- /dev/null +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderOtlpExporterExtensions.cs @@ -0,0 +1,164 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#nullable enable + +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation; +using OpenTelemetry.Internal; +using OpenTelemetry.Logs; +using OpenTelemetry.Metrics; +using OpenTelemetry.Trace; + +namespace OpenTelemetry.Exporter; + +public static class OpenTelemetryBuilderOtlpExporterExtensions +{ + public static IOpenTelemetryBuilder AddOtlpExporter( + this IOpenTelemetryBuilder builder) + => AddOtlpExporter(builder, name: null); + + public static IOpenTelemetryBuilder AddOtlpExporter( + this IOpenTelemetryBuilder builder, + Uri endpoint) + => AddOtlpExporter(builder, OtlpExportProtocol.Grpc, endpoint); + + public static IOpenTelemetryBuilder AddOtlpExporter( + this IOpenTelemetryBuilder builder, + OtlpExportProtocol protocol, + Uri endpoint) + { + Guard.ThrowIfNull(endpoint); + + return AddOtlpExporter(builder, name: null, configure: otlpBuilder => + { + otlpBuilder.ConfigureDefaultOtlpExporterOptions(o => + { + o.Protocol = protocol; + if (endpoint != null) + { + o.Endpoint = endpoint; + } + }); + }); + } + + public static IOpenTelemetryBuilder AddOtlpExporter( + this IOpenTelemetryBuilder builder, + Action configure) + { + Guard.ThrowIfNull(configure); + + return AddOtlpExporter(builder, name: null, configure: configure); + } + + public static IOpenTelemetryBuilder AddOtlpExporter( + this IOpenTelemetryBuilder builder, + IConfiguration configuration) + { + Guard.ThrowIfNull(configuration); + + return AddOtlpExporter(builder, name: null, configuration: configuration); + } + + public static IOpenTelemetryBuilder AddOtlpExporter( + this IOpenTelemetryBuilder builder, + string? name = null, + IConfiguration? configuration = null, + Action? configure = null) + { + Guard.ThrowIfNull(builder); + + if (configuration == null && string.IsNullOrEmpty(name)) + { + name = "otlp"; + } + + var otlpBuilder = new OtlpExporterBuilder(builder.Services, name, configuration); + + configure?.Invoke(otlpBuilder); + + AddOtlpExporterInternal(builder, name); + + return builder; + } + + private static void AddOtlpExporterInternal(IOpenTelemetryBuilder builder, string? name) + { + builder.Services.RegisterOptionsFactory(configuration => new SdkLimitOptions(configuration)); + builder.Services.RegisterOptionsFactory(configuration => new ExperimentalOptions(configuration)); + builder.Services.RegisterOptionsFactory((sp, configuration, name) => new OtlpExporterBuilderOptions( + configuration, + sp.GetRequiredService>().CurrentValue, + sp.GetRequiredService>().CurrentValue, + sp.GetService>()?.Get(name), + sp.GetService>()?.Get(name), + sp.GetService>()?.Get(name))); + + name ??= Options.DefaultName; + + builder.Services.ConfigureOpenTelemetryLoggerProvider( + (sp, logging) => + { + var builderOptions = GetBuilderOptions(sp, name); + + if (!builderOptions.Signals.HasFlag(OtlpExporterSignals.Logs)) + { + return; + } + + logging.AddProcessor( + OtlpLogExporterHelperExtensions.BuildOtlpLogExporter( + sp, + builderOptions.LoggingOptions.ApplyDefaults(builderOptions.DefaultOptions), + builderOptions.LogRecordExportProcessorOptions ?? throw new NotSupportedException(), + builderOptions.SdkLimitOptions, + builderOptions.ExperimentalOptions)); + }); + + builder.Services.ConfigureOpenTelemetryMeterProvider( + (sp, metrics) => + { + var builderOptions = GetBuilderOptions(sp, name); + + if (!builderOptions.Signals.HasFlag(OtlpExporterSignals.Metrics)) + { + return; + } + + metrics.AddReader( + OtlpMetricExporterExtensions.BuildOtlpExporterMetricReader( + builderOptions.MetricsOptions.ApplyDefaults(builderOptions.DefaultOptions), + builderOptions.MetricReaderOptions ?? throw new NotSupportedException(), + sp)); + }); + + builder.Services.ConfigureOpenTelemetryTracerProvider( + (sp, tracing) => + { + var builderOptions = GetBuilderOptions(sp, name); + + if (!builderOptions.Signals.HasFlag(OtlpExporterSignals.Traces)) + { + return; + } + + var processorOptions = builderOptions.ActivityExportProcessorOptions ?? throw new NotSupportedException(); + + tracing.AddProcessor( + OtlpTraceExporterHelperExtensions.BuildOtlpExporterProcessor( + builderOptions.TracingOptions.ApplyDefaults(builderOptions.DefaultOptions), + builderOptions.SdkLimitOptions, + processorOptions.ExportProcessorType, + processorOptions.BatchExportProcessorOptions, + sp)); + }); + + static OtlpExporterBuilderOptions GetBuilderOptions(IServiceProvider sp, string name) + { + return sp.GetRequiredService>().Get(name); + } + } +} diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OtlpExporterBuilder.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OtlpExporterBuilder.cs new file mode 100644 index 00000000000..b63027cab0c --- /dev/null +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OtlpExporterBuilder.cs @@ -0,0 +1,154 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#nullable enable + +using System.Diagnostics; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using OpenTelemetry.Internal; +using OpenTelemetry.Logs; +using OpenTelemetry.Metrics; +using OpenTelemetry.Trace; + +namespace OpenTelemetry.Exporter; + +public sealed class OtlpExporterBuilder +{ + private readonly string? name; + + internal OtlpExporterBuilder( + IServiceCollection services, + string? name, + IConfiguration? configuration) + { + Debug.Assert(services != null, "services was null"); + + this.name = name; + this.Services = services; + + if (configuration != null) + { + Debug.Assert(!string.IsNullOrEmpty(name), "name was null or empty"); + + BindConfigurationToOptions(services, name!, configuration); + } + } + + public IServiceCollection Services { get; } + + public OtlpExporterBuilder SetSignalsToConfigure(OtlpExporterSignals signalsToConfigure) + { + this.Services.Configure( + this.name, + o => o.Signals = signalsToConfigure); + return this; + } + + public OtlpExporterBuilder ConfigureDefaultOtlpExporterOptions( + Action configure) + { + Guard.ThrowIfNull(configure); + + this.Services.Configure( + this.name, + o => configure(o.DefaultOptions)); + return this; + } + + public OtlpExporterBuilder ConfigureLoggingOtlpExporterOptions( + Action configure) + { + Guard.ThrowIfNull(configure); + + this.Services.Configure( + this.name, + o => configure(o.LoggingOptions)); + return this; + } + + public OtlpExporterBuilder ConfigureLoggingProcessorOptions( + Action configure) + { + Guard.ThrowIfNull(configure); + + this.Services.Configure(this.name, configure); + return this; + } + + public OtlpExporterBuilder ConfigureMetricsOtlpExporterOptions( + Action configure) + { + Guard.ThrowIfNull(configure); + + this.Services.Configure( + this.name, + o => configure(o.MetricsOptions)); + return this; + } + + public OtlpExporterBuilder ConfigureMetricsReaderOptions( + Action configure) + { + Guard.ThrowIfNull(configure); + + this.Services.Configure(this.name, configure); + return this; + } + + public OtlpExporterBuilder ConfigureTracingOtlpExporterOptions( + Action configure) + { + Guard.ThrowIfNull(configure); + + this.Services.Configure( + this.name, + o => configure(o.TracingOptions)); + return this; + } + + public OtlpExporterBuilder ConfigureTracingProcessorOptions( + Action configure) + { + Guard.ThrowIfNull(configure); + + this.Services.Configure(this.name, configure); + return this; + } + + private static void BindConfigurationToOptions(IServiceCollection services, string name, IConfiguration configuration) + { + Debug.Assert(services != null, "services was null"); + Debug.Assert(!string.IsNullOrEmpty(name), "name was null or empty"); + Debug.Assert(configuration != null, "configuration was null"); + +var json = """ +{ + "Signals": "Logs, Metrics", + "DefaultOptions": { + }, + "LoggingOptions": { + "ExportProcessorType": Batch, + "BatchExportProcessorOptions": { + "ScheduledDelayMilliseconds": 1000 + } + }, + "MetricsOptions": { + }, + "TracingOptions": { + } +} +"""; + + services.Configure(name, configuration); + + services.Configure( + name, configuration.GetSection(nameof(OtlpExporterBuilderOptions.LoggingOptions))); + + services.Configure( + name, configuration.GetSection(nameof(OtlpExporterBuilderOptions.MetricsOptions))); + + services.Configure( + name, configuration.GetSection(nameof(OtlpExporterBuilderOptions.TracingOptions))); + } +} diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OtlpExporterBuilderOptions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OtlpExporterBuilderOptions.cs new file mode 100644 index 00000000000..778505ed2f8 --- /dev/null +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OtlpExporterBuilderOptions.cs @@ -0,0 +1,59 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#nullable enable + +using System.Diagnostics; +using Microsoft.Extensions.Configuration; +using OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation; +using OpenTelemetry.Logs; +using OpenTelemetry.Metrics; +using OpenTelemetry.Trace; + +namespace OpenTelemetry.Exporter; + +internal sealed class OtlpExporterBuilderOptions +{ + internal readonly SdkLimitOptions SdkLimitOptions; + internal readonly ExperimentalOptions ExperimentalOptions; + internal readonly LogRecordExportProcessorOptions? LogRecordExportProcessorOptions; + internal readonly MetricReaderOptions? MetricReaderOptions; + internal readonly ActivityExportProcessorOptions? ActivityExportProcessorOptions; + + public OtlpExporterBuilderOptions( + IConfiguration configuration, + SdkLimitOptions sdkLimitOptions, + ExperimentalOptions experimentalOptions, + LogRecordExportProcessorOptions? logRecordExportProcessorOptions, + MetricReaderOptions? metricReaderOptions, + ActivityExportProcessorOptions? activityExportProcessorOptions) + { + Debug.Assert(configuration != null, "configuration was null"); + Debug.Assert(sdkLimitOptions != null, "sdkLimitOptions was null"); + Debug.Assert(experimentalOptions != null, "experimentalOptions was null"); + + this.SdkLimitOptions = sdkLimitOptions; + this.ExperimentalOptions = experimentalOptions; + this.LogRecordExportProcessorOptions = logRecordExportProcessorOptions; + this.MetricReaderOptions = metricReaderOptions; + this.ActivityExportProcessorOptions = activityExportProcessorOptions; + + this.DefaultOptions = new OtlpExporterOptionsBase(configuration, OtlpExporterSignals.None); + + this.LoggingOptions = new OtlpExporterOptionsBase(configuration, OtlpExporterSignals.Logs); + + this.MetricsOptions = new OtlpExporterOptionsBase(configuration, OtlpExporterSignals.Metrics); + + this.TracingOptions = new OtlpExporterOptionsBase(configuration, OtlpExporterSignals.Traces); + } + + public OtlpExporterSignals Signals { get; set; } = OtlpExporterSignals.All; + + public OtlpExporterOptionsBase DefaultOptions { get; } + + public OtlpExporterOptionsBase LoggingOptions { get; } + + public OtlpExporterOptionsBase MetricsOptions { get; } + + public OtlpExporterOptionsBase TracingOptions { get; } +} diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OpenTelemetryBuilderOtlpExporterExtensions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OpenTelemetryBuilderOtlpExporterExtensions.cs deleted file mode 100644 index 35495522a61..00000000000 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OpenTelemetryBuilderOtlpExporterExtensions.cs +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -#nullable enable - -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; -using OpenTelemetry.Exporter; -using OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation; -using OpenTelemetry.Internal; -using OpenTelemetry.Logs; -using OpenTelemetry.Metrics; -using OpenTelemetry.Trace; - -namespace OpenTelemetry; - -public static class OpenTelemetryBuilderOtlpExporterExtensions -{ - public static IOpenTelemetryBuilder AddOtlpExporter( - this IOpenTelemetryBuilder builder) - => AddOtlpExporter(builder, name: null, configure: null); - - public static IOpenTelemetryBuilder AddOtlpExporter( - this IOpenTelemetryBuilder builder, - Action? configure) - => AddOtlpExporter(builder, name: null, configure); - - public static IOpenTelemetryBuilder AddOtlpExporter( - this IOpenTelemetryBuilder builder, - string? name, - Action? configure) - { - Guard.ThrowIfNull(builder); - - builder.Services.RegisterOptionsFactory(configuration => new SdkLimitOptions(configuration)); - builder.Services.RegisterOptionsFactory(configuration => new ExperimentalOptions(configuration)); - - builder.Services.RegisterOptionsFactory( - (sp, config, name) => - new OtlpExporterBuilderOptions( - config, - sp.GetRequiredService>().Get(name))); - - name ??= Options.DefaultName; - - if (configure != null) - { - builder.Services.Configure(name, configure); - } - - builder.Services.ConfigureOpenTelemetryLoggerProvider( - (sp, logging) => - { - var builderOptions = GetOptions(sp, name); - - if (!builderOptions.AddToLoggerProvider) - { - return; - } - - logging.AddProcessor( - OtlpLogExporterHelperExtensions.BuildOtlpLogExporter( - sp, - OtlpExporterOptions.Merge(builderOptions.DefaultOptions, builderOptions.LoggingOptions), - sp.GetRequiredService>().Get(name), - sp.GetRequiredService>().CurrentValue, - sp.GetRequiredService>().Get(name))); - }); - - builder.Services.ConfigureOpenTelemetryMeterProvider( - (sp, metrics) => - { - var builderOptions = GetOptions(sp, name); - - if (!builderOptions.AddToMeterProvider) - { - return; - } - - metrics.AddReader( - OtlpMetricExporterExtensions.BuildOtlpExporterMetricReader( - OtlpExporterOptions.Merge(builderOptions.DefaultOptions, builderOptions.MetricsOptions), - sp.GetRequiredService>().Get(name), - sp)); - }); - - builder.Services.ConfigureOpenTelemetryTracerProvider( - (sp, tracing) => - { - var builderOptions = GetOptions(sp, name); - - if (!builderOptions.AddToTracerProvider) - { - return; - } - - tracing.AddProcessor( - OtlpTraceExporterHelperExtensions.BuildOtlpExporterProcessor( - OtlpExporterOptions.Merge(builderOptions.DefaultOptions, builderOptions.TracingOptions), - sp.GetRequiredService>().CurrentValue, - sp)); - }); - - return builder; - - static OtlpExporterBuilderOptions GetOptions(IServiceProvider sp, string name) - { - return sp.GetRequiredService>().Get(name); - } - } -} diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterBuilderOptions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterBuilderOptions.cs deleted file mode 100644 index e29c46a5531..00000000000 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterBuilderOptions.cs +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -#nullable enable - -using Microsoft.Extensions.Configuration; -using OpenTelemetry.Exporter; -using OpenTelemetry.Internal; -using OpenTelemetry.Trace; - -namespace OpenTelemetry; - -public sealed class OtlpExporterBuilderOptions -{ - internal OtlpExporterBuilderOptions( - IConfiguration configuration, - ActivityExportProcessorOptions defaultExportProcessorOptions) - { - this.DefaultOptions = new OtlpExporterOptions(configuration, defaultExportProcessorOptions); - - var emptyConfiguration = new ConfigurationBuilder().Build(); - - this.LoggingOptions = new OtlpExporterOptions(emptyConfiguration, defaultExportProcessorOptions: null); - - this.MetricsOptions = new OtlpExporterOptions(emptyConfiguration, defaultExportProcessorOptions: null); - - this.TracingOptions = new OtlpExporterOptions(emptyConfiguration, defaultExportProcessorOptions); - - if (configuration.TryGetUriValue("OTEL_EXPORTER_OTLP_LOGS_ENDPOINT", out var endpoint)) - { - this.LoggingOptions.Endpoint = endpoint; - } - - if (configuration.TryGetValue( - "OTEL_EXPORTER_OTLP_LOGS_PROTOCOL", - OtlpExportProtocolParser.TryParse, - out var protocol)) - { - this.LoggingOptions.Protocol = protocol; - } - - if (configuration.TryGetUriValue("OTEL_EXPORTER_OTLP_METRICS_ENDPOINT", out endpoint)) - { - this.MetricsOptions.Endpoint = endpoint; - } - - if (configuration.TryGetValue( - "OTEL_EXPORTER_OTLP_METRICS_PROTOCOL", - OtlpExportProtocolParser.TryParse, - out protocol)) - { - this.MetricsOptions.Protocol = protocol; - } - - if (configuration.TryGetUriValue("OTEL_EXPORTER_OTLP_TRACES_ENDPOINT", out endpoint)) - { - this.TracingOptions.Endpoint = endpoint; - } - - if (configuration.TryGetValue( - "OTEL_EXPORTER_OTLP_TRACES_PROTOCOL", - OtlpExportProtocolParser.TryParse, - out protocol)) - { - this.TracingOptions.Protocol = protocol; - } - } - - public bool AddToLoggerProvider { get; set; } = true; - - public bool AddToMeterProvider { get; set; } = true; - - public bool AddToTracerProvider { get; set; } = true; - - public OtlpExporterOptions DefaultOptions { get; } - - public OtlpExporterOptions LoggingOptions { get; } - - public OtlpExporterOptions MetricsOptions { get; } - - public OtlpExporterOptions TracingOptions { get; } -} diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterOptions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterOptions.cs index 373a0415b55..68c8c2cc5d6 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterOptions.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterOptions.cs @@ -12,7 +12,6 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using OpenTelemetry.Internal; -using OpenTelemetry.Metrics; using OpenTelemetry.Trace; namespace OpenTelemetry.Exporter; @@ -22,211 +21,44 @@ namespace OpenTelemetry.Exporter; /// OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_EXPORTER_OTLP_HEADERS, OTEL_EXPORTER_OTLP_TIMEOUT, OTEL_EXPORTER_OTLP_PROTOCOL /// environment variables are parsed during object construction. /// -public class OtlpExporterOptions +public class OtlpExporterOptions : OtlpExporterOptionsBase { - internal const string EndpointEnvVarName = "OTEL_EXPORTER_OTLP_ENDPOINT"; - internal const string HeadersEnvVarName = "OTEL_EXPORTER_OTLP_HEADERS"; - internal const string TimeoutEnvVarName = "OTEL_EXPORTER_OTLP_TIMEOUT"; - internal const string ProtocolEnvVarName = "OTEL_EXPORTER_OTLP_PROTOCOL"; - internal static readonly KeyValuePair[] StandardHeaders = new KeyValuePair[] { new KeyValuePair("User-Agent", GetUserAgentString()), }; - internal readonly Func DefaultHttpClientFactory; - - private const string DefaultGrpcEndpoint = "http://localhost:4317"; - private const string DefaultHttpEndpoint = "http://localhost:4318"; - private const OtlpExportProtocol DefaultOtlpExportProtocol = OtlpExportProtocol.Grpc; private const string UserAgentProduct = "OTel-OTLP-Exporter-Dotnet"; - private Uri? endpoint; - private OtlpExportProtocol? protocol; - private int? timeoutMilliseconds; - private ExportProcessorType? exportProcessorType; - private ActivityExportProcessorOptions? defaultProcessorOptions; - private BatchExportProcessorOptions? batchExportProcessorOptions; - private Func? httpClientFactory; - /// /// Initializes a new instance of the class. /// public OtlpExporterOptions() - : this(new ConfigurationBuilder().AddEnvironmentVariables().Build(), defaultExportProcessorOptions: null) + : this(new ConfigurationBuilder().AddEnvironmentVariables().Build(), new()) { } internal OtlpExporterOptions( IConfiguration configuration, - ActivityExportProcessorOptions? defaultExportProcessorOptions) + BatchExportActivityProcessorOptions defaultBatchOptions) + : base(configuration, OtlpExporterSignals.None) { - Debug.Assert(configuration != null, "configuration was null"); - - if (configuration.TryGetUriValue(EndpointEnvVarName, out var endpoint)) - { - this.endpoint = endpoint; - } - - if (configuration.TryGetStringValue(HeadersEnvVarName, out var headers)) - { - this.Headers = headers; - } - - if (configuration.TryGetIntValue(TimeoutEnvVarName, out var timeout)) - { - this.TimeoutMilliseconds = timeout; - } - - if (configuration.TryGetValue( - ProtocolEnvVarName, - OtlpExportProtocolParser.TryParse, - out var protocol)) - { - this.Protocol = protocol; - } - - this.DefaultHttpClientFactory = () => - { - return new HttpClient - { - Timeout = TimeSpan.FromMilliseconds(this.TimeoutMilliseconds), - }; - }; + Debug.Assert(defaultBatchOptions != null, "defaultBatchOptions was null"); - this.defaultProcessorOptions = defaultExportProcessorOptions; - } - - /// - /// Gets or sets the target to which the exporter is going to send telemetry. - /// Must be a valid Uri with scheme (http or https) and host, and - /// may contain a port and path. The default value is - /// * http://localhost:4317 for - /// * http://localhost:4318 for . - /// - public Uri Endpoint - { - get - { - if (this.endpoint == null) - { - this.endpoint = this.Protocol == OtlpExportProtocol.Grpc - ? new Uri(DefaultGrpcEndpoint) - : new Uri(DefaultHttpEndpoint); - } - - return this.endpoint; - } - - set - { - this.endpoint = value; - this.ProgrammaticallyModifiedEndpoint = true; - } - } - - /// - /// Gets or sets optional headers for the connection. Refer to the - /// specification for information on the expected format for Headers. - /// - public string? Headers { get; set; } - - /// - /// Gets or sets the max waiting time (in milliseconds) for the backend to process each batch. The default value is 10000. - /// - public int TimeoutMilliseconds - { - get => this.timeoutMilliseconds ?? 10000; - set => this.timeoutMilliseconds = value; - } - - /// - /// Gets or sets the the OTLP transport protocol. Supported values: Grpc and HttpProtobuf. - /// - public OtlpExportProtocol Protocol - { - get => this.protocol ?? DefaultOtlpExportProtocol; - set => this.protocol = value; + this.BatchExportProcessorOptions = defaultBatchOptions; } /// /// Gets or sets the export processor type to be used with the OpenTelemetry Protocol Exporter. The default value is . /// /// Note: This only applies when exporting traces. - public ExportProcessorType ExportProcessorType - { - get => this.defaultProcessorOptions?.ExportProcessorType ?? ExportProcessorType.Batch; - set => (this.defaultProcessorOptions ??= new()).ExportProcessorType = value; - } + public ExportProcessorType ExportProcessorType { get; set; } = ExportProcessorType.Batch; /// /// Gets or sets the BatchExportProcessor options. Ignored unless ExportProcessorType is Batch. /// /// Note: This only applies when exporting traces. - public BatchExportProcessorOptions BatchExportProcessorOptions - { - get - { - if (this.batchExportProcessorOptions != null) - { - return this.batchExportProcessorOptions; - } - - return (this.defaultProcessorOptions ??= new()).BatchExportProcessorOptions; - } - - set => this.batchExportProcessorOptions = value; - } - - /// - /// Gets or sets the factory function called to create the instance that will be used at runtime to - /// transmit telemetry over HTTP. The returned instance will be reused - /// for all export invocations. - /// - /// - /// Notes: - /// - /// This is only invoked for the protocol. - /// The default behavior when using the extension is if an IHttpClientFactory - /// instance can be resolved through the application then an will be - /// created through the factory with the name "OtlpTraceExporter" - /// otherwise an will be instantiated - /// directly. - /// The default behavior when using the extension is if an IHttpClientFactory - /// instance can be resolved through the application then an will be - /// created through the factory with the name "OtlpMetricExporter" - /// otherwise an will be instantiated - /// directly. - /// - /// - public Func HttpClientFactory - { - get => this.httpClientFactory ??= this.DefaultHttpClientFactory; - set - { - this.httpClientFactory = value ?? NullHttpClientFactory; - - static HttpClient NullHttpClientFactory() - { - return null!; - } - } - } - - /// - /// Gets a value indicating whether was modified via its setter. - /// - internal bool ProgrammaticallyModifiedEndpoint { get; private set; } + public BatchExportProcessorOptions BatchExportProcessorOptions { get; set; } internal static void RegisterOtlpExporterOptionsFactory(IServiceCollection services) { @@ -239,50 +71,7 @@ internal static OtlpExporterOptions CreateOtlpExporterOptions( string name) => new( configuration, - serviceProvider.GetRequiredService>().Get(name)); - - internal static OtlpExporterOptions Merge(OtlpExporterOptions defaultInstance, OtlpExporterOptions signalInstance) - { - if (signalInstance.protocol == null) - { - signalInstance.protocol = defaultInstance.protocol; - } - - if (signalInstance.endpoint == null) - { - signalInstance.endpoint = defaultInstance.endpoint; - - // Note: We don't set ProgrammaticallyModifiedEndpoint because we - // want to append the signal to the default endpoint. - } - - if (signalInstance.Headers == null) - { - signalInstance.Headers = defaultInstance.Headers; - } - - if (!signalInstance.timeoutMilliseconds.HasValue) - { - signalInstance.timeoutMilliseconds = defaultInstance.timeoutMilliseconds; - } - - if (!signalInstance.exportProcessorType.HasValue) - { - signalInstance.exportProcessorType = defaultInstance.exportProcessorType; - } - - if (signalInstance.defaultProcessorOptions == null) - { - signalInstance.defaultProcessorOptions = defaultInstance.defaultProcessorOptions; - } - - if (signalInstance.httpClientFactory == null) - { - signalInstance.httpClientFactory = defaultInstance.httpClientFactory; - } - - return signalInstance; - } + serviceProvider.GetRequiredService>().Get(name)); private static string GetUserAgentString() { diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterOptionsBase.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterOptionsBase.cs new file mode 100644 index 00000000000..074e02bbb4f --- /dev/null +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterOptionsBase.cs @@ -0,0 +1,234 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#nullable enable + +using System.Diagnostics; +using Microsoft.Extensions.Configuration; +using OpenTelemetry.Internal; +using OpenTelemetry.Metrics; +using OpenTelemetry.Trace; + +namespace OpenTelemetry.Exporter; + +public class OtlpExporterOptionsBase +{ + internal const string EndpointEnvVarName = "OTEL_EXPORTER_OTLP_ENDPOINT"; + internal const string ProtocolEnvVarName = "OTEL_EXPORTER_OTLP_PROTOCOL"; + internal const string HeadersEnvVarName = "OTEL_EXPORTER_OTLP_HEADERS"; + internal const string TimeoutEnvVarName = "OTEL_EXPORTER_OTLP_TIMEOUT"; + + internal readonly Func DefaultHttpClientFactory; + internal bool ProgrammaticallyModifiedEndpoint; + + private const string DefaultGrpcEndpoint = "http://localhost:4317"; + private const string DefaultHttpEndpoint = "http://localhost:4318"; + private const OtlpExportProtocol DefaultOtlpExportProtocol = OtlpExportProtocol.Grpc; + + private OtlpExportProtocol? protocol; + private Uri? endpoint; + private int? timeoutMilliseconds; + private Func? httpClientFactory; + + internal OtlpExporterOptionsBase( + IConfiguration configuration, + OtlpExporterSignals signal) + { + Debug.Assert(configuration != null, "configuration was null"); + + if (signal == OtlpExporterSignals.None) + { + this.ApplyConfigurationUsingSpecificationEnvVars( + configuration, + EndpointEnvVarName, + ProtocolEnvVarName, + HeadersEnvVarName, + TimeoutEnvVarName); + } + + if (signal.HasFlag(OtlpExporterSignals.Logs)) + { + this.ApplyConfigurationUsingSpecificationEnvVars( + configuration, + "OTEL_EXPORTER_OTLP_LOGS_ENDPOINT", + "OTEL_EXPORTER_OTLP_LOGS_PROTOCOL", + "OTEL_EXPORTER_OTLP_LOGS_HEADERS", + "OTEL_EXPORTER_OTLP_LOGS_TIMEOUT"); + } + + if (signal.HasFlag(OtlpExporterSignals.Metrics)) + { + this.ApplyConfigurationUsingSpecificationEnvVars( + configuration, + "OTEL_EXPORTER_OTLP_METRICS_ENDPOINT", + "OTEL_EXPORTER_OTLP_METRICS_PROTOCOL", + "OTEL_EXPORTER_OTLP_METRICS_HEADERS", + "OTEL_EXPORTER_OTLP_METRICS_TIMEOUT"); + } + + if (signal.HasFlag(OtlpExporterSignals.Traces)) + { + this.ApplyConfigurationUsingSpecificationEnvVars( + configuration, + "OTEL_EXPORTER_OTLP_TRACES_ENDPOINT", + "OTEL_EXPORTER_OTLP_TRACES_PROTOCOL", + "OTEL_EXPORTER_OTLP_TRACES_HEADERS", + "OTEL_EXPORTER_OTLP_TRACES_TIMEOUT"); + } + + this.DefaultHttpClientFactory = () => + { + return new HttpClient + { + Timeout = TimeSpan.FromMilliseconds(this.TimeoutMilliseconds), + }; + }; + } + + /// + /// Gets or sets the the OTLP transport protocol. Supported values: Grpc and HttpProtobuf. + /// + public OtlpExportProtocol Protocol + { + get => this.protocol ?? DefaultOtlpExportProtocol; + set => this.protocol = value; + } + + /// + /// Gets or sets the target to which the exporter is going to send telemetry. + /// Must be a valid Uri with scheme (http or https) and host, and + /// may contain a port and path. The default value is + /// * http://localhost:4317 for + /// * http://localhost:4318 for . + /// + public Uri Endpoint + { + get + { + if (this.endpoint == null) + { + this.endpoint = this.Protocol == OtlpExportProtocol.Grpc + ? new Uri(DefaultGrpcEndpoint) + : new Uri(DefaultHttpEndpoint); + } + + return this.endpoint; + } + + set + { + this.endpoint = value; + this.ProgrammaticallyModifiedEndpoint = true; + } + } + + /// + /// Gets or sets optional headers for the connection. Refer to the + /// specification for information on the expected format for Headers. + /// + public string? Headers { get; set; } + + /// + /// Gets or sets the max waiting time (in milliseconds) for the backend to process each batch. The default value is 10000. + /// + public int TimeoutMilliseconds + { + get => this.timeoutMilliseconds ?? 10000; + set => this.timeoutMilliseconds = value; + } + + /// + /// Gets or sets the factory function called to create the instance that will be used at runtime to + /// transmit telemetry over HTTP. The returned instance will be reused + /// for all export invocations. + /// + /// + /// Notes: + /// + /// This is only invoked for the protocol. + /// The default behavior when using the extension is if an IHttpClientFactory + /// instance can be resolved through the application then an will be + /// created through the factory with the name "OtlpTraceExporter" + /// otherwise an will be instantiated + /// directly. + /// The default behavior when using the extension is if an IHttpClientFactory + /// instance can be resolved through the application then an will be + /// created through the factory with the name "OtlpMetricExporter" + /// otherwise an will be instantiated + /// directly. + /// + /// + public Func HttpClientFactory + { + get => this.httpClientFactory ??= this.DefaultHttpClientFactory; + set + { + this.httpClientFactory = value ?? NullHttpClientFactory; + + static HttpClient NullHttpClientFactory() + { + return null!; + } + } + } + + internal OtlpExporterOptionsBase ApplyDefaults(OtlpExporterOptionsBase defaultInstance) + { + this.protocol ??= defaultInstance.protocol; + + this.endpoint ??= defaultInstance.endpoint; + + // Note: We don't set ProgrammaticallyModifiedEndpoint because we + // want to append the signal if the endpoint came from the default + // endpoint. + + this.Headers ??= defaultInstance.Headers; + + this.timeoutMilliseconds ??= defaultInstance.timeoutMilliseconds; + + this.httpClientFactory ??= defaultInstance.httpClientFactory; + + return this; + } + + private void ApplyConfigurationUsingSpecificationEnvVars( + IConfiguration configuration, + string endpointEnvVarKey, + string protocolEnvVarKey, + string headersEnvVarKey, + string timeoutEnvVarKey) + { + if (configuration.TryGetUriValue(endpointEnvVarKey, out var endpoint)) + { + this.endpoint = endpoint; + } + + if (configuration.TryGetValue( + protocolEnvVarKey, + OtlpExportProtocolParser.TryParse, + out var protocol)) + { + this.Protocol = protocol; + } + + if (configuration.TryGetStringValue(headersEnvVarKey, out var headers)) + { + this.Headers = headers; + } + + if (configuration.TryGetIntValue(timeoutEnvVarKey, out var timeout)) + { + this.TimeoutMilliseconds = timeout; + } + } +} diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterOptionsExtensions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterOptionsExtensions.cs index 44133af1f84..ba691f678c2 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterOptionsExtensions.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterOptionsExtensions.cs @@ -20,9 +20,9 @@ namespace OpenTelemetry.Exporter; internal static class OtlpExporterOptionsExtensions { #if NETSTANDARD2_1 || NET6_0_OR_GREATER - public static GrpcChannel CreateChannel(this OtlpExporterOptions options) + public static GrpcChannel CreateChannel(this OtlpExporterOptionsBase options) #else - public static Channel CreateChannel(this OtlpExporterOptions options) + public static Channel CreateChannel(this OtlpExporterOptionsBase options) #endif { if (options.Endpoint.Scheme != Uri.UriSchemeHttp && options.Endpoint.Scheme != Uri.UriSchemeHttps) @@ -47,12 +47,12 @@ public static Channel CreateChannel(this OtlpExporterOptions options) #endif } - public static Metadata GetMetadataFromHeaders(this OtlpExporterOptions options) + public static Metadata GetMetadataFromHeaders(this OtlpExporterOptionsBase options) { return options.GetHeaders((m, k, v) => m.Add(k, v)); } - public static THeaders GetHeaders(this OtlpExporterOptions options, Action addHeader) + public static THeaders GetHeaders(this OtlpExporterOptionsBase options, Action addHeader) where THeaders : new() { var optionHeaders = options.Headers; @@ -88,16 +88,16 @@ public static THeaders GetHeaders(this OtlpExporterOptions options, Ac return headers; } - public static OtlpExporterTransmissionHandler GetTraceExportTransmissionHandler(this OtlpExporterOptions options) + public static OtlpExporterTransmissionHandler GetTraceExportTransmissionHandler(this OtlpExporterOptionsBase options) => new(GetTraceExportClient(options)); - public static OtlpExporterTransmissionHandler GetMetricsExportTransmissionHandler(this OtlpExporterOptions options) + public static OtlpExporterTransmissionHandler GetMetricsExportTransmissionHandler(this OtlpExporterOptionsBase options) => new(GetMetricsExportClient(options)); - public static OtlpExporterTransmissionHandler GetLogsExportTransmissionHandler(this OtlpExporterOptions options) + public static OtlpExporterTransmissionHandler GetLogsExportTransmissionHandler(this OtlpExporterOptionsBase options) => new(GetLogExportClient(options)); - public static IExportClient GetTraceExportClient(this OtlpExporterOptions options) => + public static IExportClient GetTraceExportClient(this OtlpExporterOptionsBase options) => options.Protocol switch { OtlpExportProtocol.Grpc => new OtlpGrpcTraceExportClient(options), @@ -107,7 +107,7 @@ public static THeaders GetHeaders(this OtlpExporterOptions options, Ac _ => throw new NotSupportedException($"Protocol {options.Protocol} is not supported."), }; - public static IExportClient GetMetricsExportClient(this OtlpExporterOptions options) => + public static IExportClient GetMetricsExportClient(this OtlpExporterOptionsBase options) => options.Protocol switch { OtlpExportProtocol.Grpc => new OtlpGrpcMetricsExportClient(options), @@ -117,7 +117,7 @@ public static THeaders GetHeaders(this OtlpExporterOptions options, Ac _ => throw new NotSupportedException($"Protocol {options.Protocol} is not supported."), }; - public static IExportClient GetLogExportClient(this OtlpExporterOptions options) => + public static IExportClient GetLogExportClient(this OtlpExporterOptionsBase options) => options.Protocol switch { OtlpExportProtocol.Grpc => new OtlpGrpcLogExportClient(options), @@ -127,7 +127,7 @@ public static THeaders GetHeaders(this OtlpExporterOptions options, Ac _ => throw new NotSupportedException($"Protocol {options.Protocol} is not supported."), }; - public static void TryEnableIHttpClientFactoryIntegration(this OtlpExporterOptions options, IServiceProvider serviceProvider, string httpClientName) + public static void TryEnableIHttpClientFactoryIntegration(this OtlpExporterOptionsBase options, IServiceProvider serviceProvider, string httpClientName) { if (serviceProvider != null && options.Protocol == OtlpExportProtocol.HttpProtobuf diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterSignals.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterSignals.cs new file mode 100644 index 00000000000..9050c32be8c --- /dev/null +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterSignals.cs @@ -0,0 +1,16 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#nullable enable + +namespace OpenTelemetry.Exporter; + +[Flags] +public enum OtlpExporterSignals +{ + All = Logs | Metrics | Traces, + None = 0b0, + Logs = 0b1, + Metrics = 0b10, + Traces = 0b100, +} diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpLogExporterHelperExtensions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpLogExporterHelperExtensions.cs index 85298517f95..32d2e45aad9 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpLogExporterHelperExtensions.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpLogExporterHelperExtensions.cs @@ -343,7 +343,7 @@ static LoggerProviderBuilder AddOtlpExporter( internal static BaseProcessor BuildOtlpLogExporter( IServiceProvider sp, - OtlpExporterOptions exporterOptions, + OtlpExporterOptionsBase exporterOptions, LogRecordExportProcessorOptions processorOptions, SdkLimitOptions sdkLimitOptions, ExperimentalOptions experimentalOptions, diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpMetricExporterExtensions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpMetricExporterExtensions.cs index f6e3be715d7..3366fde2350 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpMetricExporterExtensions.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpMetricExporterExtensions.cs @@ -176,7 +176,7 @@ public static MeterProviderBuilder AddOtlpExporter( } internal static MetricReader BuildOtlpExporterMetricReader( - OtlpExporterOptions exporterOptions, + OtlpExporterOptionsBase exporterOptions, MetricReaderOptions metricReaderOptions, IServiceProvider serviceProvider, Func, BaseExporter>? configureExporterInstance = null) diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpTraceExporterHelperExtensions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpTraceExporterHelperExtensions.cs index 45231f63604..a3864bc569c 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpTraceExporterHelperExtensions.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpTraceExporterHelperExtensions.cs @@ -101,6 +101,21 @@ internal static BaseProcessor BuildOtlpExporterProcessor( SdkLimitOptions sdkLimitOptions, IServiceProvider serviceProvider, Func, BaseExporter>? configureExporterInstance = null) + => BuildOtlpExporterProcessor( + exporterOptions, + sdkLimitOptions, + exporterOptions.ExportProcessorType, + exporterOptions.BatchExportProcessorOptions ?? new BatchExportActivityProcessorOptions(), + serviceProvider, + configureExporterInstance); + + internal static BaseProcessor BuildOtlpExporterProcessor( + OtlpExporterOptionsBase exporterOptions, + SdkLimitOptions sdkLimitOptions, + ExportProcessorType exportProcessorType, + BatchExportProcessorOptions batchExportProcessorOptions, + IServiceProvider serviceProvider, + Func, BaseExporter>? configureExporterInstance = null) { exporterOptions.TryEnableIHttpClientFactoryIntegration(serviceProvider, "OtlpTraceExporter"); @@ -111,13 +126,13 @@ internal static BaseProcessor BuildOtlpExporterProcessor( otlpExporter = configureExporterInstance(otlpExporter); } - if (exporterOptions.ExportProcessorType == ExportProcessorType.Simple) + if (exportProcessorType == ExportProcessorType.Simple) { return new SimpleActivityExportProcessor(otlpExporter); } else { - var batchOptions = exporterOptions.BatchExportProcessorOptions ?? new BatchExportActivityProcessorOptions(); + var batchOptions = batchExportProcessorOptions; return new BatchActivityExportProcessor( otlpExporter, diff --git a/src/OpenTelemetry/Logs/Builder/OpenTelemetryBuilderSdkLogsExtensions.cs b/src/OpenTelemetry/Logs/Builder/OpenTelemetryBuilderSdkLogsExtensions.cs deleted file mode 100644 index 26dfb72e58b..00000000000 --- a/src/OpenTelemetry/Logs/Builder/OpenTelemetryBuilderSdkLogsExtensions.cs +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -using Microsoft.Extensions.DependencyInjection; -using OpenTelemetry.Internal; -using OpenTelemetry.Logs; - -namespace OpenTelemetry; - -public static class OpenTelemetryBuilderSdkLogsExtensions -{ - public static IOpenTelemetryBuilder ConfigureLoggingExportProcessorOptions( - this IOpenTelemetryBuilder builder, - Action configure) - => ConfigureLoggingExportProcessorOptions(builder, name: null, configure); - - public static IOpenTelemetryBuilder ConfigureLoggingExportProcessorOptions( - this IOpenTelemetryBuilder builder, - string? name, - Action configure) - { - Guard.ThrowIfNull(configure); - - builder.Services.Configure(name, configure); - - return builder; - } -} diff --git a/src/OpenTelemetry/Metrics/Builder/OpenTelemetryBuilderSdkMetricsExtensions.cs b/src/OpenTelemetry/Metrics/Builder/OpenTelemetryBuilderSdkMetricsExtensions.cs deleted file mode 100644 index 40adcbb9bb9..00000000000 --- a/src/OpenTelemetry/Metrics/Builder/OpenTelemetryBuilderSdkMetricsExtensions.cs +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -using Microsoft.Extensions.DependencyInjection; -using OpenTelemetry.Internal; -using OpenTelemetry.Metrics; - -namespace OpenTelemetry; - -public static class OpenTelemetryBuilderSdkMetricsExtensions -{ - public static IOpenTelemetryBuilder ConfigureMetricsReaderOptions( - this IOpenTelemetryBuilder builder, - Action configure) - => ConfigureMetricsReaderOptions(builder, name: null, configure); - - public static IOpenTelemetryBuilder ConfigureMetricsReaderOptions( - this IOpenTelemetryBuilder builder, - string? name, - Action configure) - { - Guard.ThrowIfNull(configure); - - builder.Services.Configure(name, configure); - - return builder; - } -} diff --git a/src/OpenTelemetry/Trace/Builder/OpenTelemetryBuilderSdkTraceExtensions.cs b/src/OpenTelemetry/Trace/Builder/OpenTelemetryBuilderSdkTraceExtensions.cs deleted file mode 100644 index 2ce96c3fc35..00000000000 --- a/src/OpenTelemetry/Trace/Builder/OpenTelemetryBuilderSdkTraceExtensions.cs +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -using Microsoft.Extensions.DependencyInjection; -using OpenTelemetry.Internal; -using OpenTelemetry.Trace; - -namespace OpenTelemetry; - -public static class OpenTelemetryBuilderSdkTraceExtensions -{ - public static IOpenTelemetryBuilder ConfigureTracingExportProcessorOptions( - this IOpenTelemetryBuilder builder, - Action configure) - => ConfigureTracingExportProcessorOptions(builder, name: null, configure); - - public static IOpenTelemetryBuilder ConfigureTracingExportProcessorOptions( - this IOpenTelemetryBuilder builder, - string? name, - Action configure) - { - Guard.ThrowIfNull(configure); - - builder.Services.Configure(name, configure); - - return builder; - } -} From ab87eb05d4d1f3e221c5a780f1d6ead928cf232f Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Wed, 28 Feb 2024 12:20:00 -0800 Subject: [PATCH 03/48] First compiling version. --- .../ExportClient/BaseOtlpGrpcExportClient.cs | 2 +- .../ExportClient/BaseOtlpHttpExportClient.cs | 2 +- .../ExportClient/ExporterClientValidation.cs | 2 +- .../ExportClient/OtlpGrpcLogExportClient.cs | 2 +- .../ExportClient/OtlpGrpcMetricsExportClient.cs | 2 +- .../ExportClient/OtlpGrpcTraceExportClient.cs | 2 +- .../ExportClient/OtlpHttpLogExportClient.cs | 2 +- .../ExportClient/OtlpHttpMetricsExportClient.cs | 2 +- .../ExportClient/OtlpHttpTraceExportClient.cs | 2 +- .../OtlpExporterOptions.cs | 3 --- .../OtlpExporterOptionsBase.cs | 3 +++ .../OtlpLogExporter.cs | 4 ++-- .../OtlpMetricExporter.cs | 4 ++-- .../OtlpTraceExporter.cs | 8 ++++---- 14 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/BaseOtlpGrpcExportClient.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/BaseOtlpGrpcExportClient.cs index 4bb48cb8fe6..0cf08e57192 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/BaseOtlpGrpcExportClient.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/BaseOtlpGrpcExportClient.cs @@ -15,7 +15,7 @@ internal abstract class BaseOtlpGrpcExportClient : IExportClient : IExportClient /// Initializes a new instance of the class. /// /// Configuration options for the exporter. - public OtlpLogExporter(OtlpExporterOptions options) + public OtlpLogExporter(OtlpExporterOptionsBase options) : this(options, sdkLimitOptions: new(), experimentalOptions: new(), transmissionHandler: null) { } @@ -41,7 +41,7 @@ public OtlpLogExporter(OtlpExporterOptions options) /// . /// . internal OtlpLogExporter( - OtlpExporterOptions exporterOptions, + OtlpExporterOptionsBase exporterOptions, SdkLimitOptions sdkLimitOptions, ExperimentalOptions experimentalOptions, OtlpExporterTransmissionHandler? transmissionHandler = null) diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpMetricExporter.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpMetricExporter.cs index a0026d1e9f4..7873a13fd1b 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpMetricExporter.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpMetricExporter.cs @@ -24,7 +24,7 @@ public class OtlpMetricExporter : BaseExporter /// Initializes a new instance of the class. /// /// Configuration options for the exporter. - public OtlpMetricExporter(OtlpExporterOptions options) + public OtlpMetricExporter(OtlpExporterOptionsBase options) : this(options, transmissionHandler: null) { } @@ -35,7 +35,7 @@ public OtlpMetricExporter(OtlpExporterOptions options) /// Configuration options for the export. /// . internal OtlpMetricExporter( - OtlpExporterOptions options, + OtlpExporterOptionsBase options, OtlpExporterTransmissionHandler transmissionHandler = null) { // Each of the Otlp exporters: Traces, Metrics, and Logs set the same value for `OtlpKeyValueTransformer.LogUnsupportedAttributeType` diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpTraceExporter.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpTraceExporter.cs index f017d075428..dd9d500a0bc 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpTraceExporter.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpTraceExporter.cs @@ -25,7 +25,7 @@ public class OtlpTraceExporter : BaseExporter /// Initializes a new instance of the class. /// /// Configuration options for the export. - public OtlpTraceExporter(OtlpExporterOptions options) + public OtlpTraceExporter(OtlpExporterOptionsBase options) : this(options, sdkLimitOptions: new(), transmissionHandler: null) { } @@ -37,9 +37,9 @@ public OtlpTraceExporter(OtlpExporterOptions options) /// . /// . internal OtlpTraceExporter( - OtlpExporterOptions exporterOptions, - SdkLimitOptions sdkLimitOptions, - OtlpExporterTransmissionHandler transmissionHandler = null) + OtlpExporterOptionsBase exporterOptions, + SdkLimitOptions sdkLimitOptions, + OtlpExporterTransmissionHandler transmissionHandler = null) { Debug.Assert(exporterOptions != null, "exporterOptions was null"); Debug.Assert(sdkLimitOptions != null, "sdkLimitOptions was null"); From 0e9709e75891fac381683e34da22634ef79d79fc Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Wed, 28 Feb 2024 13:53:02 -0800 Subject: [PATCH 04/48] Tweaks. --- .../Builder/OtlpExporterBuilder.cs | 45 ++++++++++--------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OtlpExporterBuilder.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OtlpExporterBuilder.cs index b63027cab0c..97aa99f9648 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OtlpExporterBuilder.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OtlpExporterBuilder.cs @@ -45,7 +45,7 @@ public OtlpExporterBuilder SetSignalsToConfigure(OtlpExporterSignals signalsToCo return this; } - public OtlpExporterBuilder ConfigureDefaultOtlpExporterOptions( + public OtlpExporterBuilder ConfigureDefaultExporterOptions( Action configure) { Guard.ThrowIfNull(configure); @@ -56,7 +56,7 @@ public OtlpExporterBuilder ConfigureDefaultOtlpExporterOptions( return this; } - public OtlpExporterBuilder ConfigureLoggingOtlpExporterOptions( + public OtlpExporterBuilder ConfigureLoggingExporterOptions( Action configure) { Guard.ThrowIfNull(configure); @@ -76,7 +76,7 @@ public OtlpExporterBuilder ConfigureLoggingProcessorOptions( return this; } - public OtlpExporterBuilder ConfigureMetricsOtlpExporterOptions( + public OtlpExporterBuilder ConfigureMetricsExporterOptions( Action configure) { Guard.ThrowIfNull(configure); @@ -96,7 +96,7 @@ public OtlpExporterBuilder ConfigureMetricsReaderOptions( return this; } - public OtlpExporterBuilder ConfigureTracingOtlpExporterOptions( + public OtlpExporterBuilder ConfigureTracingExporterOptions( Action configure) { Guard.ThrowIfNull(configure); @@ -122,23 +122,26 @@ private static void BindConfigurationToOptions(IServiceCollection services, stri Debug.Assert(!string.IsNullOrEmpty(name), "name was null or empty"); Debug.Assert(configuration != null, "configuration was null"); -var json = """ -{ - "Signals": "Logs, Metrics", - "DefaultOptions": { - }, - "LoggingOptions": { - "ExportProcessorType": Batch, - "BatchExportProcessorOptions": { - "ScheduledDelayMilliseconds": 1000 - } - }, - "MetricsOptions": { - }, - "TracingOptions": { - } -} -"""; + /* Config JSON structure is expected to be something like this: + { + "Signals": "Logs, Metrics", + "DefaultOptions": { + "Endpoint": "http://default_endpoint/" + }, + "LoggingOptions": { + "Endpoint": "http://logs_endpoint/logs/" + "ExportProcessorType": Batch, + "BatchExportProcessorOptions": { + "ScheduledDelayMilliseconds": 1000 + } + }, + "MetricsOptions": { + "Endpoint": "http://metrics_endpoint/metrics/" + }, + "TracingOptions": { + } + } + */ services.Configure(name, configuration); From a19a81f723299e423b67f2f516a095a82c87e378 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Wed, 28 Feb 2024 13:53:31 -0800 Subject: [PATCH 05/48] Add concept of weight to BaseProcessor. --- ...nTelemetryBuilderOtlpExporterExtensions.cs | 43 +++++++++++-------- src/OpenTelemetry/BaseProcessor.cs | 16 +++++++ src/OpenTelemetry/Logs/LoggerProviderSdk.cs | 2 +- .../Trace/Builder/TracerProviderBuilderSdk.cs | 4 +- src/OpenTelemetry/Trace/TracerProviderSdk.cs | 8 ++-- 5 files changed, 49 insertions(+), 24 deletions(-) diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderOtlpExporterExtensions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderOtlpExporterExtensions.cs index f691162d808..61b79a82b62 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderOtlpExporterExtensions.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderOtlpExporterExtensions.cs @@ -34,7 +34,7 @@ public static IOpenTelemetryBuilder AddOtlpExporter( return AddOtlpExporter(builder, name: null, configure: otlpBuilder => { - otlpBuilder.ConfigureDefaultOtlpExporterOptions(o => + otlpBuilder.ConfigureDefaultExporterOptions(o => { o.Protocol = protocol; if (endpoint != null) @@ -67,7 +67,8 @@ public static IOpenTelemetryBuilder AddOtlpExporter( this IOpenTelemetryBuilder builder, string? name = null, IConfiguration? configuration = null, - Action? configure = null) + Action? configure = null, + bool addToEndOfPipeline = true) { Guard.ThrowIfNull(builder); @@ -80,12 +81,12 @@ public static IOpenTelemetryBuilder AddOtlpExporter( configure?.Invoke(otlpBuilder); - AddOtlpExporterInternal(builder, name); + AddOtlpExporterInternal(builder, name, addToEndOfPipeline); return builder; } - private static void AddOtlpExporterInternal(IOpenTelemetryBuilder builder, string? name) + private static void AddOtlpExporterInternal(IOpenTelemetryBuilder builder, string? name, bool addToEndOfPipeline) { builder.Services.RegisterOptionsFactory(configuration => new SdkLimitOptions(configuration)); builder.Services.RegisterOptionsFactory(configuration => new ExperimentalOptions(configuration)); @@ -109,13 +110,16 @@ private static void AddOtlpExporterInternal(IOpenTelemetryBuilder builder, strin return; } - logging.AddProcessor( - OtlpLogExporterHelperExtensions.BuildOtlpLogExporter( - sp, - builderOptions.LoggingOptions.ApplyDefaults(builderOptions.DefaultOptions), - builderOptions.LogRecordExportProcessorOptions ?? throw new NotSupportedException(), - builderOptions.SdkLimitOptions, - builderOptions.ExperimentalOptions)); + var processor = OtlpLogExporterHelperExtensions.BuildOtlpLogExporter( + sp, + builderOptions.LoggingOptions.ApplyDefaults(builderOptions.DefaultOptions), + builderOptions.LogRecordExportProcessorOptions ?? throw new NotSupportedException(), + builderOptions.SdkLimitOptions, + builderOptions.ExperimentalOptions); + + processor.Weight = addToEndOfPipeline ? int.MaxValue : 0; + + logging.AddProcessor(processor); }); builder.Services.ConfigureOpenTelemetryMeterProvider( @@ -147,13 +151,16 @@ private static void AddOtlpExporterInternal(IOpenTelemetryBuilder builder, strin var processorOptions = builderOptions.ActivityExportProcessorOptions ?? throw new NotSupportedException(); - tracing.AddProcessor( - OtlpTraceExporterHelperExtensions.BuildOtlpExporterProcessor( - builderOptions.TracingOptions.ApplyDefaults(builderOptions.DefaultOptions), - builderOptions.SdkLimitOptions, - processorOptions.ExportProcessorType, - processorOptions.BatchExportProcessorOptions, - sp)); + var processor = OtlpTraceExporterHelperExtensions.BuildOtlpExporterProcessor( + builderOptions.TracingOptions.ApplyDefaults(builderOptions.DefaultOptions), + builderOptions.SdkLimitOptions, + processorOptions.ExportProcessorType, + processorOptions.BatchExportProcessorOptions, + sp); + + processor.Weight = addToEndOfPipeline ? int.MaxValue : 0; + + tracing.AddProcessor(processor); }); static OtlpExporterBuilderOptions GetBuilderOptions(IServiceProvider sp, string name) diff --git a/src/OpenTelemetry/BaseProcessor.cs b/src/OpenTelemetry/BaseProcessor.cs index 88e1aca35e0..1cedf574232 100644 --- a/src/OpenTelemetry/BaseProcessor.cs +++ b/src/OpenTelemetry/BaseProcessor.cs @@ -27,6 +27,22 @@ public BaseProcessor() /// public BaseProvider? ParentProvider { get; private set; } + /// + /// Gets the weight of the processor. Default value: 0. + /// + /// + /// Note: Weight is used to order processors when building a provider + /// pipeline. Lower weighted processors come before higher weighted + /// processors. Changing the weight after a pipeline has been constructed + /// has no effect. + /// +#if EXPOSE_EXPERIMENTAL_FEATURES + public +#else + internal +#endif + int Weight { get; set; } + /// /// Called synchronously when a telemetry object is started. /// diff --git a/src/OpenTelemetry/Logs/LoggerProviderSdk.cs b/src/OpenTelemetry/Logs/LoggerProviderSdk.cs index f23308680e7..a03f50d770d 100644 --- a/src/OpenTelemetry/Logs/LoggerProviderSdk.cs +++ b/src/OpenTelemetry/Logs/LoggerProviderSdk.cs @@ -54,7 +54,7 @@ public LoggerProviderSdk( resourceBuilder.ServiceProvider = serviceProvider; this.Resource = resourceBuilder.Build(); - foreach (var processor in state.Processors) + foreach (var processor in state.Processors.OrderBy(p => p.Weight)) { this.AddProcessor(processor); } diff --git a/src/OpenTelemetry/Trace/Builder/TracerProviderBuilderSdk.cs b/src/OpenTelemetry/Trace/Builder/TracerProviderBuilderSdk.cs index 3e26b5fa27c..7b0af22d720 100644 --- a/src/OpenTelemetry/Trace/Builder/TracerProviderBuilderSdk.cs +++ b/src/OpenTelemetry/Trace/Builder/TracerProviderBuilderSdk.cs @@ -162,13 +162,13 @@ public TracerProviderBuilder ConfigureServices(Action config throw new NotSupportedException("Services cannot be configured after ServiceProvider has been created."); } - public void AddExceptionProcessorIfEnabled() + public void AddExceptionProcessorIfEnabled(ref IEnumerable> processors) { if (this.ExceptionProcessorEnabled) { try { - this.Processors.Insert(0, new ExceptionProcessor()); + processors = new BaseProcessor[] { new ExceptionProcessor() }.Concat(processors); } catch (Exception ex) { diff --git a/src/OpenTelemetry/Trace/TracerProviderSdk.cs b/src/OpenTelemetry/Trace/TracerProviderSdk.cs index 55507e87ff3..0e28af01b84 100644 --- a/src/OpenTelemetry/Trace/TracerProviderSdk.cs +++ b/src/OpenTelemetry/Trace/TracerProviderSdk.cs @@ -53,8 +53,6 @@ internal TracerProviderSdk( StringBuilder processorsAdded = new StringBuilder(); StringBuilder instrumentationFactoriesAdded = new StringBuilder(); - state.AddExceptionProcessorIfEnabled(); - var resourceBuilder = state.ResourceBuilder ?? ResourceBuilder.CreateDefault(); resourceBuilder.ServiceProvider = serviceProvider; this.Resource = resourceBuilder.Build(); @@ -74,7 +72,11 @@ internal TracerProviderSdk( } } - foreach (var processor in state.Processors) + var processors = (IEnumerable>)state.Processors.OrderBy(p => p.Weight); + + state.AddExceptionProcessorIfEnabled(ref processors); + + foreach (var processor in processors) { this.AddProcessor(processor); processorsAdded.Append(processor.GetType()); From 77e550c29b5203ad09d40e181100175f336625da Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Wed, 6 Mar 2024 15:48:51 -0800 Subject: [PATCH 06/48] Updates based on discussion. --- ...nTelemetryBuilderOtlpExporterExtensions.cs | 109 +++++++++++++----- ...lemetryBuilderServiceProviderExtensions.cs | 28 +++++ .../Builder/OtlpExporterBuilder.cs | 16 +-- .../Builder/OtlpExporterBuilderOptions.cs | 12 +- .../Builder/UseOtlpExporterRegistration.cs | 13 +++ .../OtlpExporterOptions.cs | 9 +- .../OtlpExporterOptionsBase.cs | 12 +- .../OtlpExporterSignals.cs | 4 +- .../OtlpLogExporterHelperExtensions.cs | 4 +- .../OtlpMetricExporterExtensions.cs | 2 + .../OtlpTraceExporterHelperExtensions.cs | 2 + src/OpenTelemetry/BaseProcessor.cs | 2 +- 12 files changed, 158 insertions(+), 55 deletions(-) create mode 100644 src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderServiceProviderExtensions.cs create mode 100644 src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/UseOtlpExporterRegistration.cs diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderOtlpExporterExtensions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderOtlpExporterExtensions.cs index 61b79a82b62..d608060f040 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderOtlpExporterExtensions.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderOtlpExporterExtensions.cs @@ -14,56 +14,94 @@ namespace OpenTelemetry.Exporter; +/// +/// Contains extension methods to facilitate registration of the OpenTelemetry +/// Protocol (OTLP) exporter into an +/// instance. +/// public static class OpenTelemetryBuilderOtlpExporterExtensions { - public static IOpenTelemetryBuilder AddOtlpExporter( + /// + /// Uses OpenTelemetry Protocol (OTLP) exporter for all signals. + /// + /// + /// Notes: + /// + /// Calling this method automatically enables logging, metrics, and + /// tracing. + /// The exporter registered by this method will be added as the last + /// processor in the pipeline established for logging and tracing. + /// This method can only be called once. Subsequent calls will results + /// in a being thrown. + /// This method cannot be called in addition to signal-specific + /// AddOtlpExporter methods. If this method is called signal-specific + /// AddOtlpExporter calls will result in a being thrown. + /// + /// + /// . + /// Supplied for chaining calls. + public static IOpenTelemetryBuilder UseOtlpExporter( this IOpenTelemetryBuilder builder) - => AddOtlpExporter(builder, name: null); - - public static IOpenTelemetryBuilder AddOtlpExporter( + => UseOtlpExporter(builder, name: null); + + /// + /// + /// + /// . + /// The base endpoint to use. A signal-specific + /// path will be appended to the base endpoint for each signal + /// automatically. + public static IOpenTelemetryBuilder UseOtlpExporter( this IOpenTelemetryBuilder builder, - Uri endpoint) - => AddOtlpExporter(builder, OtlpExportProtocol.Grpc, endpoint); - - public static IOpenTelemetryBuilder AddOtlpExporter( + Uri baseEndpoint) + => UseOtlpExporter(builder, OtlpExportProtocol.Grpc, baseEndpoint); + + /// + /// + /// + /// . + /// . + /// + public static IOpenTelemetryBuilder UseOtlpExporter( this IOpenTelemetryBuilder builder, OtlpExportProtocol protocol, - Uri endpoint) + Uri baseEndpoint) { - Guard.ThrowIfNull(endpoint); + Guard.ThrowIfNull(baseEndpoint); - return AddOtlpExporter(builder, name: null, configure: otlpBuilder => + return UseOtlpExporter(builder, name: null, configure: otlpBuilder => { otlpBuilder.ConfigureDefaultExporterOptions(o => { o.Protocol = protocol; - if (endpoint != null) + if (baseEndpoint != null) { - o.Endpoint = endpoint; + o.Endpoint = baseEndpoint; } }); }); } - public static IOpenTelemetryBuilder AddOtlpExporter( + internal static IOpenTelemetryBuilder AddOtlpExporter( this IOpenTelemetryBuilder builder, Action configure) { Guard.ThrowIfNull(configure); - return AddOtlpExporter(builder, name: null, configure: configure); + return UseOtlpExporter(builder, name: null, configure: configure); } - public static IOpenTelemetryBuilder AddOtlpExporter( + internal static IOpenTelemetryBuilder UseOtlpExporter( this IOpenTelemetryBuilder builder, IConfiguration configuration) { Guard.ThrowIfNull(configuration); - return AddOtlpExporter(builder, name: null, configuration: configuration); + return UseOtlpExporter(builder, name: null, configuration: configuration); } - public static IOpenTelemetryBuilder AddOtlpExporter( + internal static IOpenTelemetryBuilder UseOtlpExporter( this IOpenTelemetryBuilder builder, string? name = null, IConfiguration? configuration = null, @@ -81,16 +119,25 @@ public static IOpenTelemetryBuilder AddOtlpExporter( configure?.Invoke(otlpBuilder); - AddOtlpExporterInternal(builder, name, addToEndOfPipeline); + UseOtlpExporterInternal(builder, name, addToEndOfPipeline); return builder; } - private static void AddOtlpExporterInternal(IOpenTelemetryBuilder builder, string? name, bool addToEndOfPipeline) + private static void UseOtlpExporterInternal(IOpenTelemetryBuilder builder, string? name, bool addToEndOfPipeline) { - builder.Services.RegisterOptionsFactory(configuration => new SdkLimitOptions(configuration)); - builder.Services.RegisterOptionsFactory(configuration => new ExperimentalOptions(configuration)); - builder.Services.RegisterOptionsFactory((sp, configuration, name) => new OtlpExporterBuilderOptions( + builder + .WithLogging() + .WithMetrics() + .WithTracing(); + + var services = builder.Services; + + services.AddSingleton(); + + services.RegisterOptionsFactory(configuration => new SdkLimitOptions(configuration)); + services.RegisterOptionsFactory(configuration => new ExperimentalOptions(configuration)); + services.RegisterOptionsFactory((sp, configuration, name) => new OtlpExporterBuilderOptions( configuration, sp.GetRequiredService>().CurrentValue, sp.GetRequiredService>().CurrentValue, @@ -100,10 +147,10 @@ private static void AddOtlpExporterInternal(IOpenTelemetryBuilder builder, strin name ??= Options.DefaultName; - builder.Services.ConfigureOpenTelemetryLoggerProvider( + services.ConfigureOpenTelemetryLoggerProvider( (sp, logging) => { - var builderOptions = GetBuilderOptions(sp, name); + var builderOptions = GetBuilderOptionsAndValidateRegistrations(sp, name); if (!builderOptions.Signals.HasFlag(OtlpExporterSignals.Logs)) { @@ -122,10 +169,10 @@ private static void AddOtlpExporterInternal(IOpenTelemetryBuilder builder, strin logging.AddProcessor(processor); }); - builder.Services.ConfigureOpenTelemetryMeterProvider( + services.ConfigureOpenTelemetryMeterProvider( (sp, metrics) => { - var builderOptions = GetBuilderOptions(sp, name); + var builderOptions = GetBuilderOptionsAndValidateRegistrations(sp, name); if (!builderOptions.Signals.HasFlag(OtlpExporterSignals.Metrics)) { @@ -139,10 +186,10 @@ private static void AddOtlpExporterInternal(IOpenTelemetryBuilder builder, strin sp)); }); - builder.Services.ConfigureOpenTelemetryTracerProvider( + services.ConfigureOpenTelemetryTracerProvider( (sp, tracing) => { - var builderOptions = GetBuilderOptions(sp, name); + var builderOptions = GetBuilderOptionsAndValidateRegistrations(sp, name); if (!builderOptions.Signals.HasFlag(OtlpExporterSignals.Traces)) { @@ -163,8 +210,10 @@ private static void AddOtlpExporterInternal(IOpenTelemetryBuilder builder, strin tracing.AddProcessor(processor); }); - static OtlpExporterBuilderOptions GetBuilderOptions(IServiceProvider sp, string name) + static OtlpExporterBuilderOptions GetBuilderOptionsAndValidateRegistrations(IServiceProvider sp, string name) { + sp.EnsureSingleUseOtlpExporterRegistration(); + return sp.GetRequiredService>().Get(name); } } diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderServiceProviderExtensions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderServiceProviderExtensions.cs new file mode 100644 index 00000000000..353cebb3d9b --- /dev/null +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderServiceProviderExtensions.cs @@ -0,0 +1,28 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +using Microsoft.Extensions.DependencyInjection; +using OpenTelemetry.Exporter; + +namespace System; + +internal static class OpenTelemetryBuilderServiceProviderExtensions +{ + public static void EnsureSingleUseOtlpExporterRegistration(this IServiceProvider serviceProvider) + { + var registrations = serviceProvider.GetServices(); + if (registrations.Count() > 1) + { + throw new NotSupportedException("Multiple calls to UseOtlpExporter on the same IServiceCollection are not supported."); + } + } + + public static void EnsureNoUseOtlpExporterRegistrations(this IServiceProvider serviceProvider) + { + var registrations = serviceProvider.GetServices(); + if (registrations.Any()) + { + throw new NotSupportedException("Signal-specific AddOtlpExporter methods and the cross-cutting UseOtlpExporter method being invoked on the same IServiceCollection is not supported."); + } + } +} diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OtlpExporterBuilder.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OtlpExporterBuilder.cs index 97aa99f9648..2e64d233e09 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OtlpExporterBuilder.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OtlpExporterBuilder.cs @@ -13,7 +13,7 @@ namespace OpenTelemetry.Exporter; -public sealed class OtlpExporterBuilder +internal sealed class OtlpExporterBuilder { private readonly string? name; @@ -25,13 +25,13 @@ internal OtlpExporterBuilder( Debug.Assert(services != null, "services was null"); this.name = name; - this.Services = services; + this.Services = services!; if (configuration != null) { Debug.Assert(!string.IsNullOrEmpty(name), "name was null or empty"); - BindConfigurationToOptions(services, name!, configuration); + BindConfigurationToOptions(services!, name!, configuration); } } @@ -143,15 +143,15 @@ private static void BindConfigurationToOptions(IServiceCollection services, stri } */ - services.Configure(name, configuration); + services!.Configure(name, configuration!); - services.Configure( - name, configuration.GetSection(nameof(OtlpExporterBuilderOptions.LoggingOptions))); + services!.Configure( + name, configuration!.GetSection(nameof(OtlpExporterBuilderOptions.LoggingOptions))); - services.Configure( + services!.Configure( name, configuration.GetSection(nameof(OtlpExporterBuilderOptions.MetricsOptions))); - services.Configure( + services!.Configure( name, configuration.GetSection(nameof(OtlpExporterBuilderOptions.TracingOptions))); } } diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OtlpExporterBuilderOptions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OtlpExporterBuilderOptions.cs index 778505ed2f8..90d6b9d5d9d 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OtlpExporterBuilderOptions.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OtlpExporterBuilderOptions.cs @@ -32,19 +32,19 @@ public OtlpExporterBuilderOptions( Debug.Assert(sdkLimitOptions != null, "sdkLimitOptions was null"); Debug.Assert(experimentalOptions != null, "experimentalOptions was null"); - this.SdkLimitOptions = sdkLimitOptions; - this.ExperimentalOptions = experimentalOptions; + this.SdkLimitOptions = sdkLimitOptions!; + this.ExperimentalOptions = experimentalOptions!; this.LogRecordExportProcessorOptions = logRecordExportProcessorOptions; this.MetricReaderOptions = metricReaderOptions; this.ActivityExportProcessorOptions = activityExportProcessorOptions; - this.DefaultOptions = new OtlpExporterOptionsBase(configuration, OtlpExporterSignals.None); + this.DefaultOptions = new OtlpExporterOptionsBase(configuration!, OtlpExporterSignals.None); - this.LoggingOptions = new OtlpExporterOptionsBase(configuration, OtlpExporterSignals.Logs); + this.LoggingOptions = new OtlpExporterOptionsBase(configuration!, OtlpExporterSignals.Logs); - this.MetricsOptions = new OtlpExporterOptionsBase(configuration, OtlpExporterSignals.Metrics); + this.MetricsOptions = new OtlpExporterOptionsBase(configuration!, OtlpExporterSignals.Metrics); - this.TracingOptions = new OtlpExporterOptionsBase(configuration, OtlpExporterSignals.Traces); + this.TracingOptions = new OtlpExporterOptionsBase(configuration!, OtlpExporterSignals.Traces); } public OtlpExporterSignals Signals { get; set; } = OtlpExporterSignals.All; diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/UseOtlpExporterRegistration.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/UseOtlpExporterRegistration.cs new file mode 100644 index 00000000000..ad0ad9fbc90 --- /dev/null +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/UseOtlpExporterRegistration.cs @@ -0,0 +1,13 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#nullable enable + +namespace OpenTelemetry.Exporter; + +// Note: This class is added to the IServiceCollection when UseOtlpExporter is +// called. Its purpose is to detect registrations so that subsequent calls and +// calls to signal-specific AddOtlpExporter can throw. +internal sealed class UseOtlpExporterRegistration +{ +} diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterOptions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterOptions.cs index 3d905927580..3def7129c89 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterOptions.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterOptions.cs @@ -15,9 +15,12 @@ namespace OpenTelemetry.Exporter; /// /// OpenTelemetry Protocol (OTLP) exporter options. -/// OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_EXPORTER_OTLP_HEADERS, OTEL_EXPORTER_OTLP_TIMEOUT, OTEL_EXPORTER_OTLP_PROTOCOL -/// environment variables are parsed during object construction. /// +/// +/// Note: OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_EXPORTER_OTLP_HEADERS, +/// OTEL_EXPORTER_OTLP_TIMEOUT, and OTEL_EXPORTER_OTLP_PROTOCOL environment +/// variables are parsed during object construction. +/// public class OtlpExporterOptions : OtlpExporterOptionsBase { internal static readonly KeyValuePair[] StandardHeaders = new KeyValuePair[] @@ -42,7 +45,7 @@ internal OtlpExporterOptions( { Debug.Assert(defaultBatchOptions != null, "defaultBatchOptions was null"); - this.BatchExportProcessorOptions = defaultBatchOptions; + this.BatchExportProcessorOptions = defaultBatchOptions!; } /// diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterOptionsBase.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterOptionsBase.cs index 01b730c1192..ca301483978 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterOptionsBase.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterOptionsBase.cs @@ -14,6 +14,10 @@ namespace OpenTelemetry.Exporter; +/// +/// Base class for OpenTelemetry Protocol (OTLP) exporter options shared by all +/// signals. +/// public class OtlpExporterOptionsBase { internal const string EndpointEnvVarName = "OTEL_EXPORTER_OTLP_ENDPOINT"; @@ -42,7 +46,7 @@ internal OtlpExporterOptionsBase( if (signal == OtlpExporterSignals.None) { this.ApplyConfigurationUsingSpecificationEnvVars( - configuration, + configuration!, EndpointEnvVarName, ProtocolEnvVarName, HeadersEnvVarName, @@ -52,7 +56,7 @@ internal OtlpExporterOptionsBase( if (signal.HasFlag(OtlpExporterSignals.Logs)) { this.ApplyConfigurationUsingSpecificationEnvVars( - configuration, + configuration!, "OTEL_EXPORTER_OTLP_LOGS_ENDPOINT", "OTEL_EXPORTER_OTLP_LOGS_PROTOCOL", "OTEL_EXPORTER_OTLP_LOGS_HEADERS", @@ -62,7 +66,7 @@ internal OtlpExporterOptionsBase( if (signal.HasFlag(OtlpExporterSignals.Metrics)) { this.ApplyConfigurationUsingSpecificationEnvVars( - configuration, + configuration!, "OTEL_EXPORTER_OTLP_METRICS_ENDPOINT", "OTEL_EXPORTER_OTLP_METRICS_PROTOCOL", "OTEL_EXPORTER_OTLP_METRICS_HEADERS", @@ -72,7 +76,7 @@ internal OtlpExporterOptionsBase( if (signal.HasFlag(OtlpExporterSignals.Traces)) { this.ApplyConfigurationUsingSpecificationEnvVars( - configuration, + configuration!, "OTEL_EXPORTER_OTLP_TRACES_ENDPOINT", "OTEL_EXPORTER_OTLP_TRACES_PROTOCOL", "OTEL_EXPORTER_OTLP_TRACES_HEADERS", diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterSignals.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterSignals.cs index 9050c32be8c..190b7397a36 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterSignals.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterSignals.cs @@ -6,11 +6,13 @@ namespace OpenTelemetry.Exporter; [Flags] -public enum OtlpExporterSignals +internal enum OtlpExporterSignals { +#pragma warning disable SA1602 // Enumeration items should be documented All = Logs | Metrics | Traces, None = 0b0, Logs = 0b1, Metrics = 0b10, Traces = 0b100, +#pragma warning restore SA1602 // Enumeration items should be documented } diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpLogExporterHelperExtensions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpLogExporterHelperExtensions.cs index 32d2e45aad9..2e77050bb9a 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpLogExporterHelperExtensions.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpLogExporterHelperExtensions.cs @@ -349,14 +349,14 @@ internal static BaseProcessor BuildOtlpLogExporter( ExperimentalOptions experimentalOptions, Func, BaseExporter>? 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"); + sp.EnsureNoUseOtlpExporterRegistrations(); + /* * Note: * diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpMetricExporterExtensions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpMetricExporterExtensions.cs index 3366fde2350..67bfd435f8d 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpMetricExporterExtensions.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpMetricExporterExtensions.cs @@ -181,6 +181,8 @@ internal static MetricReader BuildOtlpExporterMetricReader( IServiceProvider serviceProvider, Func, BaseExporter>? configureExporterInstance = null) { + serviceProvider.EnsureNoUseOtlpExporterRegistrations(); + exporterOptions.TryEnableIHttpClientFactoryIntegration(serviceProvider, "OtlpMetricExporter"); BaseExporter metricExporter = new OtlpMetricExporter(exporterOptions); diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpTraceExporterHelperExtensions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpTraceExporterHelperExtensions.cs index a3864bc569c..99b4685297a 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpTraceExporterHelperExtensions.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpTraceExporterHelperExtensions.cs @@ -117,6 +117,8 @@ internal static BaseProcessor BuildOtlpExporterProcessor( IServiceProvider serviceProvider, Func, BaseExporter>? configureExporterInstance = null) { + serviceProvider.EnsureNoUseOtlpExporterRegistrations(); + exporterOptions.TryEnableIHttpClientFactoryIntegration(serviceProvider, "OtlpTraceExporter"); BaseExporter otlpExporter = new OtlpTraceExporter(exporterOptions, sdkLimitOptions); diff --git a/src/OpenTelemetry/BaseProcessor.cs b/src/OpenTelemetry/BaseProcessor.cs index 1cedf574232..e23e0abff10 100644 --- a/src/OpenTelemetry/BaseProcessor.cs +++ b/src/OpenTelemetry/BaseProcessor.cs @@ -28,7 +28,7 @@ public BaseProcessor() public BaseProvider? ParentProvider { get; private set; } /// - /// Gets the weight of the processor. Default value: 0. + /// Gets or sets the weight of the processor. Default value: 0. /// /// /// Note: Weight is used to order processors when building a provider From ef6652b250a59e29d4d8797202891c9be59f48f2 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Thu, 7 Mar 2024 10:46:28 -0800 Subject: [PATCH 07/48] Tweak processor pipeline weight design. --- ...nTelemetryBuilderOtlpExporterExtensions.cs | 10 ++-- src/OpenTelemetry/BaseProcessor.cs | 6 ++- src/OpenTelemetry/Logs/LoggerProviderSdk.cs | 2 +- src/OpenTelemetry/ProcessorPipelineWeight.cs | 48 +++++++++++++++++++ src/OpenTelemetry/Trace/TracerProviderSdk.cs | 2 +- 5 files changed, 59 insertions(+), 9 deletions(-) create mode 100644 src/OpenTelemetry/ProcessorPipelineWeight.cs diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderOtlpExporterExtensions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderOtlpExporterExtensions.cs index d608060f040..f3c8cfd9c02 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderOtlpExporterExtensions.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderOtlpExporterExtensions.cs @@ -106,7 +106,7 @@ internal static IOpenTelemetryBuilder UseOtlpExporter( string? name = null, IConfiguration? configuration = null, Action? configure = null, - bool addToEndOfPipeline = true) + ProcessorPipelineWeight processorPipelineWeight = ProcessorPipelineWeight.PipelineExporter) { Guard.ThrowIfNull(builder); @@ -119,12 +119,12 @@ internal static IOpenTelemetryBuilder UseOtlpExporter( configure?.Invoke(otlpBuilder); - UseOtlpExporterInternal(builder, name, addToEndOfPipeline); + UseOtlpExporterInternal(builder, name, processorPipelineWeight); return builder; } - private static void UseOtlpExporterInternal(IOpenTelemetryBuilder builder, string? name, bool addToEndOfPipeline) + private static void UseOtlpExporterInternal(IOpenTelemetryBuilder builder, string? name, ProcessorPipelineWeight processorPipelineWeight) { builder .WithLogging() @@ -164,7 +164,7 @@ private static void UseOtlpExporterInternal(IOpenTelemetryBuilder builder, strin builderOptions.SdkLimitOptions, builderOptions.ExperimentalOptions); - processor.Weight = addToEndOfPipeline ? int.MaxValue : 0; + processor.PipelineWeight = processorPipelineWeight; logging.AddProcessor(processor); }); @@ -205,7 +205,7 @@ private static void UseOtlpExporterInternal(IOpenTelemetryBuilder builder, strin processorOptions.BatchExportProcessorOptions, sp); - processor.Weight = addToEndOfPipeline ? int.MaxValue : 0; + processor.PipelineWeight = addToEndOfPipeline ? int.MaxValue : 0; tracing.AddProcessor(processor); }); diff --git a/src/OpenTelemetry/BaseProcessor.cs b/src/OpenTelemetry/BaseProcessor.cs index e23e0abff10..f0d8a189ff7 100644 --- a/src/OpenTelemetry/BaseProcessor.cs +++ b/src/OpenTelemetry/BaseProcessor.cs @@ -28,7 +28,9 @@ public BaseProcessor() public BaseProvider? ParentProvider { get; private set; } /// - /// Gets or sets the weight of the processor. Default value: 0. + /// Gets or sets the weight of the processor when added to the provider + /// pipeline. Default value: . /// /// /// Note: Weight is used to order processors when building a provider @@ -41,7 +43,7 @@ public BaseProcessor() #else internal #endif - int Weight { get; set; } + ProcessorPipelineWeight PipelineWeight { get; set; } = ProcessorPipelineWeight.PipelineMiddle; /// /// Called synchronously when a telemetry object is started. diff --git a/src/OpenTelemetry/Logs/LoggerProviderSdk.cs b/src/OpenTelemetry/Logs/LoggerProviderSdk.cs index a03f50d770d..61a023787fb 100644 --- a/src/OpenTelemetry/Logs/LoggerProviderSdk.cs +++ b/src/OpenTelemetry/Logs/LoggerProviderSdk.cs @@ -54,7 +54,7 @@ public LoggerProviderSdk( resourceBuilder.ServiceProvider = serviceProvider; this.Resource = resourceBuilder.Build(); - foreach (var processor in state.Processors.OrderBy(p => p.Weight)) + foreach (var processor in state.Processors.OrderBy(p => (int)p.PipelineWeight)) { this.AddProcessor(processor); } diff --git a/src/OpenTelemetry/ProcessorPipelineWeight.cs b/src/OpenTelemetry/ProcessorPipelineWeight.cs new file mode 100644 index 00000000000..bc343b27321 --- /dev/null +++ b/src/OpenTelemetry/ProcessorPipelineWeight.cs @@ -0,0 +1,48 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +namespace OpenTelemetry; + +#if EXPOSE_EXPERIMENTAL_FEATURES +/// +/// Contains definitions for common processor pipeline weights. +/// +public +#else +internal +#endif + enum ProcessorPipelineWeight +{ + /// + /// Pipeline start. Value: . + /// + PipelineStart = int.MinValue, + + /// + /// Pipeline enrichment. Value: -10000. + /// + /// + /// Note: Enrichment processors which modify telemetry typically need to run + /// before exporters. + /// + PipelineEnrichment = -10_000, + + /// + /// Pipeline middle. Value: 0. + /// + PipelineMiddle = 0, + + /// + /// Pipeline exporter. Value: 10000. + /// + /// + /// Note: Export processors emitting telemetry typically need to run after + /// enrichment processor. + /// + PipelineExporter = 10_000, + + /// + /// Pipeline end. Value: . + /// + PipelineEnd = int.MaxValue, +} diff --git a/src/OpenTelemetry/Trace/TracerProviderSdk.cs b/src/OpenTelemetry/Trace/TracerProviderSdk.cs index 0e28af01b84..0bbfb1a9b9d 100644 --- a/src/OpenTelemetry/Trace/TracerProviderSdk.cs +++ b/src/OpenTelemetry/Trace/TracerProviderSdk.cs @@ -72,7 +72,7 @@ internal TracerProviderSdk( } } - var processors = (IEnumerable>)state.Processors.OrderBy(p => p.Weight); + var processors = (IEnumerable>)state.Processors.OrderBy(p => (int)p.PipelineWeight); state.AddExceptionProcessorIfEnabled(ref processors); From ea8d6a165fee3a101448799c94fd4c4d771cecc9 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Thu, 7 Mar 2024 11:22:44 -0800 Subject: [PATCH 08/48] Revert OtlpExporterOptionsBase refactor to shrink the diff of changes. --- ...nTelemetryBuilderOtlpExporterExtensions.cs | 8 +- .../Builder/OtlpExporterBuilder.cs | 8 +- .../Builder/OtlpExporterBuilderOptions.cs | 23 +- .../IOtlpExporterOptions.cs | 95 +++++++ .../ExportClient/BaseOtlpGrpcExportClient.cs | 2 +- .../ExportClient/BaseOtlpHttpExportClient.cs | 2 +- .../ExportClient/ExporterClientValidation.cs | 2 +- .../ExportClient/OtlpGrpcLogExportClient.cs | 2 +- .../OtlpGrpcMetricsExportClient.cs | 2 +- .../ExportClient/OtlpGrpcTraceExportClient.cs | 2 +- .../ExportClient/OtlpHttpLogExportClient.cs | 2 +- .../OtlpHttpMetricsExportClient.cs | 2 +- .../ExportClient/OtlpHttpTraceExportClient.cs | 2 +- .../OtlpExporterOptions.cs | 182 ++++++++++++- .../OtlpExporterOptionsBase.cs | 241 ------------------ .../OtlpExporterOptionsExtensions.cs | 22 +- .../OtlpLogExporter.cs | 4 +- .../OtlpLogExporterHelperExtensions.cs | 2 +- .../OtlpMetricExporter.cs | 4 +- .../OtlpMetricExporterExtensions.cs | 2 +- .../OtlpTraceExporter.cs | 4 +- .../OtlpTraceExporterHelperExtensions.cs | 2 +- 22 files changed, 326 insertions(+), 289 deletions(-) create mode 100644 src/OpenTelemetry.Exporter.OpenTelemetryProtocol/IOtlpExporterOptions.cs delete mode 100644 src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterOptionsBase.cs diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderOtlpExporterExtensions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderOtlpExporterExtensions.cs index f3c8cfd9c02..b8cf4d9c9db 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderOtlpExporterExtensions.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderOtlpExporterExtensions.cs @@ -159,7 +159,7 @@ private static void UseOtlpExporterInternal(IOpenTelemetryBuilder builder, strin var processor = OtlpLogExporterHelperExtensions.BuildOtlpLogExporter( sp, - builderOptions.LoggingOptions.ApplyDefaults(builderOptions.DefaultOptions), + builderOptions.LoggingOptionsInstance.ApplyDefaults(builderOptions.DefaultOptionsInstance), builderOptions.LogRecordExportProcessorOptions ?? throw new NotSupportedException(), builderOptions.SdkLimitOptions, builderOptions.ExperimentalOptions); @@ -181,7 +181,7 @@ private static void UseOtlpExporterInternal(IOpenTelemetryBuilder builder, strin metrics.AddReader( OtlpMetricExporterExtensions.BuildOtlpExporterMetricReader( - builderOptions.MetricsOptions.ApplyDefaults(builderOptions.DefaultOptions), + builderOptions.MetricsOptionsInstance.ApplyDefaults(builderOptions.DefaultOptionsInstance), builderOptions.MetricReaderOptions ?? throw new NotSupportedException(), sp)); }); @@ -199,13 +199,13 @@ private static void UseOtlpExporterInternal(IOpenTelemetryBuilder builder, strin var processorOptions = builderOptions.ActivityExportProcessorOptions ?? throw new NotSupportedException(); var processor = OtlpTraceExporterHelperExtensions.BuildOtlpExporterProcessor( - builderOptions.TracingOptions.ApplyDefaults(builderOptions.DefaultOptions), + builderOptions.TracingOptionsInstance.ApplyDefaults(builderOptions.DefaultOptionsInstance), builderOptions.SdkLimitOptions, processorOptions.ExportProcessorType, processorOptions.BatchExportProcessorOptions, sp); - processor.PipelineWeight = addToEndOfPipeline ? int.MaxValue : 0; + processor.PipelineWeight = processorPipelineWeight; tracing.AddProcessor(processor); }); diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OtlpExporterBuilder.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OtlpExporterBuilder.cs index 2e64d233e09..9d461560a7e 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OtlpExporterBuilder.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OtlpExporterBuilder.cs @@ -46,7 +46,7 @@ public OtlpExporterBuilder SetSignalsToConfigure(OtlpExporterSignals signalsToCo } public OtlpExporterBuilder ConfigureDefaultExporterOptions( - Action configure) + Action configure) { Guard.ThrowIfNull(configure); @@ -57,7 +57,7 @@ public OtlpExporterBuilder ConfigureDefaultExporterOptions( } public OtlpExporterBuilder ConfigureLoggingExporterOptions( - Action configure) + Action configure) { Guard.ThrowIfNull(configure); @@ -77,7 +77,7 @@ public OtlpExporterBuilder ConfigureLoggingProcessorOptions( } public OtlpExporterBuilder ConfigureMetricsExporterOptions( - Action configure) + Action configure) { Guard.ThrowIfNull(configure); @@ -97,7 +97,7 @@ public OtlpExporterBuilder ConfigureMetricsReaderOptions( } public OtlpExporterBuilder ConfigureTracingExporterOptions( - Action configure) + Action configure) { Guard.ThrowIfNull(configure); diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OtlpExporterBuilderOptions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OtlpExporterBuilderOptions.cs index 90d6b9d5d9d..1ca167bd91f 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OtlpExporterBuilderOptions.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OtlpExporterBuilderOptions.cs @@ -20,6 +20,11 @@ internal sealed class OtlpExporterBuilderOptions internal readonly MetricReaderOptions? MetricReaderOptions; internal readonly ActivityExportProcessorOptions? ActivityExportProcessorOptions; + internal readonly OtlpExporterOptions DefaultOptionsInstance; + internal readonly OtlpExporterOptions LoggingOptionsInstance; + internal readonly OtlpExporterOptions MetricsOptionsInstance; + internal readonly OtlpExporterOptions TracingOptionsInstance; + public OtlpExporterBuilderOptions( IConfiguration configuration, SdkLimitOptions sdkLimitOptions, @@ -38,22 +43,24 @@ public OtlpExporterBuilderOptions( this.MetricReaderOptions = metricReaderOptions; this.ActivityExportProcessorOptions = activityExportProcessorOptions; - this.DefaultOptions = new OtlpExporterOptionsBase(configuration!, OtlpExporterSignals.None); + var defaultBatchOptions = this.ActivityExportProcessorOptions!.BatchExportProcessorOptions; + + this.DefaultOptionsInstance = new OtlpExporterOptions(configuration!, OtlpExporterSignals.None, defaultBatchOptions); - this.LoggingOptions = new OtlpExporterOptionsBase(configuration!, OtlpExporterSignals.Logs); + this.LoggingOptionsInstance = new OtlpExporterOptions(configuration!, OtlpExporterSignals.Logs, defaultBatchOptions); - this.MetricsOptions = new OtlpExporterOptionsBase(configuration!, OtlpExporterSignals.Metrics); + this.MetricsOptionsInstance = new OtlpExporterOptions(configuration!, OtlpExporterSignals.Metrics, defaultBatchOptions); - this.TracingOptions = new OtlpExporterOptionsBase(configuration!, OtlpExporterSignals.Traces); + this.TracingOptionsInstance = new OtlpExporterOptions(configuration!, OtlpExporterSignals.Traces, defaultBatchOptions); } public OtlpExporterSignals Signals { get; set; } = OtlpExporterSignals.All; - public OtlpExporterOptionsBase DefaultOptions { get; } + public IOtlpExporterOptions DefaultOptions => this.DefaultOptionsInstance; - public OtlpExporterOptionsBase LoggingOptions { get; } + public IOtlpExporterOptions LoggingOptions => this.LoggingOptionsInstance; - public OtlpExporterOptionsBase MetricsOptions { get; } + public IOtlpExporterOptions MetricsOptions => this.MetricsOptionsInstance; - public OtlpExporterOptionsBase TracingOptions { get; } + public IOtlpExporterOptions TracingOptions => this.TracingOptionsInstance; } diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/IOtlpExporterOptions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/IOtlpExporterOptions.cs new file mode 100644 index 00000000000..f35d7b5c688 --- /dev/null +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/IOtlpExporterOptions.cs @@ -0,0 +1,95 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#nullable enable + +#if NETFRAMEWORK +using System.Net.Http; +#endif + +namespace OpenTelemetry.Exporter; + +/// +/// Describes the OpenTelemetry Protocol (OTLP) exporter options shared by all +/// signals. +/// +internal interface IOtlpExporterOptions +{ + /// + /// Gets or sets the the OTLP transport protocol. + /// + OtlpExportProtocol Protocol { get; set; } + + /// + /// Gets or sets the target to which the exporter is going to send + /// telemetry. + /// + /// + /// Notes: + /// + /// must be a valid with + /// scheme (http or https) and host, and may contain a port and path. + /// The default value is based on the property: + /// + /// http://localhost:4317 for . + /// http://localhost:4318 for . + /// + /// + /// + /// + Uri Endpoint { get; set; } + + /// + /// Gets or sets optional headers for the connection. + /// + /// + /// Note: Refer to the + /// OpenTelemetry Specification for details on the format of . + /// + string? Headers { get; set; } + + /// + /// Gets or sets the max waiting time (in milliseconds) for the backend to + /// process each batch. Default value: 10000. + /// + int TimeoutMilliseconds { get; set; } + + /// + /// Gets or sets the factory function called to create the instance that will be used at runtime to + /// transmit telemetry over HTTP. The returned instance will be reused + /// for all export invocations. + /// + /// + /// Notes: + /// + /// This is only invoked for the protocol. + /// The default behavior when using tracing registration extensions is + /// if an IHttpClientFactory + /// instance can be resolved through the application then an will be + /// created through the factory with the name "OtlpTraceExporter" otherwise + /// an will be instantiated directly. + /// The default behavior when using metrics registration extensions is + /// if an IHttpClientFactory + /// instance can be resolved through the application then an will be + /// created through the factory with the name "OtlpMetricExporter" otherwise + /// an will be instantiated directly. + /// + /// The default behavior when using logging registration extensions is an + /// will be instantiated directly. IHttpClientFactory + /// is not currently supported for logging. + /// + /// + /// + Func HttpClientFactory { get; set; } +} diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/BaseOtlpGrpcExportClient.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/BaseOtlpGrpcExportClient.cs index 0cf08e57192..4bb48cb8fe6 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/BaseOtlpGrpcExportClient.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/BaseOtlpGrpcExportClient.cs @@ -15,7 +15,7 @@ internal abstract class BaseOtlpGrpcExportClient : IExportClient : IExportClient -public class OtlpExporterOptions : OtlpExporterOptionsBase +public class OtlpExporterOptions : IOtlpExporterOptions { + internal const string EndpointEnvVarName = "OTEL_EXPORTER_OTLP_ENDPOINT"; + internal const string ProtocolEnvVarName = "OTEL_EXPORTER_OTLP_PROTOCOL"; + internal const string HeadersEnvVarName = "OTEL_EXPORTER_OTLP_HEADERS"; + internal const string TimeoutEnvVarName = "OTEL_EXPORTER_OTLP_TIMEOUT"; + internal static readonly KeyValuePair[] StandardHeaders = new KeyValuePair[] { new KeyValuePair("User-Agent", GetUserAgentString()), }; + internal readonly Func DefaultHttpClientFactory; + internal bool ProgrammaticallyModifiedEndpoint; + private const string UserAgentProduct = "OTel-OTLP-Exporter-Dotnet"; + private const string DefaultGrpcEndpoint = "http://localhost:4317"; + private const string DefaultHttpEndpoint = "http://localhost:4318"; + private const OtlpExportProtocol DefaultOtlpExportProtocol = OtlpExportProtocol.Grpc; + + private OtlpExportProtocol? protocol; + private Uri? endpoint; + private int? timeoutMilliseconds; + private Func? httpClientFactory; /// /// Initializes a new instance of the class. /// public OtlpExporterOptions() - : this(new ConfigurationBuilder().AddEnvironmentVariables().Build(), new()) + : this( + configuration: new ConfigurationBuilder().AddEnvironmentVariables().Build(), + signal: OtlpExporterSignals.None, + defaultBatchOptions: new()) { } internal OtlpExporterOptions( IConfiguration configuration, + OtlpExporterSignals signal, BatchExportActivityProcessorOptions defaultBatchOptions) - : base(configuration, OtlpExporterSignals.None) { + Debug.Assert(configuration != null, "configuration was null"); Debug.Assert(defaultBatchOptions != null, "defaultBatchOptions was null"); + if (signal == OtlpExporterSignals.None) + { + this.ApplyConfigurationUsingSpecificationEnvVars( + configuration!, + EndpointEnvVarName, + ProtocolEnvVarName, + HeadersEnvVarName, + TimeoutEnvVarName); + } + + if (signal.HasFlag(OtlpExporterSignals.Logs)) + { + this.ApplyConfigurationUsingSpecificationEnvVars( + configuration!, + "OTEL_EXPORTER_OTLP_LOGS_ENDPOINT", + "OTEL_EXPORTER_OTLP_LOGS_PROTOCOL", + "OTEL_EXPORTER_OTLP_LOGS_HEADERS", + "OTEL_EXPORTER_OTLP_LOGS_TIMEOUT"); + } + + if (signal.HasFlag(OtlpExporterSignals.Metrics)) + { + this.ApplyConfigurationUsingSpecificationEnvVars( + configuration!, + "OTEL_EXPORTER_OTLP_METRICS_ENDPOINT", + "OTEL_EXPORTER_OTLP_METRICS_PROTOCOL", + "OTEL_EXPORTER_OTLP_METRICS_HEADERS", + "OTEL_EXPORTER_OTLP_METRICS_TIMEOUT"); + } + + if (signal.HasFlag(OtlpExporterSignals.Traces)) + { + this.ApplyConfigurationUsingSpecificationEnvVars( + configuration!, + "OTEL_EXPORTER_OTLP_TRACES_ENDPOINT", + "OTEL_EXPORTER_OTLP_TRACES_PROTOCOL", + "OTEL_EXPORTER_OTLP_TRACES_HEADERS", + "OTEL_EXPORTER_OTLP_TRACES_TIMEOUT"); + } + + this.DefaultHttpClientFactory = () => + { + return new HttpClient + { + Timeout = TimeSpan.FromMilliseconds(this.TimeoutMilliseconds), + }; + }; + this.BatchExportProcessorOptions = defaultBatchOptions!; } + /// + public OtlpExportProtocol Protocol + { + get => this.protocol ?? DefaultOtlpExportProtocol; + set => this.protocol = value; + } + + /// + public Uri Endpoint + { + get + { + if (this.endpoint == null) + { + this.endpoint = this.Protocol == OtlpExportProtocol.Grpc + ? new Uri(DefaultGrpcEndpoint) + : new Uri(DefaultHttpEndpoint); + } + + return this.endpoint; + } + + set + { + this.endpoint = value; + this.ProgrammaticallyModifiedEndpoint = true; + } + } + + /// + public string? Headers { get; set; } + + /// + public int TimeoutMilliseconds + { + get => this.timeoutMilliseconds ?? 10000; + set => this.timeoutMilliseconds = value; + } + + /// + public Func HttpClientFactory + { + get => this.httpClientFactory ??= this.DefaultHttpClientFactory; + set + { + this.httpClientFactory = value ?? NullHttpClientFactory; + + static HttpClient NullHttpClientFactory() + { + return null!; + } + } + } + /// /// Gets or sets the export processor type to be used with the OpenTelemetry Protocol Exporter. The default value is . /// @@ -71,8 +196,28 @@ internal static OtlpExporterOptions CreateOtlpExporterOptions( string name) => new( configuration, + OtlpExporterSignals.None, serviceProvider.GetRequiredService>().Get(name)); + internal OtlpExporterOptions ApplyDefaults(OtlpExporterOptions defaultExporterOptions) + { + this.protocol ??= defaultExporterOptions.protocol; + + this.endpoint ??= defaultExporterOptions.endpoint; + + // Note: We don't set ProgrammaticallyModifiedEndpoint because we + // want to append the signal if the endpoint came from the default + // endpoint. + + this.Headers ??= defaultExporterOptions.Headers; + + this.timeoutMilliseconds ??= defaultExporterOptions.timeoutMilliseconds; + + this.httpClientFactory ??= defaultExporterOptions.httpClientFactory; + + return this; + } + private static string GetUserAgentString() { try @@ -86,4 +231,35 @@ private static string GetUserAgentString() return UserAgentProduct; } } + + private void ApplyConfigurationUsingSpecificationEnvVars( + IConfiguration configuration, + string endpointEnvVarKey, + string protocolEnvVarKey, + string headersEnvVarKey, + string timeoutEnvVarKey) + { + if (configuration.TryGetUriValue(endpointEnvVarKey, out var endpoint)) + { + this.endpoint = endpoint; + } + + if (configuration.TryGetValue( + protocolEnvVarKey, + OtlpExportProtocolParser.TryParse, + out var protocol)) + { + this.Protocol = protocol; + } + + if (configuration.TryGetStringValue(headersEnvVarKey, out var headers)) + { + this.Headers = headers; + } + + if (configuration.TryGetIntValue(timeoutEnvVarKey, out var timeout)) + { + this.TimeoutMilliseconds = timeout; + } + } } diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterOptionsBase.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterOptionsBase.cs deleted file mode 100644 index ca301483978..00000000000 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterOptionsBase.cs +++ /dev/null @@ -1,241 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -#nullable enable - -using System.Diagnostics; -#if NETFRAMEWORK -using System.Net.Http; -#endif -using Microsoft.Extensions.Configuration; -using OpenTelemetry.Internal; -using OpenTelemetry.Metrics; -using OpenTelemetry.Trace; - -namespace OpenTelemetry.Exporter; - -/// -/// Base class for OpenTelemetry Protocol (OTLP) exporter options shared by all -/// signals. -/// -public class OtlpExporterOptionsBase -{ - internal const string EndpointEnvVarName = "OTEL_EXPORTER_OTLP_ENDPOINT"; - internal const string ProtocolEnvVarName = "OTEL_EXPORTER_OTLP_PROTOCOL"; - internal const string HeadersEnvVarName = "OTEL_EXPORTER_OTLP_HEADERS"; - internal const string TimeoutEnvVarName = "OTEL_EXPORTER_OTLP_TIMEOUT"; - - internal readonly Func DefaultHttpClientFactory; - internal bool ProgrammaticallyModifiedEndpoint; - - private const string DefaultGrpcEndpoint = "http://localhost:4317"; - private const string DefaultHttpEndpoint = "http://localhost:4318"; - private const OtlpExportProtocol DefaultOtlpExportProtocol = OtlpExportProtocol.Grpc; - - private OtlpExportProtocol? protocol; - private Uri? endpoint; - private int? timeoutMilliseconds; - private Func? httpClientFactory; - - internal OtlpExporterOptionsBase( - IConfiguration configuration, - OtlpExporterSignals signal) - { - Debug.Assert(configuration != null, "configuration was null"); - - if (signal == OtlpExporterSignals.None) - { - this.ApplyConfigurationUsingSpecificationEnvVars( - configuration!, - EndpointEnvVarName, - ProtocolEnvVarName, - HeadersEnvVarName, - TimeoutEnvVarName); - } - - if (signal.HasFlag(OtlpExporterSignals.Logs)) - { - this.ApplyConfigurationUsingSpecificationEnvVars( - configuration!, - "OTEL_EXPORTER_OTLP_LOGS_ENDPOINT", - "OTEL_EXPORTER_OTLP_LOGS_PROTOCOL", - "OTEL_EXPORTER_OTLP_LOGS_HEADERS", - "OTEL_EXPORTER_OTLP_LOGS_TIMEOUT"); - } - - if (signal.HasFlag(OtlpExporterSignals.Metrics)) - { - this.ApplyConfigurationUsingSpecificationEnvVars( - configuration!, - "OTEL_EXPORTER_OTLP_METRICS_ENDPOINT", - "OTEL_EXPORTER_OTLP_METRICS_PROTOCOL", - "OTEL_EXPORTER_OTLP_METRICS_HEADERS", - "OTEL_EXPORTER_OTLP_METRICS_TIMEOUT"); - } - - if (signal.HasFlag(OtlpExporterSignals.Traces)) - { - this.ApplyConfigurationUsingSpecificationEnvVars( - configuration!, - "OTEL_EXPORTER_OTLP_TRACES_ENDPOINT", - "OTEL_EXPORTER_OTLP_TRACES_PROTOCOL", - "OTEL_EXPORTER_OTLP_TRACES_HEADERS", - "OTEL_EXPORTER_OTLP_TRACES_TIMEOUT"); - } - - this.DefaultHttpClientFactory = () => - { - return new HttpClient - { - Timeout = TimeSpan.FromMilliseconds(this.TimeoutMilliseconds), - }; - }; - } - - /// - /// Gets or sets the the OTLP transport protocol. Supported values: Grpc and HttpProtobuf. - /// - public OtlpExportProtocol Protocol - { - get => this.protocol ?? DefaultOtlpExportProtocol; - set => this.protocol = value; - } - - /// - /// Gets or sets the target to which the exporter is going to send telemetry. - /// Must be a valid Uri with scheme (http or https) and host, and - /// may contain a port and path. The default value is - /// * http://localhost:4317 for - /// * http://localhost:4318 for . - /// - public Uri Endpoint - { - get - { - if (this.endpoint == null) - { - this.endpoint = this.Protocol == OtlpExportProtocol.Grpc - ? new Uri(DefaultGrpcEndpoint) - : new Uri(DefaultHttpEndpoint); - } - - return this.endpoint; - } - - set - { - this.endpoint = value; - this.ProgrammaticallyModifiedEndpoint = true; - } - } - - /// - /// Gets or sets optional headers for the connection. Refer to the - /// specification for information on the expected format for Headers. - /// - public string? Headers { get; set; } - - /// - /// Gets or sets the max waiting time (in milliseconds) for the backend to process each batch. The default value is 10000. - /// - public int TimeoutMilliseconds - { - get => this.timeoutMilliseconds ?? 10000; - set => this.timeoutMilliseconds = value; - } - - /// - /// Gets or sets the factory function called to create the instance that will be used at runtime to - /// transmit telemetry over HTTP. The returned instance will be reused - /// for all export invocations. - /// - /// - /// Notes: - /// - /// This is only invoked for the protocol. - /// The default behavior when using the extension is if an IHttpClientFactory - /// instance can be resolved through the application then an will be - /// created through the factory with the name "OtlpTraceExporter" - /// otherwise an will be instantiated - /// directly. - /// The default behavior when using the extension is if an IHttpClientFactory - /// instance can be resolved through the application then an will be - /// created through the factory with the name "OtlpMetricExporter" - /// otherwise an will be instantiated - /// directly. - /// - /// - public Func HttpClientFactory - { - get => this.httpClientFactory ??= this.DefaultHttpClientFactory; - set - { - this.httpClientFactory = value ?? NullHttpClientFactory; - - static HttpClient NullHttpClientFactory() - { - return null!; - } - } - } - - internal OtlpExporterOptionsBase ApplyDefaults(OtlpExporterOptionsBase defaultInstance) - { - this.protocol ??= defaultInstance.protocol; - - this.endpoint ??= defaultInstance.endpoint; - - // Note: We don't set ProgrammaticallyModifiedEndpoint because we - // want to append the signal if the endpoint came from the default - // endpoint. - - this.Headers ??= defaultInstance.Headers; - - this.timeoutMilliseconds ??= defaultInstance.timeoutMilliseconds; - - this.httpClientFactory ??= defaultInstance.httpClientFactory; - - return this; - } - - private void ApplyConfigurationUsingSpecificationEnvVars( - IConfiguration configuration, - string endpointEnvVarKey, - string protocolEnvVarKey, - string headersEnvVarKey, - string timeoutEnvVarKey) - { - if (configuration.TryGetUriValue(endpointEnvVarKey, out var endpoint)) - { - this.endpoint = endpoint; - } - - if (configuration.TryGetValue( - protocolEnvVarKey, - OtlpExportProtocolParser.TryParse, - out var protocol)) - { - this.Protocol = protocol; - } - - if (configuration.TryGetStringValue(headersEnvVarKey, out var headers)) - { - this.Headers = headers; - } - - if (configuration.TryGetIntValue(timeoutEnvVarKey, out var timeout)) - { - this.TimeoutMilliseconds = timeout; - } - } -} diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterOptionsExtensions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterOptionsExtensions.cs index ba691f678c2..44133af1f84 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterOptionsExtensions.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterOptionsExtensions.cs @@ -20,9 +20,9 @@ namespace OpenTelemetry.Exporter; internal static class OtlpExporterOptionsExtensions { #if NETSTANDARD2_1 || NET6_0_OR_GREATER - public static GrpcChannel CreateChannel(this OtlpExporterOptionsBase options) + public static GrpcChannel CreateChannel(this OtlpExporterOptions options) #else - public static Channel CreateChannel(this OtlpExporterOptionsBase options) + public static Channel CreateChannel(this OtlpExporterOptions options) #endif { if (options.Endpoint.Scheme != Uri.UriSchemeHttp && options.Endpoint.Scheme != Uri.UriSchemeHttps) @@ -47,12 +47,12 @@ public static Channel CreateChannel(this OtlpExporterOptionsBase options) #endif } - public static Metadata GetMetadataFromHeaders(this OtlpExporterOptionsBase options) + public static Metadata GetMetadataFromHeaders(this OtlpExporterOptions options) { return options.GetHeaders((m, k, v) => m.Add(k, v)); } - public static THeaders GetHeaders(this OtlpExporterOptionsBase options, Action addHeader) + public static THeaders GetHeaders(this OtlpExporterOptions options, Action addHeader) where THeaders : new() { var optionHeaders = options.Headers; @@ -88,16 +88,16 @@ public static THeaders GetHeaders(this OtlpExporterOptionsBase options return headers; } - public static OtlpExporterTransmissionHandler GetTraceExportTransmissionHandler(this OtlpExporterOptionsBase options) + public static OtlpExporterTransmissionHandler GetTraceExportTransmissionHandler(this OtlpExporterOptions options) => new(GetTraceExportClient(options)); - public static OtlpExporterTransmissionHandler GetMetricsExportTransmissionHandler(this OtlpExporterOptionsBase options) + public static OtlpExporterTransmissionHandler GetMetricsExportTransmissionHandler(this OtlpExporterOptions options) => new(GetMetricsExportClient(options)); - public static OtlpExporterTransmissionHandler GetLogsExportTransmissionHandler(this OtlpExporterOptionsBase options) + public static OtlpExporterTransmissionHandler GetLogsExportTransmissionHandler(this OtlpExporterOptions options) => new(GetLogExportClient(options)); - public static IExportClient GetTraceExportClient(this OtlpExporterOptionsBase options) => + public static IExportClient GetTraceExportClient(this OtlpExporterOptions options) => options.Protocol switch { OtlpExportProtocol.Grpc => new OtlpGrpcTraceExportClient(options), @@ -107,7 +107,7 @@ public static THeaders GetHeaders(this OtlpExporterOptionsBase options _ => throw new NotSupportedException($"Protocol {options.Protocol} is not supported."), }; - public static IExportClient GetMetricsExportClient(this OtlpExporterOptionsBase options) => + public static IExportClient GetMetricsExportClient(this OtlpExporterOptions options) => options.Protocol switch { OtlpExportProtocol.Grpc => new OtlpGrpcMetricsExportClient(options), @@ -117,7 +117,7 @@ public static THeaders GetHeaders(this OtlpExporterOptionsBase options _ => throw new NotSupportedException($"Protocol {options.Protocol} is not supported."), }; - public static IExportClient GetLogExportClient(this OtlpExporterOptionsBase options) => + public static IExportClient GetLogExportClient(this OtlpExporterOptions options) => options.Protocol switch { OtlpExportProtocol.Grpc => new OtlpGrpcLogExportClient(options), @@ -127,7 +127,7 @@ public static THeaders GetHeaders(this OtlpExporterOptionsBase options _ => throw new NotSupportedException($"Protocol {options.Protocol} is not supported."), }; - public static void TryEnableIHttpClientFactoryIntegration(this OtlpExporterOptionsBase options, IServiceProvider serviceProvider, string httpClientName) + public static void TryEnableIHttpClientFactoryIntegration(this OtlpExporterOptions options, IServiceProvider serviceProvider, string httpClientName) { if (serviceProvider != null && options.Protocol == OtlpExportProtocol.HttpProtobuf diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpLogExporter.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpLogExporter.cs index b0abc53241c..8e5c626d917 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpLogExporter.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpLogExporter.cs @@ -28,7 +28,7 @@ public sealed class OtlpLogExporter : BaseExporter /// Initializes a new instance of the class. /// /// Configuration options for the exporter. - public OtlpLogExporter(OtlpExporterOptionsBase options) + public OtlpLogExporter(OtlpExporterOptions options) : this(options, sdkLimitOptions: new(), experimentalOptions: new(), transmissionHandler: null) { } @@ -41,7 +41,7 @@ public OtlpLogExporter(OtlpExporterOptionsBase options) /// . /// . internal OtlpLogExporter( - OtlpExporterOptionsBase exporterOptions, + OtlpExporterOptions exporterOptions, SdkLimitOptions sdkLimitOptions, ExperimentalOptions experimentalOptions, OtlpExporterTransmissionHandler? transmissionHandler = null) diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpLogExporterHelperExtensions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpLogExporterHelperExtensions.cs index 2e77050bb9a..69e0a5cb939 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpLogExporterHelperExtensions.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpLogExporterHelperExtensions.cs @@ -343,7 +343,7 @@ static LoggerProviderBuilder AddOtlpExporter( internal static BaseProcessor BuildOtlpLogExporter( IServiceProvider sp, - OtlpExporterOptionsBase exporterOptions, + OtlpExporterOptions exporterOptions, LogRecordExportProcessorOptions processorOptions, SdkLimitOptions sdkLimitOptions, ExperimentalOptions experimentalOptions, diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpMetricExporter.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpMetricExporter.cs index 7873a13fd1b..a0026d1e9f4 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpMetricExporter.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpMetricExporter.cs @@ -24,7 +24,7 @@ public class OtlpMetricExporter : BaseExporter /// Initializes a new instance of the class. /// /// Configuration options for the exporter. - public OtlpMetricExporter(OtlpExporterOptionsBase options) + public OtlpMetricExporter(OtlpExporterOptions options) : this(options, transmissionHandler: null) { } @@ -35,7 +35,7 @@ public OtlpMetricExporter(OtlpExporterOptionsBase options) /// Configuration options for the export. /// . internal OtlpMetricExporter( - OtlpExporterOptionsBase options, + OtlpExporterOptions options, OtlpExporterTransmissionHandler transmissionHandler = null) { // Each of the Otlp exporters: Traces, Metrics, and Logs set the same value for `OtlpKeyValueTransformer.LogUnsupportedAttributeType` diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpMetricExporterExtensions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpMetricExporterExtensions.cs index 67bfd435f8d..344ad3e8647 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpMetricExporterExtensions.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpMetricExporterExtensions.cs @@ -176,7 +176,7 @@ public static MeterProviderBuilder AddOtlpExporter( } internal static MetricReader BuildOtlpExporterMetricReader( - OtlpExporterOptionsBase exporterOptions, + OtlpExporterOptions exporterOptions, MetricReaderOptions metricReaderOptions, IServiceProvider serviceProvider, Func, BaseExporter>? configureExporterInstance = null) diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpTraceExporter.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpTraceExporter.cs index dd9d500a0bc..48788f20c13 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpTraceExporter.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpTraceExporter.cs @@ -25,7 +25,7 @@ public class OtlpTraceExporter : BaseExporter /// Initializes a new instance of the class. /// /// Configuration options for the export. - public OtlpTraceExporter(OtlpExporterOptionsBase options) + public OtlpTraceExporter(OtlpExporterOptions options) : this(options, sdkLimitOptions: new(), transmissionHandler: null) { } @@ -37,7 +37,7 @@ public OtlpTraceExporter(OtlpExporterOptionsBase options) /// . /// . internal OtlpTraceExporter( - OtlpExporterOptionsBase exporterOptions, + OtlpExporterOptions exporterOptions, SdkLimitOptions sdkLimitOptions, OtlpExporterTransmissionHandler transmissionHandler = null) { diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpTraceExporterHelperExtensions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpTraceExporterHelperExtensions.cs index 99b4685297a..aba9cb06df5 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpTraceExporterHelperExtensions.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpTraceExporterHelperExtensions.cs @@ -110,7 +110,7 @@ internal static BaseProcessor BuildOtlpExporterProcessor( configureExporterInstance); internal static BaseProcessor BuildOtlpExporterProcessor( - OtlpExporterOptionsBase exporterOptions, + OtlpExporterOptions exporterOptions, SdkLimitOptions sdkLimitOptions, ExportProcessorType exportProcessorType, BatchExportProcessorOptions batchExportProcessorOptions, From b3d9db498366cec4fa236792f07219a4e64f69e5 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Thu, 7 Mar 2024 11:33:17 -0800 Subject: [PATCH 09/48] Reduce changes shown on diff. --- .../OtlpExporterOptions.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterOptions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterOptions.cs index 24ebc0cacf2..6f4476eeaf9 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterOptions.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterOptions.cs @@ -27,9 +27,9 @@ namespace OpenTelemetry.Exporter; public class OtlpExporterOptions : IOtlpExporterOptions { internal const string EndpointEnvVarName = "OTEL_EXPORTER_OTLP_ENDPOINT"; - internal const string ProtocolEnvVarName = "OTEL_EXPORTER_OTLP_PROTOCOL"; internal const string HeadersEnvVarName = "OTEL_EXPORTER_OTLP_HEADERS"; internal const string TimeoutEnvVarName = "OTEL_EXPORTER_OTLP_TIMEOUT"; + internal const string ProtocolEnvVarName = "OTEL_EXPORTER_OTLP_PROTOCOL"; internal static readonly KeyValuePair[] StandardHeaders = new KeyValuePair[] { @@ -39,10 +39,10 @@ public class OtlpExporterOptions : IOtlpExporterOptions internal readonly Func DefaultHttpClientFactory; internal bool ProgrammaticallyModifiedEndpoint; - private const string UserAgentProduct = "OTel-OTLP-Exporter-Dotnet"; private const string DefaultGrpcEndpoint = "http://localhost:4317"; private const string DefaultHttpEndpoint = "http://localhost:4318"; private const OtlpExportProtocol DefaultOtlpExportProtocol = OtlpExportProtocol.Grpc; + private const string UserAgentProduct = "OTel-OTLP-Exporter-Dotnet"; private OtlpExportProtocol? protocol; private Uri? endpoint; From 75111d204d931250337a4efa9d0d2d90bbe55295 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Thu, 7 Mar 2024 12:26:35 -0800 Subject: [PATCH 10/48] Tweaks. --- .../ExportClient/BaseOtlpHttpExportClient.cs | 2 +- .../OtlpExporterSpecEnvVarKeyDefinitions.cs | 27 +++++++++ .../OtlpExporterOptions.cs | 56 +++++++++++-------- .../OtlpExporterOptionsTests.cs | 2 +- ...eriodicExportingMetricReaderHelperTests.cs | 6 +- 5 files changed, 65 insertions(+), 28 deletions(-) create mode 100644 src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/OtlpExporterSpecEnvVarKeyDefinitions.cs diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/BaseOtlpHttpExportClient.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/BaseOtlpHttpExportClient.cs index 4aad820b1e2..213316cf8ce 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/BaseOtlpHttpExportClient.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/BaseOtlpHttpExportClient.cs @@ -21,7 +21,7 @@ protected BaseOtlpHttpExportClient(OtlpExporterOptions options, HttpClient httpC Guard.ThrowIfNull(signalPath); Guard.ThrowIfInvalidTimeout(options.TimeoutMilliseconds); - Uri exporterEndpoint = !options.ProgrammaticallyModifiedEndpoint + Uri exporterEndpoint = options.AppendSignalPathToEndpoint ? options.Endpoint.AppendPathIfNotPresent(signalPath) : options.Endpoint; this.Endpoint = new UriBuilder(exporterEndpoint).Uri; diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/OtlpExporterSpecEnvVarKeyDefinitions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/OtlpExporterSpecEnvVarKeyDefinitions.cs new file mode 100644 index 00000000000..98e365c290a --- /dev/null +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/OtlpExporterSpecEnvVarKeyDefinitions.cs @@ -0,0 +1,27 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +namespace OpenTelemetry.Exporter; + +internal static class OtlpExporterSpecEnvVarKeyDefinitions +{ + public const string DefaultEndpointEnvVarName = "OTEL_EXPORTER_OTLP_ENDPOINT"; + public const string DefaultHeadersEnvVarName = "OTEL_EXPORTER_OTLP_HEADERS"; + public const string DefaultTimeoutEnvVarName = "OTEL_EXPORTER_OTLP_TIMEOUT"; + public const string DefaultProtocolEnvVarName = "OTEL_EXPORTER_OTLP_PROTOCOL"; + + public const string LogsEndpointEnvVarName = "OTEL_EXPORTER_OTLP_LOGS_ENDPOINT"; + public const string LogsHeadersEnvVarName = "OTEL_EXPORTER_OTLP_LOGS_HEADERS"; + public const string LogsTimeoutEnvVarName = "OTEL_EXPORTER_OTLP_LOGS_TIMEOUT"; + public const string LogsProtocolEnvVarName = "OTEL_EXPORTER_OTLP_LOGS_PROTOCOL"; + + public const string MetricsEndpointEnvVarName = "OTEL_EXPORTER_OTLP_METRICS_ENDPOINT"; + public const string MetricsHeadersEnvVarName = "OTEL_EXPORTER_OTLP_METRICS_HEADERS"; + public const string MetricsTimeoutEnvVarName = "OTEL_EXPORTER_OTLP_METRICS_TIMEOUT"; + public const string MetricsProtocolEnvVarName = "OTEL_EXPORTER_OTLP_METRICS_PROTOCOL"; + + public const string TracesEndpointEnvVarName = "OTEL_EXPORTER_OTLP_TRACES_ENDPOINT"; + public const string TracesHeadersEnvVarName = "OTEL_EXPORTER_OTLP_TRACES_HEADERS"; + public const string TracesTimeoutEnvVarName = "OTEL_EXPORTER_OTLP_TRACES_TIMEOUT"; + public const string TracesProtocolEnvVarName = "OTEL_EXPORTER_OTLP_TRACES_PROTOCOL"; +} diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterOptions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterOptions.cs index 6f4476eeaf9..2c018ad7ad9 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterOptions.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterOptions.cs @@ -26,10 +26,11 @@ namespace OpenTelemetry.Exporter; /// public class OtlpExporterOptions : IOtlpExporterOptions { - internal const string EndpointEnvVarName = "OTEL_EXPORTER_OTLP_ENDPOINT"; - internal const string HeadersEnvVarName = "OTEL_EXPORTER_OTLP_HEADERS"; - internal const string TimeoutEnvVarName = "OTEL_EXPORTER_OTLP_TIMEOUT"; - internal const string ProtocolEnvVarName = "OTEL_EXPORTER_OTLP_PROTOCOL"; + // TODO: Remove these and use OtlpExporterSpecEnvVarKeyDefinitions directly from tests in a follow-up PR + internal const string EndpointEnvVarName = OtlpExporterSpecEnvVarKeyDefinitions.DefaultEndpointEnvVarName; + internal const string HeadersEnvVarName = OtlpExporterSpecEnvVarKeyDefinitions.DefaultHeadersEnvVarName; + internal const string TimeoutEnvVarName = OtlpExporterSpecEnvVarKeyDefinitions.DefaultTimeoutEnvVarName; + internal const string ProtocolEnvVarName = OtlpExporterSpecEnvVarKeyDefinitions.DefaultProtocolEnvVarName; internal static readonly KeyValuePair[] StandardHeaders = new KeyValuePair[] { @@ -37,7 +38,7 @@ public class OtlpExporterOptions : IOtlpExporterOptions }; internal readonly Func DefaultHttpClientFactory; - internal bool ProgrammaticallyModifiedEndpoint; + internal bool AppendSignalPathToEndpoint = true; private const string DefaultGrpcEndpoint = "http://localhost:4317"; private const string DefaultHttpEndpoint = "http://localhost:4318"; @@ -72,40 +73,44 @@ internal OtlpExporterOptions( { this.ApplyConfigurationUsingSpecificationEnvVars( configuration!, - EndpointEnvVarName, - ProtocolEnvVarName, - HeadersEnvVarName, - TimeoutEnvVarName); + OtlpExporterSpecEnvVarKeyDefinitions.DefaultEndpointEnvVarName, + appendSignalPathToEndpoint: true, + OtlpExporterSpecEnvVarKeyDefinitions.DefaultProtocolEnvVarName, + OtlpExporterSpecEnvVarKeyDefinitions.DefaultHeadersEnvVarName, + OtlpExporterSpecEnvVarKeyDefinitions.DefaultTimeoutEnvVarName); } if (signal.HasFlag(OtlpExporterSignals.Logs)) { this.ApplyConfigurationUsingSpecificationEnvVars( configuration!, - "OTEL_EXPORTER_OTLP_LOGS_ENDPOINT", - "OTEL_EXPORTER_OTLP_LOGS_PROTOCOL", - "OTEL_EXPORTER_OTLP_LOGS_HEADERS", - "OTEL_EXPORTER_OTLP_LOGS_TIMEOUT"); + OtlpExporterSpecEnvVarKeyDefinitions.LogsEndpointEnvVarName, + appendSignalPathToEndpoint: false, + OtlpExporterSpecEnvVarKeyDefinitions.LogsProtocolEnvVarName, + OtlpExporterSpecEnvVarKeyDefinitions.LogsHeadersEnvVarName, + OtlpExporterSpecEnvVarKeyDefinitions.LogsTimeoutEnvVarName); } if (signal.HasFlag(OtlpExporterSignals.Metrics)) { this.ApplyConfigurationUsingSpecificationEnvVars( configuration!, - "OTEL_EXPORTER_OTLP_METRICS_ENDPOINT", - "OTEL_EXPORTER_OTLP_METRICS_PROTOCOL", - "OTEL_EXPORTER_OTLP_METRICS_HEADERS", - "OTEL_EXPORTER_OTLP_METRICS_TIMEOUT"); + OtlpExporterSpecEnvVarKeyDefinitions.MetricsEndpointEnvVarName, + appendSignalPathToEndpoint: false, + OtlpExporterSpecEnvVarKeyDefinitions.MetricsProtocolEnvVarName, + OtlpExporterSpecEnvVarKeyDefinitions.MetricsHeadersEnvVarName, + OtlpExporterSpecEnvVarKeyDefinitions.MetricsTimeoutEnvVarName); } if (signal.HasFlag(OtlpExporterSignals.Traces)) { this.ApplyConfigurationUsingSpecificationEnvVars( configuration!, - "OTEL_EXPORTER_OTLP_TRACES_ENDPOINT", - "OTEL_EXPORTER_OTLP_TRACES_PROTOCOL", - "OTEL_EXPORTER_OTLP_TRACES_HEADERS", - "OTEL_EXPORTER_OTLP_TRACES_TIMEOUT"); + OtlpExporterSpecEnvVarKeyDefinitions.TracesEndpointEnvVarName, + appendSignalPathToEndpoint: false, + OtlpExporterSpecEnvVarKeyDefinitions.TracesProtocolEnvVarName, + OtlpExporterSpecEnvVarKeyDefinitions.TracesHeadersEnvVarName, + OtlpExporterSpecEnvVarKeyDefinitions.TracesTimeoutEnvVarName); } this.DefaultHttpClientFactory = () => @@ -144,7 +149,7 @@ public Uri Endpoint set { this.endpoint = value; - this.ProgrammaticallyModifiedEndpoint = true; + this.AppendSignalPathToEndpoint = false; } } @@ -205,7 +210,7 @@ internal OtlpExporterOptions ApplyDefaults(OtlpExporterOptions defaultExporterOp this.endpoint ??= defaultExporterOptions.endpoint; - // Note: We don't set ProgrammaticallyModifiedEndpoint because we + // Note: We leave AppendSignalPathToEndpoint set to true here because we // want to append the signal if the endpoint came from the default // endpoint. @@ -235,6 +240,7 @@ private static string GetUserAgentString() private void ApplyConfigurationUsingSpecificationEnvVars( IConfiguration configuration, string endpointEnvVarKey, + bool appendSignalPathToEndpoint, string protocolEnvVarKey, string headersEnvVarKey, string timeoutEnvVarKey) @@ -242,6 +248,10 @@ private void ApplyConfigurationUsingSpecificationEnvVars( if (configuration.TryGetUriValue(endpointEnvVarKey, out var endpoint)) { this.endpoint = endpoint; + if (!appendSignalPathToEndpoint) + { + this.AppendSignalPathToEndpoint = false; + } } if (configuration.TryGetValue( diff --git a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpExporterOptionsTests.cs b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpExporterOptionsTests.cs index 619dd7999e9..35c7072c275 100644 --- a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpExporterOptionsTests.cs +++ b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpExporterOptionsTests.cs @@ -74,7 +74,7 @@ public void OtlpExporterOptions_UsingIConfiguration() .AddInMemoryCollection(values) .Build(); - var options = new OtlpExporterOptions(configuration, new()); + var options = new OtlpExporterOptions(configuration, OtlpExporterSignals.None, new()); Assert.Equal(new Uri("http://test:8888"), options.Endpoint); Assert.Equal("A=2,B=3", options.Headers); diff --git a/test/OpenTelemetry.Tests/Internal/PeriodicExportingMetricReaderHelperTests.cs b/test/OpenTelemetry.Tests/Internal/PeriodicExportingMetricReaderHelperTests.cs index 4b8847a17c3..d0da994e018 100644 --- a/test/OpenTelemetry.Tests/Internal/PeriodicExportingMetricReaderHelperTests.cs +++ b/test/OpenTelemetry.Tests/Internal/PeriodicExportingMetricReaderHelperTests.cs @@ -109,10 +109,10 @@ public void CreatePeriodicExportingMetricReader_FromIConfiguration() .AddInMemoryCollection(values) .Build(); - var options = new MetricReaderOptions(configuration); + var options = new PeriodicExportingMetricReaderOptions(configuration); - Assert.Equal(18, options.PeriodicExportingMetricReaderOptions.ExportIntervalMilliseconds); - Assert.Equal(19, options.PeriodicExportingMetricReaderOptions.ExportTimeoutMilliseconds); + Assert.Equal(18, options.ExportIntervalMilliseconds); + Assert.Equal(19, options.ExportTimeoutMilliseconds); } [Fact] From c2db1009e350045b1765456c60aee948c3d572d9 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Thu, 7 Mar 2024 13:22:44 -0800 Subject: [PATCH 11/48] Code review. --- .../OpenTelemetry.Exporter.OpenTelemetryProtocol.csproj | 4 ---- src/OpenTelemetry/AssemblyInfo.cs | 2 +- src/OpenTelemetry/BaseProcessor.cs | 7 +------ src/OpenTelemetry/ProcessorPipelineWeight.cs | 7 +------ 4 files changed, 3 insertions(+), 17 deletions(-) diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OpenTelemetry.Exporter.OpenTelemetryProtocol.csproj b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OpenTelemetry.Exporter.OpenTelemetryProtocol.csproj index bed60120451..f4de3db506a 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OpenTelemetry.Exporter.OpenTelemetryProtocol.csproj +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OpenTelemetry.Exporter.OpenTelemetryProtocol.csproj @@ -35,14 +35,10 @@ NOT required because this project sees API + SDK internals --> - - - - diff --git a/src/OpenTelemetry/AssemblyInfo.cs b/src/OpenTelemetry/AssemblyInfo.cs index 90823131448..da02f32e095 100644 --- a/src/OpenTelemetry/AssemblyInfo.cs +++ b/src/OpenTelemetry/AssemblyInfo.cs @@ -5,6 +5,7 @@ [assembly: InternalsVisibleTo("OpenTelemetry.Tests" + AssemblyInfo.PublicKey)] [assembly: InternalsVisibleTo("OpenTelemetry.Exporter.InMemory" + AssemblyInfo.PublicKey)] +[assembly: InternalsVisibleTo("OpenTelemetry.Exporter.OpenTelemetryProtocol" + AssemblyInfo.PublicKey)] [assembly: InternalsVisibleTo("OpenTelemetry.Exporter.Prometheus.AspNetCore" + AssemblyInfo.PublicKey)] [assembly: InternalsVisibleTo("OpenTelemetry.Exporter.Prometheus.AspNetCore.Tests" + AssemblyInfo.PublicKey)] [assembly: InternalsVisibleTo("OpenTelemetry.Exporter.Prometheus.HttpListener.Tests" + AssemblyInfo.PublicKey)] @@ -14,7 +15,6 @@ #if !EXPOSE_EXPERIMENTAL_FEATURES [assembly: InternalsVisibleTo("OpenTelemetry.Exporter.Console" + AssemblyInfo.PublicKey)] -[assembly: InternalsVisibleTo("OpenTelemetry.Exporter.OpenTelemetryProtocol" + AssemblyInfo.PublicKey)] [assembly: InternalsVisibleTo("OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests" + AssemblyInfo.PublicKey)] [assembly: InternalsVisibleTo("OpenTelemetry.Tests.Stress.Metrics" + AssemblyInfo.PublicKey)] #endif diff --git a/src/OpenTelemetry/BaseProcessor.cs b/src/OpenTelemetry/BaseProcessor.cs index f0d8a189ff7..39734c91f5a 100644 --- a/src/OpenTelemetry/BaseProcessor.cs +++ b/src/OpenTelemetry/BaseProcessor.cs @@ -38,12 +38,7 @@ public BaseProcessor() /// processors. Changing the weight after a pipeline has been constructed /// has no effect. /// -#if EXPOSE_EXPERIMENTAL_FEATURES - public -#else - internal -#endif - ProcessorPipelineWeight PipelineWeight { get; set; } = ProcessorPipelineWeight.PipelineMiddle; + internal ProcessorPipelineWeight PipelineWeight { get; set; } = ProcessorPipelineWeight.PipelineMiddle; /// /// Called synchronously when a telemetry object is started. diff --git a/src/OpenTelemetry/ProcessorPipelineWeight.cs b/src/OpenTelemetry/ProcessorPipelineWeight.cs index bc343b27321..32a9122afe2 100644 --- a/src/OpenTelemetry/ProcessorPipelineWeight.cs +++ b/src/OpenTelemetry/ProcessorPipelineWeight.cs @@ -3,15 +3,10 @@ namespace OpenTelemetry; -#if EXPOSE_EXPERIMENTAL_FEATURES /// /// Contains definitions for common processor pipeline weights. /// -public -#else -internal -#endif - enum ProcessorPipelineWeight +internal enum ProcessorPipelineWeight { /// /// Pipeline start. Value: . From 24cbc81bf0129cdea15493238593f8ef2ece963c Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Mon, 11 Mar 2024 11:50:06 -0700 Subject: [PATCH 12/48] Merge fixes. --- ...enTelemetryBuilderOtlpExporterExtensions.cs | 15 --------------- .../Builder/OtlpExporterBuilder.cs | 9 --------- .../Builder/OtlpExporterBuilderOptions.cs | 10 ++++------ .../OtlpExporterSignals.cs | 18 ------------------ 4 files changed, 4 insertions(+), 48 deletions(-) delete mode 100644 src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterSignals.cs diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderOtlpExporterExtensions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderOtlpExporterExtensions.cs index 33679dc3c44..3321eac0d7b 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderOtlpExporterExtensions.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderOtlpExporterExtensions.cs @@ -152,11 +152,6 @@ private static void UseOtlpExporterInternal(IOpenTelemetryBuilder builder, strin { var builderOptions = GetBuilderOptionsAndValidateRegistrations(sp, name); - if (!builderOptions.Signals.HasFlag(OtlpExporterSignals.Logs)) - { - return; - } - var processor = OtlpLogExporterHelperExtensions.BuildOtlpLogExporter( sp, builderOptions.LoggingOptionsInstance.ApplyDefaults(builderOptions.DefaultOptionsInstance), @@ -174,11 +169,6 @@ private static void UseOtlpExporterInternal(IOpenTelemetryBuilder builder, strin { var builderOptions = GetBuilderOptionsAndValidateRegistrations(sp, name); - if (!builderOptions.Signals.HasFlag(OtlpExporterSignals.Metrics)) - { - return; - } - metrics.AddReader( OtlpMetricExporterExtensions.BuildOtlpExporterMetricReader( builderOptions.MetricsOptionsInstance.ApplyDefaults(builderOptions.DefaultOptionsInstance), @@ -191,11 +181,6 @@ private static void UseOtlpExporterInternal(IOpenTelemetryBuilder builder, strin { var builderOptions = GetBuilderOptionsAndValidateRegistrations(sp, name); - if (!builderOptions.Signals.HasFlag(OtlpExporterSignals.Traces)) - { - return; - } - var processorOptions = builderOptions.ActivityExportProcessorOptions ?? throw new NotSupportedException(); var processor = OtlpTraceExporterHelperExtensions.BuildOtlpExporterProcessor( diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OtlpExporterBuilder.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OtlpExporterBuilder.cs index 9d461560a7e..2c7435c1e47 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OtlpExporterBuilder.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OtlpExporterBuilder.cs @@ -37,14 +37,6 @@ internal OtlpExporterBuilder( public IServiceCollection Services { get; } - public OtlpExporterBuilder SetSignalsToConfigure(OtlpExporterSignals signalsToConfigure) - { - this.Services.Configure( - this.name, - o => o.Signals = signalsToConfigure); - return this; - } - public OtlpExporterBuilder ConfigureDefaultExporterOptions( Action configure) { @@ -124,7 +116,6 @@ private static void BindConfigurationToOptions(IServiceCollection services, stri /* Config JSON structure is expected to be something like this: { - "Signals": "Logs, Metrics", "DefaultOptions": { "Endpoint": "http://default_endpoint/" }, diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OtlpExporterBuilderOptions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OtlpExporterBuilderOptions.cs index 1ca167bd91f..f5dca04c657 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OtlpExporterBuilderOptions.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OtlpExporterBuilderOptions.cs @@ -45,17 +45,15 @@ public OtlpExporterBuilderOptions( var defaultBatchOptions = this.ActivityExportProcessorOptions!.BatchExportProcessorOptions; - this.DefaultOptionsInstance = new OtlpExporterOptions(configuration!, OtlpExporterSignals.None, defaultBatchOptions); + this.DefaultOptionsInstance = new OtlpExporterOptions(configuration!, OtlpExporterOptionsConfigurationType.Default, defaultBatchOptions); - this.LoggingOptionsInstance = new OtlpExporterOptions(configuration!, OtlpExporterSignals.Logs, defaultBatchOptions); + this.LoggingOptionsInstance = new OtlpExporterOptions(configuration!, OtlpExporterOptionsConfigurationType.Logs, defaultBatchOptions); - this.MetricsOptionsInstance = new OtlpExporterOptions(configuration!, OtlpExporterSignals.Metrics, defaultBatchOptions); + this.MetricsOptionsInstance = new OtlpExporterOptions(configuration!, OtlpExporterOptionsConfigurationType.Metrics, defaultBatchOptions); - this.TracingOptionsInstance = new OtlpExporterOptions(configuration!, OtlpExporterSignals.Traces, defaultBatchOptions); + this.TracingOptionsInstance = new OtlpExporterOptions(configuration!, OtlpExporterOptionsConfigurationType.Traces, defaultBatchOptions); } - public OtlpExporterSignals Signals { get; set; } = OtlpExporterSignals.All; - public IOtlpExporterOptions DefaultOptions => this.DefaultOptionsInstance; public IOtlpExporterOptions LoggingOptions => this.LoggingOptionsInstance; diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterSignals.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterSignals.cs deleted file mode 100644 index 190b7397a36..00000000000 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterSignals.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -#nullable enable - -namespace OpenTelemetry.Exporter; - -[Flags] -internal enum OtlpExporterSignals -{ -#pragma warning disable SA1602 // Enumeration items should be documented - All = Logs | Metrics | Traces, - None = 0b0, - Logs = 0b1, - Metrics = 0b10, - Traces = 0b100, -#pragma warning restore SA1602 // Enumeration items should be documented -} From 2e7811d696ce002a5b3ec06f61ae8d697b2be40d Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Mon, 11 Mar 2024 12:03:06 -0700 Subject: [PATCH 13/48] Tweaks and fixes. --- .../Builder/OpenTelemetryBuilderOtlpExporterExtensions.cs | 4 ++++ .../OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests.csproj | 1 + 2 files changed, 5 insertions(+) diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderOtlpExporterExtensions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderOtlpExporterExtensions.cs index 3321eac0d7b..e9d87f247ae 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderOtlpExporterExtensions.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderOtlpExporterExtensions.cs @@ -126,6 +126,7 @@ internal static IOpenTelemetryBuilder UseOtlpExporter( private static void UseOtlpExporterInternal(IOpenTelemetryBuilder builder, string? name, int processorPipelineWeight) { + // Note: We automatically turn on signals for "UseOtlpExporter" builder .WithLogging() .WithMetrics() @@ -133,6 +134,9 @@ private static void UseOtlpExporterInternal(IOpenTelemetryBuilder builder, strin var services = builder.Services; + // Note: UseOtlpExporterRegistration is added to the service collection + // to detect repeated calls to "UseOtlpExporter" and to throw if + // "AddOtlpExporter" extensions are called services.AddSingleton(); services.RegisterOptionsFactory(configuration => new SdkLimitOptions(configuration)); diff --git a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests.csproj b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests.csproj index ad60a2385a2..44802d3a5aa 100644 --- a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests.csproj +++ b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests.csproj @@ -25,6 +25,7 @@ + From 71c56e6a95050e52dfd204bd8ebf69b3468b3f35 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Mon, 11 Mar 2024 14:38:03 -0700 Subject: [PATCH 14/48] Revert order changes. --- .../OtlpExporterOptions.cs | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterOptions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterOptions.cs index 97d97613e20..28578531ab2 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterOptions.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterOptions.cs @@ -81,13 +81,6 @@ internal OtlpExporterOptions( this.BatchExportProcessorOptions = defaultBatchOptions!; } - /// - public OtlpExportProtocol Protocol - { - get => this.protocol ?? DefaultOtlpExportProtocol; - set => this.protocol = value; - } - /// public Uri Endpoint { @@ -123,15 +116,10 @@ public int TimeoutMilliseconds } /// - public Func HttpClientFactory + public OtlpExportProtocol Protocol { - get => this.httpClientFactory ??= this.DefaultHttpClientFactory; - set - { - Guard.ThrowIfNull(value); - - this.httpClientFactory = value; - } + get => this.protocol ?? DefaultOtlpExportProtocol; + set => this.protocol = value; } /// @@ -146,6 +134,18 @@ public Func HttpClientFactory /// Note: This only applies when exporting traces. public BatchExportProcessorOptions BatchExportProcessorOptions { get; set; } + /// + public Func HttpClientFactory + { + get => this.httpClientFactory ??= this.DefaultHttpClientFactory; + set + { + Guard.ThrowIfNull(value); + + this.httpClientFactory = value; + } + } + /// /// Gets a value indicating whether or not the signal-specific path should /// be appended to . From aa84a40ee0e1aa53f9537c270b89d063e6e32761 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Mon, 11 Mar 2024 14:39:34 -0700 Subject: [PATCH 15/48] Tweak. --- .../OtlpExporterOptions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterOptions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterOptions.cs index 28578531ab2..8193dbbcfee 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterOptions.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterOptions.cs @@ -137,7 +137,7 @@ public OtlpExportProtocol Protocol /// public Func HttpClientFactory { - get => this.httpClientFactory ??= this.DefaultHttpClientFactory; + get => this.httpClientFactory ?? this.DefaultHttpClientFactory; set { Guard.ThrowIfNull(value); From 4ba37bb5f9f960ef3fb5a8c33fad957612eed574 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Mon, 11 Mar 2024 14:45:01 -0700 Subject: [PATCH 16/48] Tweak. --- .../Builder/OpenTelemetryBuilderOtlpExporterExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderOtlpExporterExtensions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderOtlpExporterExtensions.cs index e9d87f247ae..0d9caa4f4a8 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderOtlpExporterExtensions.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderOtlpExporterExtensions.cs @@ -83,7 +83,7 @@ public static IOpenTelemetryBuilder UseOtlpExporter( }); } - internal static IOpenTelemetryBuilder AddOtlpExporter( + internal static IOpenTelemetryBuilder UseOtlpExporter( this IOpenTelemetryBuilder builder, Action configure) { From 7f3c3cb1e9fa644f56bfe338392fe3697f8f0527 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Mon, 11 Mar 2024 14:47:10 -0700 Subject: [PATCH 17/48] API files. --- .../.publicApi/Stable/PublicAPI.Unshipped.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/.publicApi/Stable/PublicAPI.Unshipped.txt b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/.publicApi/Stable/PublicAPI.Unshipped.txt index e69de29bb2d..5cde8170f36 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/.publicApi/Stable/PublicAPI.Unshipped.txt +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/.publicApi/Stable/PublicAPI.Unshipped.txt @@ -0,0 +1,4 @@ +OpenTelemetry.Exporter.OpenTelemetryBuilderOtlpExporterExtensions +static OpenTelemetry.Exporter.OpenTelemetryBuilderOtlpExporterExtensions.UseOtlpExporter(this OpenTelemetry.IOpenTelemetryBuilder! builder) -> OpenTelemetry.IOpenTelemetryBuilder! +static OpenTelemetry.Exporter.OpenTelemetryBuilderOtlpExporterExtensions.UseOtlpExporter(this OpenTelemetry.IOpenTelemetryBuilder! builder, OpenTelemetry.Exporter.OtlpExportProtocol protocol, System.Uri! baseEndpoint) -> OpenTelemetry.IOpenTelemetryBuilder! +static OpenTelemetry.Exporter.OpenTelemetryBuilderOtlpExporterExtensions.UseOtlpExporter(this OpenTelemetry.IOpenTelemetryBuilder! builder, System.Uri! baseEndpoint) -> OpenTelemetry.IOpenTelemetryBuilder! \ No newline at end of file From ded0687a4bf67e593ad545079225b6d52109e0a0 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Mon, 11 Mar 2024 15:26:42 -0700 Subject: [PATCH 18/48] Bug fix. --- .../Builder/OpenTelemetryBuilderOtlpExporterExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderOtlpExporterExtensions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderOtlpExporterExtensions.cs index 0d9caa4f4a8..03954486db3 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderOtlpExporterExtensions.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderOtlpExporterExtensions.cs @@ -110,7 +110,7 @@ internal static IOpenTelemetryBuilder UseOtlpExporter( { Guard.ThrowIfNull(builder); - if (configuration == null && string.IsNullOrEmpty(name)) + if (configuration != null && string.IsNullOrEmpty(name)) { name = "otlp"; } From bc272543c63dc75fe1134e7b2d8e1fe44188d429 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Mon, 11 Mar 2024 15:37:43 -0700 Subject: [PATCH 19/48] Make processor pipeline weight a constant. --- .../OpenTelemetryBuilderOtlpExporterExtensions.cs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderOtlpExporterExtensions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderOtlpExporterExtensions.cs index 03954486db3..ba4a3df6a3a 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderOtlpExporterExtensions.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderOtlpExporterExtensions.cs @@ -21,6 +21,8 @@ namespace OpenTelemetry.Exporter; /// public static class OpenTelemetryBuilderOtlpExporterExtensions { + private const int DefaultProcessorPipelineWeight = 10_000; + /// /// Uses OpenTelemetry Protocol (OTLP) exporter for all signals. /// @@ -105,8 +107,7 @@ internal static IOpenTelemetryBuilder UseOtlpExporter( this IOpenTelemetryBuilder builder, string? name = null, IConfiguration? configuration = null, - Action? configure = null, - int processorPipelineWeight = 10_000) + Action? configure = null) { Guard.ThrowIfNull(builder); @@ -119,12 +120,12 @@ internal static IOpenTelemetryBuilder UseOtlpExporter( configure?.Invoke(otlpBuilder); - UseOtlpExporterInternal(builder, name, processorPipelineWeight); + UseOtlpExporterInternal(builder, name); return builder; } - private static void UseOtlpExporterInternal(IOpenTelemetryBuilder builder, string? name, int processorPipelineWeight) + private static void UseOtlpExporterInternal(IOpenTelemetryBuilder builder, string? name) { // Note: We automatically turn on signals for "UseOtlpExporter" builder @@ -163,7 +164,7 @@ private static void UseOtlpExporterInternal(IOpenTelemetryBuilder builder, strin builderOptions.SdkLimitOptions, builderOptions.ExperimentalOptions); - processor.PipelineWeight = processorPipelineWeight; + processor.PipelineWeight = DefaultProcessorPipelineWeight; logging.AddProcessor(processor); }); @@ -194,7 +195,7 @@ private static void UseOtlpExporterInternal(IOpenTelemetryBuilder builder, strin processorOptions.BatchExportProcessorOptions, sp); - processor.PipelineWeight = processorPipelineWeight; + processor.PipelineWeight = DefaultProcessorPipelineWeight; tracing.AddProcessor(processor); }); From 57cbc9974f74fd6e74c6a600d95901f50af94762 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Mon, 11 Mar 2024 15:40:47 -0700 Subject: [PATCH 20/48] Tweak. --- .../Builder/OpenTelemetryBuilderOtlpExporterExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderOtlpExporterExtensions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderOtlpExporterExtensions.cs index ba4a3df6a3a..da3d5e7669f 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderOtlpExporterExtensions.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderOtlpExporterExtensions.cs @@ -53,7 +53,7 @@ public static IOpenTelemetryBuilder UseOtlpExporter( /// . /// The base endpoint to use. A signal-specific /// path will be appended to the base endpoint for each signal - /// automatically. + /// automatically if the protocol is set to . public static IOpenTelemetryBuilder UseOtlpExporter( this IOpenTelemetryBuilder builder, Uri baseEndpoint) From 356628efa577c9032b44fc166e43f74eda26a720 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Tue, 12 Mar 2024 12:12:12 -0700 Subject: [PATCH 21/48] Bug fix. --- ...nTelemetryBuilderOtlpExporterExtensions.cs | 9 +++++--- .../OtlpExporterOptions.cs | 6 +++++ .../OtlpLogExporterHelperExtensions.cs | 10 ++++++--- .../OtlpMetricExporterExtensions.cs | 20 ++++++++++++----- .../OtlpTraceExporterHelperExtensions.cs | 22 ++++++++++++++----- .../IntegrationTest/IntegrationTests.cs | 8 +++---- 6 files changed, 54 insertions(+), 21 deletions(-) diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderOtlpExporterExtensions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderOtlpExporterExtensions.cs index da3d5e7669f..6edeb74ca61 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderOtlpExporterExtensions.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderOtlpExporterExtensions.cs @@ -162,7 +162,8 @@ private static void UseOtlpExporterInternal(IOpenTelemetryBuilder builder, strin builderOptions.LoggingOptionsInstance.ApplyDefaults(builderOptions.DefaultOptionsInstance), builderOptions.LogRecordExportProcessorOptions ?? throw new NotSupportedException(), builderOptions.SdkLimitOptions, - builderOptions.ExperimentalOptions); + builderOptions.ExperimentalOptions, + skipUseOtlpExporterRegistrationCheck: true); processor.PipelineWeight = DefaultProcessorPipelineWeight; @@ -176,9 +177,10 @@ private static void UseOtlpExporterInternal(IOpenTelemetryBuilder builder, strin metrics.AddReader( OtlpMetricExporterExtensions.BuildOtlpExporterMetricReader( + sp, builderOptions.MetricsOptionsInstance.ApplyDefaults(builderOptions.DefaultOptionsInstance), builderOptions.MetricReaderOptions ?? throw new NotSupportedException(), - sp)); + skipUseOtlpExporterRegistrationCheck: true)); }); services.ConfigureOpenTelemetryTracerProvider( @@ -189,11 +191,12 @@ private static void UseOtlpExporterInternal(IOpenTelemetryBuilder builder, strin var processorOptions = builderOptions.ActivityExportProcessorOptions ?? throw new NotSupportedException(); var processor = OtlpTraceExporterHelperExtensions.BuildOtlpExporterProcessor( + sp, builderOptions.TracingOptionsInstance.ApplyDefaults(builderOptions.DefaultOptionsInstance), builderOptions.SdkLimitOptions, processorOptions.ExportProcessorType, processorOptions.BatchExportProcessorOptions, - sp); + skipUseOtlpExporterRegistrationCheck: true); processor.PipelineWeight = DefaultProcessorPipelineWeight; diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterOptions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterOptions.cs index 8193dbbcfee..9805c4e2b77 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterOptions.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterOptions.cs @@ -156,6 +156,12 @@ public Func HttpClientFactory /// internal bool AppendSignalPathToEndpoint { get; private set; } = true; + internal bool HasData + => this.protocol.HasValue + || this.endpoint != null + || this.timeoutMilliseconds.HasValue + || this.httpClientFactory != null; + internal static void RegisterOtlpExporterOptionsFactory(IServiceCollection services) { services.RegisterOptionsFactory(CreateOtlpExporterOptions); diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpLogExporterHelperExtensions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpLogExporterHelperExtensions.cs index b2d362bc391..5a8528c82ec 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpLogExporterHelperExtensions.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpLogExporterHelperExtensions.cs @@ -342,20 +342,24 @@ static LoggerProviderBuilder AddOtlpExporter( } internal static BaseProcessor BuildOtlpLogExporter( - IServiceProvider sp, + IServiceProvider serviceProvider, OtlpExporterOptions exporterOptions, LogRecordExportProcessorOptions processorOptions, SdkLimitOptions sdkLimitOptions, ExperimentalOptions experimentalOptions, + bool skipUseOtlpExporterRegistrationCheck = false, Func, BaseExporter>? configureExporterInstance = null) { - Debug.Assert(sp != null, "sp was null"); + Debug.Assert(serviceProvider != null, "serviceProvider 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"); - sp.EnsureNoUseOtlpExporterRegistrations(); + if (!skipUseOtlpExporterRegistrationCheck) + { + serviceProvider.EnsureNoUseOtlpExporterRegistrations(); + } /* * Note: diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpMetricExporterExtensions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpMetricExporterExtensions.cs index 2ccfc6b085d..2a65d610ac7 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpMetricExporterExtensions.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpMetricExporterExtensions.cs @@ -3,10 +3,12 @@ #nullable enable +using System.Diagnostics; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using OpenTelemetry.Exporter; +using OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation; using OpenTelemetry.Internal; namespace OpenTelemetry.Metrics; @@ -96,9 +98,9 @@ public static MeterProviderBuilder AddOtlpExporter( } return BuildOtlpExporterMetricReader( + sp, exporterOptions, - sp.GetRequiredService>().Get(finalOptionsName), - sp); + sp.GetRequiredService>().Get(finalOptionsName)); }); } @@ -169,17 +171,25 @@ public static MeterProviderBuilder AddOtlpExporter( configureExporterAndMetricReader?.Invoke(exporterOptions, metricReaderOptions); - return BuildOtlpExporterMetricReader(exporterOptions, metricReaderOptions, sp); + return BuildOtlpExporterMetricReader(sp, exporterOptions, metricReaderOptions); }); } internal static MetricReader BuildOtlpExporterMetricReader( + IServiceProvider serviceProvider, OtlpExporterOptions exporterOptions, MetricReaderOptions metricReaderOptions, - IServiceProvider serviceProvider, + bool skipUseOtlpExporterRegistrationCheck = false, Func, BaseExporter>? configureExporterInstance = null) { - serviceProvider.EnsureNoUseOtlpExporterRegistrations(); + Debug.Assert(serviceProvider != null, "serviceProvider was null"); + Debug.Assert(exporterOptions != null, "exporterOptions was null"); + Debug.Assert(metricReaderOptions != null, "metricReaderOptions was null"); + + if (!skipUseOtlpExporterRegistrationCheck) + { + serviceProvider.EnsureNoUseOtlpExporterRegistrations(); + } exporterOptions.TryEnableIHttpClientFactoryIntegration(serviceProvider, "OtlpMetricExporter"); diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpTraceExporterHelperExtensions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpTraceExporterHelperExtensions.cs index aba9cb06df5..70755dd8a3e 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpTraceExporterHelperExtensions.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpTraceExporterHelperExtensions.cs @@ -92,32 +92,42 @@ public static TracerProviderBuilder AddOtlpExporter( // instance. var sdkOptionsManager = sp.GetRequiredService>().CurrentValue; - return BuildOtlpExporterProcessor(exporterOptions, sdkOptionsManager, sp); + return BuildOtlpExporterProcessor(sp, exporterOptions, sdkOptionsManager); }); } internal static BaseProcessor BuildOtlpExporterProcessor( + IServiceProvider serviceProvider, OtlpExporterOptions exporterOptions, SdkLimitOptions sdkLimitOptions, - IServiceProvider serviceProvider, Func, BaseExporter>? configureExporterInstance = null) => BuildOtlpExporterProcessor( + serviceProvider, exporterOptions, sdkLimitOptions, exporterOptions.ExportProcessorType, exporterOptions.BatchExportProcessorOptions ?? new BatchExportActivityProcessorOptions(), - serviceProvider, - configureExporterInstance); + skipUseOtlpExporterRegistrationCheck: false, + configureExporterInstance: configureExporterInstance); internal static BaseProcessor BuildOtlpExporterProcessor( + IServiceProvider serviceProvider, OtlpExporterOptions exporterOptions, SdkLimitOptions sdkLimitOptions, ExportProcessorType exportProcessorType, BatchExportProcessorOptions batchExportProcessorOptions, - IServiceProvider serviceProvider, + bool skipUseOtlpExporterRegistrationCheck = false, Func, BaseExporter>? configureExporterInstance = null) { - serviceProvider.EnsureNoUseOtlpExporterRegistrations(); + Debug.Assert(serviceProvider != null, "serviceProvider was null"); + Debug.Assert(exporterOptions != null, "exporterOptions was null"); + Debug.Assert(sdkLimitOptions != null, "sdkLimitOptions was null"); + Debug.Assert(batchExportProcessorOptions != null, "batchExportProcessorOptions was null"); + + if (!skipUseOtlpExporterRegistrationCheck) + { + serviceProvider.EnsureNoUseOtlpExporterRegistrations(); + } exporterOptions.TryEnableIHttpClientFactoryIntegration(serviceProvider, "OtlpTraceExporter"); diff --git a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/IntegrationTest/IntegrationTests.cs b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/IntegrationTest/IntegrationTests.cs index 11b5b797127..a7d16e9451d 100644 --- a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/IntegrationTest/IntegrationTests.cs +++ b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/IntegrationTest/IntegrationTests.cs @@ -70,9 +70,9 @@ public void TraceExportResultIsSuccess(OtlpExportProtocol protocol, string endpo .AddSource(activitySourceName); builder.AddProcessor(OtlpTraceExporterHelperExtensions.BuildOtlpExporterProcessor( - exporterOptions, - DefaultSdkLimitOptions, serviceProvider: null, + exporterOptions: exporterOptions, + sdkLimitOptions: DefaultSdkLimitOptions, configureExporterInstance: otlpExporter => { delegatingExporter = new DelegatingExporter @@ -151,9 +151,9 @@ public void MetricExportResultIsSuccess(OtlpExportProtocol protocol, string endp readerOptions.PeriodicExportingMetricReaderOptions.ExportIntervalMilliseconds = useManualExport ? Timeout.Infinite : ExportIntervalMilliseconds; builder.AddReader(OtlpMetricExporterExtensions.BuildOtlpExporterMetricReader( - exporterOptions, - readerOptions, serviceProvider: null, + exporterOptions: exporterOptions, + metricReaderOptions: readerOptions, configureExporterInstance: otlpExporter => { delegatingExporter = new DelegatingExporter From 3493219fc75179488355314e5d942b20cacc6904 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Tue, 12 Mar 2024 12:38:24 -0700 Subject: [PATCH 22/48] Add tests. --- src/OpenTelemetry/AssemblyInfo.cs | 2 +- src/OpenTelemetry/CompositeProcessor.cs | 12 + ...xporter.OpenTelemetryProtocol.Tests.csproj | 1 - .../OtlpExporterOptionsTests.cs | 40 ++ .../UseOtlpExporterExtensionTests.cs | 344 ++++++++++++++++++ 5 files changed, 397 insertions(+), 2 deletions(-) create mode 100644 test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/UseOtlpExporterExtensionTests.cs diff --git a/src/OpenTelemetry/AssemblyInfo.cs b/src/OpenTelemetry/AssemblyInfo.cs index da02f32e095..45c008955ad 100644 --- a/src/OpenTelemetry/AssemblyInfo.cs +++ b/src/OpenTelemetry/AssemblyInfo.cs @@ -6,6 +6,7 @@ [assembly: InternalsVisibleTo("OpenTelemetry.Tests" + AssemblyInfo.PublicKey)] [assembly: InternalsVisibleTo("OpenTelemetry.Exporter.InMemory" + AssemblyInfo.PublicKey)] [assembly: InternalsVisibleTo("OpenTelemetry.Exporter.OpenTelemetryProtocol" + AssemblyInfo.PublicKey)] +[assembly: InternalsVisibleTo("OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests" + AssemblyInfo.PublicKey)] [assembly: InternalsVisibleTo("OpenTelemetry.Exporter.Prometheus.AspNetCore" + AssemblyInfo.PublicKey)] [assembly: InternalsVisibleTo("OpenTelemetry.Exporter.Prometheus.AspNetCore.Tests" + AssemblyInfo.PublicKey)] [assembly: InternalsVisibleTo("OpenTelemetry.Exporter.Prometheus.HttpListener.Tests" + AssemblyInfo.PublicKey)] @@ -15,7 +16,6 @@ #if !EXPOSE_EXPERIMENTAL_FEATURES [assembly: InternalsVisibleTo("OpenTelemetry.Exporter.Console" + AssemblyInfo.PublicKey)] -[assembly: InternalsVisibleTo("OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests" + AssemblyInfo.PublicKey)] [assembly: InternalsVisibleTo("OpenTelemetry.Tests.Stress.Metrics" + AssemblyInfo.PublicKey)] #endif diff --git a/src/OpenTelemetry/CompositeProcessor.cs b/src/OpenTelemetry/CompositeProcessor.cs index 614e0dd3b13..d59936ee87e 100644 --- a/src/OpenTelemetry/CompositeProcessor.cs +++ b/src/OpenTelemetry/CompositeProcessor.cs @@ -86,6 +86,18 @@ internal override void SetParentProvider(BaseProvider parentProvider) } } + internal IReadOnlyList> ToReadOnlyList() + { + var list = new List>(); + + for (var cur = this.Head; cur != null; cur = cur.Next) + { + list.Add(cur.Value); + } + + return list; + } + /// protected override bool OnForceFlush(int timeoutMilliseconds) { diff --git a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests.csproj b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests.csproj index 44802d3a5aa..ad60a2385a2 100644 --- a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests.csproj +++ b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests.csproj @@ -25,7 +25,6 @@ - diff --git a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpExporterOptionsTests.cs b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpExporterOptionsTests.cs index bf55abb4795..8790ec54ffb 100644 --- a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpExporterOptionsTests.cs +++ b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpExporterOptionsTests.cs @@ -259,6 +259,46 @@ public void OtlpExporterOptions_HttpClientFactoryThrowsWhenSetToNull() Assert.Throws(() => options.HttpClientFactory = null); } + [Fact] + public void OtlpExporterOptions_ApplyDefaultsTest() + { + var defaultOptions = new OtlpExporterOptions(OtlpExporterOptionsConfigurationType.Default); + + defaultOptions.Endpoint = new Uri("http://default_endpoint/"); + defaultOptions.Protocol = OtlpExportProtocol.HttpProtobuf; + defaultOptions.Headers = "key1=value1"; + defaultOptions.TimeoutMilliseconds = 18; + defaultOptions.HttpClientFactory = () => null!; + + var signalOptionsWithoutData = new OtlpExporterOptions(OtlpExporterOptionsConfigurationType.Traces); + + signalOptionsWithoutData.ApplyDefaults(defaultOptions); + + Assert.Equal(defaultOptions.Endpoint, signalOptionsWithoutData.Endpoint); + Assert.True(signalOptionsWithoutData.AppendSignalPathToEndpoint); + Assert.Equal(defaultOptions.Protocol, signalOptionsWithoutData.Protocol); + Assert.Equal(defaultOptions.Headers, signalOptionsWithoutData.Headers); + Assert.Equal(defaultOptions.TimeoutMilliseconds, signalOptionsWithoutData.TimeoutMilliseconds); + Assert.Equal(defaultOptions.HttpClientFactory, signalOptionsWithoutData.HttpClientFactory); + + var signalOptionsWithData = new OtlpExporterOptions(OtlpExporterOptionsConfigurationType.Metrics); + + signalOptionsWithData.Endpoint = new Uri("http://metrics_endpoint/"); + signalOptionsWithData.Protocol = OtlpExportProtocol.Grpc; + signalOptionsWithData.Headers = "key2=value2"; + signalOptionsWithData.TimeoutMilliseconds = 1800; + signalOptionsWithData.HttpClientFactory = () => throw new NotImplementedException(); + + signalOptionsWithData.ApplyDefaults(defaultOptions); + + Assert.NotEqual(defaultOptions.Endpoint, signalOptionsWithData.Endpoint); + Assert.False(signalOptionsWithData.AppendSignalPathToEndpoint); + Assert.NotEqual(defaultOptions.Protocol, signalOptionsWithData.Protocol); + Assert.NotEqual(defaultOptions.Headers, signalOptionsWithData.Headers); + Assert.NotEqual(defaultOptions.TimeoutMilliseconds, signalOptionsWithData.TimeoutMilliseconds); + Assert.NotEqual(defaultOptions.HttpClientFactory, signalOptionsWithData.HttpClientFactory); + } + private static void ClearEnvVars() { foreach (var item in GetOtlpExporterOptionsTestCases()) diff --git a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/UseOtlpExporterExtensionTests.cs b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/UseOtlpExporterExtensionTests.cs new file mode 100644 index 00000000000..d4ac745b856 --- /dev/null +++ b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/UseOtlpExporterExtensionTests.cs @@ -0,0 +1,344 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#nullable enable + +using System.Diagnostics; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using OpenTelemetry.Logs; +using OpenTelemetry.Metrics; +using OpenTelemetry.Tests; +using OpenTelemetry.Trace; +using Xunit; + +namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests; + +public class UseOtlpExporterExtensionTests +{ + [Fact] + public void UseOtlpExporterDefaultTest() + { + var services = new ServiceCollection(); + + services.AddOpenTelemetry() + .UseOtlpExporter(); + + using var sp = services.BuildServiceProvider(); + + var exporterOptions = sp.GetRequiredService>().CurrentValue; + + Assert.Equal(new Uri(OtlpExporterOptions.DefaultGrpcEndpoint), exporterOptions.DefaultOptions.Endpoint); + Assert.Equal(OtlpExporterOptions.DefaultOtlpExportProtocol, exporterOptions.DefaultOptions.Protocol); + Assert.False(((OtlpExporterOptions)exporterOptions.DefaultOptions).HasData); + + Assert.False(((OtlpExporterOptions)exporterOptions.LoggingOptions).HasData); + Assert.False(((OtlpExporterOptions)exporterOptions.MetricsOptions).HasData); + Assert.False(((OtlpExporterOptions)exporterOptions.TracingOptions).HasData); + } + + [Fact] + public void UseOtlpExporterSetEndpointTest() + { + var services = new ServiceCollection(); + + services.AddOpenTelemetry() + .UseOtlpExporter(new Uri("http://test_base_endpoint/")); + + using var sp = services.BuildServiceProvider(); + + var exporterOptions = sp.GetRequiredService>().CurrentValue; + + Assert.Equal(new Uri("http://test_base_endpoint/"), exporterOptions.DefaultOptions.Endpoint); + Assert.Equal(OtlpExporterOptions.DefaultOtlpExportProtocol, exporterOptions.DefaultOptions.Protocol); + Assert.True(((OtlpExporterOptions)exporterOptions.DefaultOptions).HasData); + + Assert.False(((OtlpExporterOptions)exporterOptions.LoggingOptions).HasData); + Assert.False(((OtlpExporterOptions)exporterOptions.MetricsOptions).HasData); + Assert.False(((OtlpExporterOptions)exporterOptions.TracingOptions).HasData); + } + + [Fact] + public void UseOtlpExporterSetEndpointAndProtocolTest() + { + var services = new ServiceCollection(); + + services.AddOpenTelemetry() + .UseOtlpExporter( + OtlpExportProtocol.HttpProtobuf, + new Uri("http://test_base_endpoint/")); + + using var sp = services.BuildServiceProvider(); + + var exporterOptions = sp.GetRequiredService>().CurrentValue; + + Assert.Equal(new Uri("http://test_base_endpoint/"), exporterOptions.DefaultOptions.Endpoint); + Assert.Equal(OtlpExportProtocol.HttpProtobuf, exporterOptions.DefaultOptions.Protocol); + Assert.True(((OtlpExporterOptions)exporterOptions.DefaultOptions).HasData); + + Assert.False(((OtlpExporterOptions)exporterOptions.LoggingOptions).HasData); + Assert.False(((OtlpExporterOptions)exporterOptions.MetricsOptions).HasData); + Assert.False(((OtlpExporterOptions)exporterOptions.TracingOptions).HasData); + } + + [Theory] + [InlineData(null)] + [InlineData("testNamedOptions")] + public void UseOtlpExporterConfigureTest(string? name) + { + var services = new ServiceCollection(); + + if (!string.IsNullOrEmpty(name)) + { + services.AddOpenTelemetry() + .UseOtlpExporter(name: name, configure: Configure); + } + else + { + services.AddOpenTelemetry() + .UseOtlpExporter(Configure); + + name = "otlp"; + } + + using var sp = services.BuildServiceProvider(); + + VerifyOptionsApplied(sp, name); + + static void Configure(OtlpExporterBuilder builder) + { + builder.ConfigureDefaultExporterOptions( + defaultOptions => defaultOptions.Endpoint = new Uri("http://default_endpoint/")); + + builder.ConfigureLoggingExporterOptions( + exporterOptions => exporterOptions.Endpoint = new Uri("http://signal_endpoint/logs/")); + builder.ConfigureLoggingProcessorOptions( + processorOptions => + { + processorOptions.ExportProcessorType = ExportProcessorType.Simple; + processorOptions.BatchExportProcessorOptions.ScheduledDelayMilliseconds = 1000; + }); + + builder.ConfigureMetricsExporterOptions( + exporterOptions => exporterOptions.Endpoint = new Uri("http://signal_endpoint/metrics/")); + builder.ConfigureMetricsReaderOptions( + readerOptions => + { + readerOptions.TemporalityPreference = MetricReaderTemporalityPreference.Delta; + readerOptions.PeriodicExportingMetricReaderOptions.ExportIntervalMilliseconds = 1001; + }); + + builder.ConfigureTracingExporterOptions( + exporterOptions => exporterOptions.Endpoint = new Uri("http://signal_endpoint/traces/")); + builder.ConfigureTracingProcessorOptions( + processorOptions => + { + processorOptions.ExportProcessorType = ExportProcessorType.Simple; + processorOptions.BatchExportProcessorOptions.ScheduledDelayMilliseconds = 1002; + }); + } + } + + [Theory] + [InlineData(null)] + [InlineData("testNamedOptions")] + public void UseOtlpExporterConfigurationTest(string? name) + { + var config = new ConfigurationBuilder() + .AddInMemoryCollection(new Dictionary + { + ["DefaultOptions:Endpoint"] = "http://default_endpoint/", + ["LoggingOptions:Endpoint"] = "http://signal_endpoint/logs/", + ["LoggingOptions:ExportProcessorType"] = "Simple", + ["LoggingOptions:BatchExportProcessorOptions:ScheduledDelayMilliseconds"] = "1000", + ["MetricsOptions:Endpoint"] = "http://signal_endpoint/metrics/", + ["MetricsOptions:TemporalityPreference"] = "Delta", + ["MetricsOptions:PeriodicExportingMetricReaderOptions:ExportIntervalMilliseconds"] = "1001", + ["TracingOptions:Endpoint"] = "http://signal_endpoint/traces/", + ["TracingOptions:ExportProcessorType"] = "Simple", + ["TracingOptions:BatchExportProcessorOptions:ScheduledDelayMilliseconds"] = "1002", + }) + .Build(); + + var services = new ServiceCollection(); + + if (!string.IsNullOrEmpty(name)) + { + services.AddOpenTelemetry() + .UseOtlpExporter(name: name, configuration: config); + } + else + { + services.AddOpenTelemetry() + .UseOtlpExporter(config); + + name = "otlp"; + } + + using var sp = services.BuildServiceProvider(); + + VerifyOptionsApplied(sp, name); + } + + [Fact] + public void UseOtlpExporterSingleCallsTest() + { + var services = new ServiceCollection(); + + services.AddOpenTelemetry() + .UseOtlpExporter(); + + using var sp = services.BuildServiceProvider(); + + Assert.NotNull(sp.GetRequiredService()); + Assert.NotNull(sp.GetRequiredService()); + Assert.NotNull(sp.GetRequiredService()); + } + + [Fact] + public void UseOtlpExporterMultipleCallsTest() + { + var services = new ServiceCollection(); + + services.AddOpenTelemetry() + .UseOtlpExporter() + .UseOtlpExporter(); + + using var sp = services.BuildServiceProvider(); + + Assert.Throws(() => sp.GetRequiredService()); + Assert.Throws(() => sp.GetRequiredService()); + Assert.Throws(() => sp.GetRequiredService()); + } + + [Fact] + public void UseOtlpExporterWithAddOtlpExporterLoggingTest() + { + var services = new ServiceCollection(); + + services.AddOpenTelemetry() + .UseOtlpExporter() + .WithLogging(builder => builder.AddOtlpExporter()); + + using var sp = services.BuildServiceProvider(); + + Assert.Throws(() => sp.GetRequiredService()); + } + + [Fact] + public void UseOtlpExporterWithAddOtlpExporterMetricsTest() + { + var services = new ServiceCollection(); + + services.AddOpenTelemetry() + .UseOtlpExporter() + .WithMetrics(builder => builder.AddOtlpExporter()); + + using var sp = services.BuildServiceProvider(); + + Assert.Throws(() => sp.GetRequiredService()); + } + + [Fact] + public void UseOtlpExporterWithAddOtlpExporterTracingTest() + { + var services = new ServiceCollection(); + + services.AddOpenTelemetry() + .UseOtlpExporter() + .WithTracing(builder => builder.AddOtlpExporter()); + + using var sp = services.BuildServiceProvider(); + + Assert.Throws(() => sp.GetRequiredService()); + } + + [Fact] + public void UseOtlpExporterAddsTracingProcessorToPipelineEndTest() + { + var services = new ServiceCollection(); + + services.AddOpenTelemetry() + .UseOtlpExporter() + .WithTracing(builder => builder.AddProcessor(new TestActivityProcessor())); + + using var sp = services.BuildServiceProvider(); + + var tracerProvider = sp.GetRequiredService() as TracerProviderSdk; + + Assert.NotNull(tracerProvider); + + var processor = tracerProvider.Processor as CompositeProcessor; + + Assert.NotNull(processor); + + var processors = processor.ToReadOnlyList(); + + Assert.True(processors[0] is TestActivityProcessor); + Assert.True(processors[1] is BatchActivityExportProcessor); + } + + [Fact] + public void UseOtlpExporterAddsLoggingProcessorToPipelineEndTest() + { + var services = new ServiceCollection(); + + services.AddOpenTelemetry() + .UseOtlpExporter() + .WithLogging(builder => builder.AddProcessor(new TestLogRecordProcessor())); + + using var sp = services.BuildServiceProvider(); + + var tracerProvider = sp.GetRequiredService() as LoggerProviderSdk; + + Assert.NotNull(tracerProvider); + + var processor = tracerProvider.Processor as CompositeProcessor; + + Assert.NotNull(processor); + + var processors = processor.ToReadOnlyList(); + + Assert.True(processors[0] is TestLogRecordProcessor); + Assert.True(processors[1] is BatchLogRecordExportProcessor); + } + + private static void VerifyOptionsApplied(ServiceProvider serviceProvider, string name) + { + var exporterOptions = serviceProvider.GetRequiredService>().Get(name); + + Assert.Equal("http://default_endpoint/", exporterOptions.DefaultOptions.Endpoint.ToString()); + /* Note: False is OK here. For cross-cutting extension + AppendSignalPathToEndpoint on default options isn't used for anything */ + Assert.False(((OtlpExporterOptions)exporterOptions.DefaultOptions).AppendSignalPathToEndpoint); + + Assert.Equal("http://signal_endpoint/logs/", exporterOptions.LoggingOptions.Endpoint.ToString()); + Assert.False(((OtlpExporterOptions)exporterOptions.LoggingOptions).AppendSignalPathToEndpoint); + + Assert.Equal("http://signal_endpoint/metrics/", exporterOptions.MetricsOptions.Endpoint.ToString()); + Assert.False(((OtlpExporterOptions)exporterOptions.MetricsOptions).AppendSignalPathToEndpoint); + + Assert.Equal("http://signal_endpoint/traces/", exporterOptions.TracingOptions.Endpoint.ToString()); + Assert.False(((OtlpExporterOptions)exporterOptions.TracingOptions).AppendSignalPathToEndpoint); + + var logRecordProcessorOptions = serviceProvider.GetRequiredService>().Get(name); + + Assert.Equal(ExportProcessorType.Simple, logRecordProcessorOptions.ExportProcessorType); + Assert.Equal(1000, logRecordProcessorOptions.BatchExportProcessorOptions.ScheduledDelayMilliseconds); + + var metricReaderOptions = serviceProvider.GetRequiredService>().Get(name); + + Assert.Equal(MetricReaderTemporalityPreference.Delta, metricReaderOptions.TemporalityPreference); + Assert.Equal(1001, metricReaderOptions.PeriodicExportingMetricReaderOptions.ExportIntervalMilliseconds); + + var activityProcessorOptions = serviceProvider.GetRequiredService>().Get(name); + + Assert.Equal(ExportProcessorType.Simple, activityProcessorOptions.ExportProcessorType); + Assert.Equal(1002, activityProcessorOptions.BatchExportProcessorOptions.ScheduledDelayMilliseconds); + } + + private sealed class TestLogRecordProcessor : BaseProcessor + { + } +} From 15ac47b9672820c95c65c86b52e4307db5e17e57 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Tue, 12 Mar 2024 12:51:31 -0700 Subject: [PATCH 23/48] CHANGELOG update. --- .../CHANGELOG.md | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md index e88f15d58dd..9fe970b903a 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md @@ -39,6 +39,29 @@ to `null` will now result in an `ArgumentNullException` being thrown. ([#5434](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5434)) +* Added `IOpenTelemetryBuilder.UseOtlpExporter` extension to simplify setup of + the OTLP Exporter when all three signals are used (logs, metrics, and traces). + The new extension has the following behaviors: + + * Calling `UseOtlpExporter` will automatically enable logging, tracing, and + metrics. Additional calls to `WithLogging`, `WithMetrics`, and `WithTracing` + are NOT required. + + * `UseOtlpExporter` can only be called once and cannot be used with the + existing `AddOtlpExporter` extensions. Extra calls will result in + `NotSupportedException`s being thrown. + + * `UseOtlpExporter` will register the OTLP Exporter at the end of the + processor pipeline for logging and tracing. + + * The OTLP Exporters added for logging, tracing, and metrics can be configured + using environment variables or `IConfiguration`. + + For details see: [README > UseOtlpExporter + Extension](./README.md#useotlpexporter-extension). + + PR: [#5400](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5400) + ## 1.7.0 Released 2023-Dec-08 From c80c86f58c546b47f407d55088a188a0d1ce9d7b Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Tue, 12 Mar 2024 12:53:43 -0700 Subject: [PATCH 24/48] Lint. --- src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md index 9fe970b903a..36980153d6a 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md @@ -55,7 +55,7 @@ processor pipeline for logging and tracing. * The OTLP Exporters added for logging, tracing, and metrics can be configured - using environment variables or `IConfiguration`. + using environment variables or `IConfiguration`. For details see: [README > UseOtlpExporter Extension](./README.md#useotlpexporter-extension). From 74f24fefefb35093e060f35eaa002cd4f1c73e05 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Tue, 12 Mar 2024 12:54:51 -0700 Subject: [PATCH 25/48] Warning cleanup. --- .../OtlpMetricExporterExtensions.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpMetricExporterExtensions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpMetricExporterExtensions.cs index 2a65d610ac7..886fe5368ec 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpMetricExporterExtensions.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpMetricExporterExtensions.cs @@ -8,7 +8,6 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using OpenTelemetry.Exporter; -using OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation; using OpenTelemetry.Internal; namespace OpenTelemetry.Metrics; From ef38a9d116359c212f21dc935290fa27053335c0 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Tue, 12 Mar 2024 14:14:14 -0700 Subject: [PATCH 26/48] Warning cleanup. --- .../OtlpMetricExporterExtensions.cs | 2 +- .../OtlpTraceExporterHelperExtensions.cs | 10 ++++------ .../UseOtlpExporterExtensionTests.cs | 4 ++-- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpMetricExporterExtensions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpMetricExporterExtensions.cs index 886fe5368ec..455e4f779de 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpMetricExporterExtensions.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpMetricExporterExtensions.cs @@ -201,6 +201,6 @@ internal static MetricReader BuildOtlpExporterMetricReader( return PeriodicExportingMetricReaderHelper.CreatePeriodicExportingMetricReader( metricExporter, - metricReaderOptions); + metricReaderOptions!); } } diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpTraceExporterHelperExtensions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpTraceExporterHelperExtensions.cs index 70755dd8a3e..0b0dd52f87e 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpTraceExporterHelperExtensions.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpTraceExporterHelperExtensions.cs @@ -144,14 +144,12 @@ internal static BaseProcessor BuildOtlpExporterProcessor( } else { - var batchOptions = batchExportProcessorOptions; - return new BatchActivityExportProcessor( otlpExporter, - batchOptions.MaxQueueSize, - batchOptions.ScheduledDelayMilliseconds, - batchOptions.ExporterTimeoutMilliseconds, - batchOptions.MaxExportBatchSize); + batchExportProcessorOptions!.MaxQueueSize, + batchExportProcessorOptions.ScheduledDelayMilliseconds, + batchExportProcessorOptions.ExporterTimeoutMilliseconds, + batchExportProcessorOptions.MaxExportBatchSize); } } } diff --git a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/UseOtlpExporterExtensionTests.cs b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/UseOtlpExporterExtensionTests.cs index d4ac745b856..b893a04f589 100644 --- a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/UseOtlpExporterExtensionTests.cs +++ b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/UseOtlpExporterExtensionTests.cs @@ -104,7 +104,7 @@ public void UseOtlpExporterConfigureTest(string? name) using var sp = services.BuildServiceProvider(); - VerifyOptionsApplied(sp, name); + VerifyOptionsApplied(sp, name!); static void Configure(OtlpExporterBuilder builder) { @@ -178,7 +178,7 @@ public void UseOtlpExporterConfigurationTest(string? name) using var sp = services.BuildServiceProvider(); - VerifyOptionsApplied(sp, name); + VerifyOptionsApplied(sp, name!); } [Fact] From e76706913a6f3d47fdc3d9815043b823527466a8 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Tue, 12 Mar 2024 14:27:39 -0700 Subject: [PATCH 27/48] Docfx fix. --- .../CHANGELOG.md | 4 ++-- src/OpenTelemetry.Exporter.OpenTelemetryProtocol/README.md | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md index 36980153d6a..0d1fad1da3a 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md @@ -57,8 +57,8 @@ * The OTLP Exporters added for logging, tracing, and metrics can be configured using environment variables or `IConfiguration`. - For details see: [README > UseOtlpExporter - Extension](./README.md#useotlpexporter-extension). + For details see: [README > Enable OTLP Exporter for all + signals](./README.md#enable-otlp-exporter-for-all-signals). PR: [#5400](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5400) diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/README.md b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/README.md index 763c2d54deb..a670487e5f7 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/README.md +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/README.md @@ -14,6 +14,7 @@ implementation. * [Enable Log Exporter](#enable-log-exporter) * [Enable Metric Exporter](#enable-metric-exporter) * [Enable Trace Exporter](#enable-trace-exporter) +* [Enable OTLP Exporter for all signals](#enable-otlp-exporter-for-all-signals) * [Configuration](#configuration) * [OtlpExporterOptions](#otlpexporteroptions) * [LogRecordExportProcessorOptions](#logrecordexportprocessoroptions) @@ -109,6 +110,10 @@ var tracerProvider = Sdk.CreateTracerProviderBuilder() See the [`TestOtlpExporter.cs`](../../examples/Console/TestOtlpExporter.cs) for runnable example. +## Enable OTLP Exporter for all signals + +Content coming soon. + ## Configuration You can configure the `OtlpExporter` through `OtlpExporterOptions` From f515cc01769b5e330c2b5b76c2e5a6458e99f987 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Tue, 12 Mar 2024 14:42:35 -0700 Subject: [PATCH 28/48] Integration test fixes. --- .../IntegrationTest/IntegrationTests.cs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/IntegrationTest/IntegrationTests.cs b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/IntegrationTest/IntegrationTests.cs index a7d16e9451d..ee802c64ede 100644 --- a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/IntegrationTest/IntegrationTests.cs +++ b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/IntegrationTest/IntegrationTests.cs @@ -21,6 +21,7 @@ public sealed class IntegrationTests : IDisposable private const string CollectorHostnameEnvVarName = "OTEL_COLLECTOR_HOSTNAME"; private const int ExportIntervalMilliseconds = 10000; private static readonly SdkLimitOptions DefaultSdkLimitOptions = new(); + private static readonly ExperimentalOptions DefaultExperimentalOptions = new(); private static readonly string CollectorHostname = SkipUnlessEnvVarFoundTheoryAttribute.GetEnvironmentVariable(CollectorHostnameEnvVarName); private readonly OpenTelemetryEventListener openTelemetryEventListener; @@ -69,8 +70,8 @@ public void TraceExportResultIsSuccess(OtlpExportProtocol protocol, string endpo var builder = Sdk.CreateTracerProviderBuilder() .AddSource(activitySourceName); - builder.AddProcessor(OtlpTraceExporterHelperExtensions.BuildOtlpExporterProcessor( - serviceProvider: null, + builder.AddProcessor(sp => OtlpTraceExporterHelperExtensions.BuildOtlpExporterProcessor( + serviceProvider: sp, exporterOptions: exporterOptions, sdkLimitOptions: DefaultSdkLimitOptions, configureExporterInstance: otlpExporter => @@ -150,8 +151,8 @@ public void MetricExportResultIsSuccess(OtlpExportProtocol protocol, string endp var readerOptions = new MetricReaderOptions(); readerOptions.PeriodicExportingMetricReaderOptions.ExportIntervalMilliseconds = useManualExport ? Timeout.Infinite : ExportIntervalMilliseconds; - builder.AddReader(OtlpMetricExporterExtensions.BuildOtlpExporterMetricReader( - serviceProvider: null, + builder.AddReader(sp => OtlpMetricExporterExtensions.BuildOtlpExporterMetricReader( + serviceProvider: sp, exporterOptions: exporterOptions, metricReaderOptions: readerOptions, configureExporterInstance: otlpExporter => @@ -238,8 +239,8 @@ public void LogExportResultIsSuccess(OtlpExportProtocol protocol, string endpoin sp, exporterOptions, processorOptions, - new SdkLimitOptions(), - new ExperimentalOptions(), + DefaultSdkLimitOptions, + DefaultExperimentalOptions, configureExporterInstance: otlpExporter => { delegatingExporter = new DelegatingExporter From b1450075f579fb706b47551d09b9aa7aac0b6ddd Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Tue, 12 Mar 2024 14:44:28 -0700 Subject: [PATCH 29/48] Code review. --- .../Builder/OpenTelemetryBuilderOtlpExporterExtensions.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderOtlpExporterExtensions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderOtlpExporterExtensions.cs index 6edeb74ca61..9e9f43f9e41 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderOtlpExporterExtensions.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderOtlpExporterExtensions.cs @@ -77,10 +77,7 @@ public static IOpenTelemetryBuilder UseOtlpExporter( otlpBuilder.ConfigureDefaultExporterOptions(o => { o.Protocol = protocol; - if (baseEndpoint != null) - { - o.Endpoint = baseEndpoint; - } + o.Endpoint = baseEndpoint; }); }); } From 16e07410b89170926b1cdf029f653b169a540b15 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Tue, 12 Mar 2024 14:59:32 -0700 Subject: [PATCH 30/48] JSON config comment update. --- .../Builder/OtlpExporterBuilder.cs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OtlpExporterBuilder.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OtlpExporterBuilder.cs index 2c7435c1e47..4c611c8ad9a 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OtlpExporterBuilder.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OtlpExporterBuilder.cs @@ -120,16 +120,25 @@ private static void BindConfigurationToOptions(IServiceCollection services, stri "Endpoint": "http://default_endpoint/" }, "LoggingOptions": { - "Endpoint": "http://logs_endpoint/logs/" + "Endpoint": "http://logs_endpoint/" "ExportProcessorType": Batch, "BatchExportProcessorOptions": { - "ScheduledDelayMilliseconds": 1000 + "ScheduledDelayMilliseconds": 5000 } }, "MetricsOptions": { - "Endpoint": "http://metrics_endpoint/metrics/" + "Endpoint": "http://metrics_endpoint/", + "TemporalityPreference": "Delta", + "PeriodicExportingMetricReaderOptions": { + "ExportIntervalMilliseconds": 5000 + } }, "TracingOptions": { + "Endpoint": "http://trcing_endpoint/" + "ExportProcessorType": Batch, + "BatchExportProcessorOptions": { + "ScheduledDelayMilliseconds": 5000 + } } } */ From 6f2c8e012d119b7988e3ffe8b43924ce1c713b39 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Tue, 12 Mar 2024 15:09:52 -0700 Subject: [PATCH 31/48] Code review. --- .../Builder/OpenTelemetryBuilderOtlpExporterExtensions.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderOtlpExporterExtensions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderOtlpExporterExtensions.cs index 9e9f43f9e41..045bf11c4d9 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderOtlpExporterExtensions.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderOtlpExporterExtensions.cs @@ -124,6 +124,8 @@ internal static IOpenTelemetryBuilder UseOtlpExporter( private static void UseOtlpExporterInternal(IOpenTelemetryBuilder builder, string? name) { + name ??= Options.DefaultName; + // Note: We automatically turn on signals for "UseOtlpExporter" builder .WithLogging() @@ -147,8 +149,6 @@ private static void UseOtlpExporterInternal(IOpenTelemetryBuilder builder, strin sp.GetService>()?.Get(name), sp.GetService>()?.Get(name))); - name ??= Options.DefaultName; - services.ConfigureOpenTelemetryLoggerProvider( (sp, logging) => { From b75f139910eef47fbf56644a0756045713bee107 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Tue, 12 Mar 2024 15:22:58 -0700 Subject: [PATCH 32/48] Code review. --- .../OpenTelemetryBuilderOtlpExporterExtensions.cs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderOtlpExporterExtensions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderOtlpExporterExtensions.cs index 045bf11c4d9..31ba61f195e 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderOtlpExporterExtensions.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderOtlpExporterExtensions.cs @@ -145,6 +145,12 @@ private static void UseOtlpExporterInternal(IOpenTelemetryBuilder builder, strin configuration, sp.GetRequiredService>().CurrentValue, sp.GetRequiredService>().CurrentValue, + /* Note: We allow LogRecordExportProcessorOptions, + MetricReaderOptions, & ActivityExportProcessorOptions to be null + because those only exist if the corresponding signal is turned on. + Currently this extension turns on all signals so they will always be + there but that may change in the future so it is handled + defensively. */ sp.GetService>()?.Get(name), sp.GetService>()?.Get(name), sp.GetService>()?.Get(name))); @@ -157,7 +163,7 @@ private static void UseOtlpExporterInternal(IOpenTelemetryBuilder builder, strin var processor = OtlpLogExporterHelperExtensions.BuildOtlpLogExporter( sp, builderOptions.LoggingOptionsInstance.ApplyDefaults(builderOptions.DefaultOptionsInstance), - builderOptions.LogRecordExportProcessorOptions ?? throw new NotSupportedException(), + builderOptions.LogRecordExportProcessorOptions ?? throw new InvalidOperationException("LogRecordExportProcessorOptions were missing with logging enabled"), builderOptions.SdkLimitOptions, builderOptions.ExperimentalOptions, skipUseOtlpExporterRegistrationCheck: true); @@ -176,7 +182,7 @@ private static void UseOtlpExporterInternal(IOpenTelemetryBuilder builder, strin OtlpMetricExporterExtensions.BuildOtlpExporterMetricReader( sp, builderOptions.MetricsOptionsInstance.ApplyDefaults(builderOptions.DefaultOptionsInstance), - builderOptions.MetricReaderOptions ?? throw new NotSupportedException(), + builderOptions.MetricReaderOptions ?? throw new InvalidOperationException("MetricReaderOptions were missing with metrics enabled"), skipUseOtlpExporterRegistrationCheck: true)); }); @@ -185,7 +191,7 @@ private static void UseOtlpExporterInternal(IOpenTelemetryBuilder builder, strin { var builderOptions = GetBuilderOptionsAndValidateRegistrations(sp, name); - var processorOptions = builderOptions.ActivityExportProcessorOptions ?? throw new NotSupportedException(); + var processorOptions = builderOptions.ActivityExportProcessorOptions ?? throw new InvalidOperationException("ActivityExportProcessorOptions were missing with tracing enabled"); var processor = OtlpTraceExporterHelperExtensions.BuildOtlpExporterProcessor( sp, From 0faac1a49f79c5b906d5b8d854bc651ef7db9ede Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Tue, 12 Mar 2024 16:22:27 -0700 Subject: [PATCH 33/48] CHANGELOG tweak. --- src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md index 0d1fad1da3a..54bdae200ea 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md @@ -45,7 +45,8 @@ * Calling `UseOtlpExporter` will automatically enable logging, tracing, and metrics. Additional calls to `WithLogging`, `WithMetrics`, and `WithTracing` - are NOT required. + are NOT required however for metrics and tracing sources/meters still need + to be enabled. * `UseOtlpExporter` can only be called once and cannot be used with the existing `AddOtlpExporter` extensions. Extra calls will result in From 4bef0ef5e784ab62ba3e1441e98d02e8500529bc Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Tue, 12 Mar 2024 17:09:27 -0700 Subject: [PATCH 34/48] Ctor tweak. --- .../Builder/OtlpExporterBuilderOptions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OtlpExporterBuilderOptions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OtlpExporterBuilderOptions.cs index f5dca04c657..e3ba3541ffb 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OtlpExporterBuilderOptions.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OtlpExporterBuilderOptions.cs @@ -25,7 +25,7 @@ internal sealed class OtlpExporterBuilderOptions internal readonly OtlpExporterOptions MetricsOptionsInstance; internal readonly OtlpExporterOptions TracingOptionsInstance; - public OtlpExporterBuilderOptions( + internal OtlpExporterBuilderOptions( IConfiguration configuration, SdkLimitOptions sdkLimitOptions, ExperimentalOptions experimentalOptions, From 956f537e0f755c01b2e5bb393dbe8fda616bbe27 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Wed, 13 Mar 2024 09:20:58 -0700 Subject: [PATCH 35/48] Test tweaks. --- .../OtlpExporterOptionsTests.cs | 62 +++++++++++-------- 1 file changed, 35 insertions(+), 27 deletions(-) diff --git a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpExporterOptionsTests.cs b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpExporterOptionsTests.cs index 8790ec54ffb..0ba2658482d 100644 --- a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpExporterOptionsTests.cs +++ b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpExporterOptionsTests.cs @@ -262,41 +262,49 @@ public void OtlpExporterOptions_HttpClientFactoryThrowsWhenSetToNull() [Fact] public void OtlpExporterOptions_ApplyDefaultsTest() { - var defaultOptions = new OtlpExporterOptions(OtlpExporterOptionsConfigurationType.Default); + var defaultOptionsWithData = new OtlpExporterOptions + { + Endpoint = new Uri("http://default_endpoint/"), + Protocol = OtlpExportProtocol.HttpProtobuf, + Headers = "key1=value1", + TimeoutMilliseconds = 18, + HttpClientFactory = () => null!, + }; - defaultOptions.Endpoint = new Uri("http://default_endpoint/"); - defaultOptions.Protocol = OtlpExportProtocol.HttpProtobuf; - defaultOptions.Headers = "key1=value1"; - defaultOptions.TimeoutMilliseconds = 18; - defaultOptions.HttpClientFactory = () => null!; + Assert.True(defaultOptionsWithData.HasData); - var signalOptionsWithoutData = new OtlpExporterOptions(OtlpExporterOptionsConfigurationType.Traces); + var targetOptionsWithoutData = new OtlpExporterOptions(); - signalOptionsWithoutData.ApplyDefaults(defaultOptions); + Assert.False(targetOptionsWithoutData.HasData); - Assert.Equal(defaultOptions.Endpoint, signalOptionsWithoutData.Endpoint); - Assert.True(signalOptionsWithoutData.AppendSignalPathToEndpoint); - Assert.Equal(defaultOptions.Protocol, signalOptionsWithoutData.Protocol); - Assert.Equal(defaultOptions.Headers, signalOptionsWithoutData.Headers); - Assert.Equal(defaultOptions.TimeoutMilliseconds, signalOptionsWithoutData.TimeoutMilliseconds); - Assert.Equal(defaultOptions.HttpClientFactory, signalOptionsWithoutData.HttpClientFactory); + targetOptionsWithoutData.ApplyDefaults(defaultOptionsWithData); - var signalOptionsWithData = new OtlpExporterOptions(OtlpExporterOptionsConfigurationType.Metrics); + Assert.Equal(defaultOptionsWithData.Endpoint, targetOptionsWithoutData.Endpoint); + Assert.True(targetOptionsWithoutData.AppendSignalPathToEndpoint); + Assert.Equal(defaultOptionsWithData.Protocol, targetOptionsWithoutData.Protocol); + Assert.Equal(defaultOptionsWithData.Headers, targetOptionsWithoutData.Headers); + Assert.Equal(defaultOptionsWithData.TimeoutMilliseconds, targetOptionsWithoutData.TimeoutMilliseconds); + Assert.Equal(defaultOptionsWithData.HttpClientFactory, targetOptionsWithoutData.HttpClientFactory); + + var targetOptionsWithData = new OtlpExporterOptions + { + Endpoint = new Uri("http://metrics_endpoint/"), + Protocol = OtlpExportProtocol.Grpc, + Headers = "key2=value2", + TimeoutMilliseconds = 1800, + HttpClientFactory = () => throw new NotImplementedException(), + }; - signalOptionsWithData.Endpoint = new Uri("http://metrics_endpoint/"); - signalOptionsWithData.Protocol = OtlpExportProtocol.Grpc; - signalOptionsWithData.Headers = "key2=value2"; - signalOptionsWithData.TimeoutMilliseconds = 1800; - signalOptionsWithData.HttpClientFactory = () => throw new NotImplementedException(); + Assert.True(targetOptionsWithData.HasData); - signalOptionsWithData.ApplyDefaults(defaultOptions); + targetOptionsWithData.ApplyDefaults(defaultOptionsWithData); - Assert.NotEqual(defaultOptions.Endpoint, signalOptionsWithData.Endpoint); - Assert.False(signalOptionsWithData.AppendSignalPathToEndpoint); - Assert.NotEqual(defaultOptions.Protocol, signalOptionsWithData.Protocol); - Assert.NotEqual(defaultOptions.Headers, signalOptionsWithData.Headers); - Assert.NotEqual(defaultOptions.TimeoutMilliseconds, signalOptionsWithData.TimeoutMilliseconds); - Assert.NotEqual(defaultOptions.HttpClientFactory, signalOptionsWithData.HttpClientFactory); + Assert.NotEqual(defaultOptionsWithData.Endpoint, targetOptionsWithData.Endpoint); + Assert.False(targetOptionsWithData.AppendSignalPathToEndpoint); + Assert.NotEqual(defaultOptionsWithData.Protocol, targetOptionsWithData.Protocol); + Assert.NotEqual(defaultOptionsWithData.Headers, targetOptionsWithData.Headers); + Assert.NotEqual(defaultOptionsWithData.TimeoutMilliseconds, targetOptionsWithData.TimeoutMilliseconds); + Assert.NotEqual(defaultOptionsWithData.HttpClientFactory, targetOptionsWithData.HttpClientFactory); } private static void ClearEnvVars() From 2c1cc370e7e21a62df7c7d8b367de42d7cdd4753 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Wed, 13 Mar 2024 10:24:58 -0700 Subject: [PATCH 36/48] Tweaked exposed overloads and refactored code. --- .../.publicApi/Stable/PublicAPI.Unshipped.txt | 2 +- ...nTelemetryBuilderOtlpExporterExtensions.cs | 155 ++++-------------- .../Builder/OtlpExporterBuilder.cs | 106 +++++++++++- 3 files changed, 136 insertions(+), 127 deletions(-) diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/.publicApi/Stable/PublicAPI.Unshipped.txt b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/.publicApi/Stable/PublicAPI.Unshipped.txt index 5cde8170f36..dbc6de8075b 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/.publicApi/Stable/PublicAPI.Unshipped.txt +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/.publicApi/Stable/PublicAPI.Unshipped.txt @@ -1,4 +1,4 @@ OpenTelemetry.Exporter.OpenTelemetryBuilderOtlpExporterExtensions static OpenTelemetry.Exporter.OpenTelemetryBuilderOtlpExporterExtensions.UseOtlpExporter(this OpenTelemetry.IOpenTelemetryBuilder! builder) -> OpenTelemetry.IOpenTelemetryBuilder! static OpenTelemetry.Exporter.OpenTelemetryBuilderOtlpExporterExtensions.UseOtlpExporter(this OpenTelemetry.IOpenTelemetryBuilder! builder, OpenTelemetry.Exporter.OtlpExportProtocol protocol, System.Uri! baseEndpoint) -> OpenTelemetry.IOpenTelemetryBuilder! -static OpenTelemetry.Exporter.OpenTelemetryBuilderOtlpExporterExtensions.UseOtlpExporter(this OpenTelemetry.IOpenTelemetryBuilder! builder, System.Uri! baseEndpoint) -> OpenTelemetry.IOpenTelemetryBuilder! \ No newline at end of file +static OpenTelemetry.Exporter.OpenTelemetryBuilderOtlpExporterExtensions.UseOtlpExporter(this OpenTelemetry.IOpenTelemetryBuilder! builder, OpenTelemetry.Exporter.OtlpExportProtocol protocol) -> OpenTelemetry.IOpenTelemetryBuilder! diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderOtlpExporterExtensions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderOtlpExporterExtensions.cs index 31ba61f195e..6bae5971909 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderOtlpExporterExtensions.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderOtlpExporterExtensions.cs @@ -4,13 +4,7 @@ #nullable enable using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; -using OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation; using OpenTelemetry.Internal; -using OpenTelemetry.Logs; -using OpenTelemetry.Metrics; -using OpenTelemetry.Trace; namespace OpenTelemetry.Exporter; @@ -21,8 +15,6 @@ namespace OpenTelemetry.Exporter; /// public static class OpenTelemetryBuilderOtlpExporterExtensions { - private const int DefaultProcessorPipelineWeight = 10_000; - /// /// Uses OpenTelemetry Protocol (OTLP) exporter for all signals. /// @@ -45,26 +37,29 @@ public static class OpenTelemetryBuilderOtlpExporterExtensions /// Supplied for chaining calls. public static IOpenTelemetryBuilder UseOtlpExporter( this IOpenTelemetryBuilder builder) - => UseOtlpExporter(builder, name: null); + => UseOtlpExporter(builder, name: null, configuration: null, configure: null); /// /// /// /// . - /// The base endpoint to use. A signal-specific - /// path will be appended to the base endpoint for each signal - /// automatically if the protocol is set to . + /// . public static IOpenTelemetryBuilder UseOtlpExporter( this IOpenTelemetryBuilder builder, - Uri baseEndpoint) - => UseOtlpExporter(builder, OtlpExportProtocol.Grpc, baseEndpoint); + OtlpExportProtocol protocol) + => UseOtlpExporterWithProtocolAndBaseEndpoint(builder, protocol, baseEndpoint: null); /// /// /// /// . /// . - /// + /// + /// Base endpoint to use. + /// Note: A signal-specific path will be appended to the base endpoint for + /// each signal automatically if the protocol is set to . + /// public static IOpenTelemetryBuilder UseOtlpExporter( this IOpenTelemetryBuilder builder, OtlpExportProtocol protocol, @@ -72,14 +67,7 @@ public static IOpenTelemetryBuilder UseOtlpExporter( { Guard.ThrowIfNull(baseEndpoint); - return UseOtlpExporter(builder, name: null, configure: otlpBuilder => - { - otlpBuilder.ConfigureDefaultExporterOptions(o => - { - o.Protocol = protocol; - o.Endpoint = baseEndpoint; - }); - }); + return UseOtlpExporterWithProtocolAndBaseEndpoint(builder, protocol, baseEndpoint); } internal static IOpenTelemetryBuilder UseOtlpExporter( @@ -88,7 +76,7 @@ internal static IOpenTelemetryBuilder UseOtlpExporter( { Guard.ThrowIfNull(configure); - return UseOtlpExporter(builder, name: null, configure: configure); + return UseOtlpExporter(builder, name: null, configuration: null, configure); } internal static IOpenTelemetryBuilder UseOtlpExporter( @@ -97,120 +85,45 @@ internal static IOpenTelemetryBuilder UseOtlpExporter( { Guard.ThrowIfNull(configuration); - return UseOtlpExporter(builder, name: null, configuration: configuration); + return UseOtlpExporter(builder, name: null, configuration, configure: null); } internal static IOpenTelemetryBuilder UseOtlpExporter( this IOpenTelemetryBuilder builder, - string? name = null, - IConfiguration? configuration = null, - Action? configure = null) + string? name, + IConfiguration? configuration, + Action? configure) { Guard.ThrowIfNull(builder); - if (configuration != null && string.IsNullOrEmpty(name)) - { - name = "otlp"; - } - - var otlpBuilder = new OtlpExporterBuilder(builder.Services, name, configuration); - - configure?.Invoke(otlpBuilder); - - UseOtlpExporterInternal(builder, name); - - return builder; - } - - private static void UseOtlpExporterInternal(IOpenTelemetryBuilder builder, string? name) - { - name ??= Options.DefaultName; - // Note: We automatically turn on signals for "UseOtlpExporter" builder .WithLogging() .WithMetrics() .WithTracing(); - var services = builder.Services; - - // Note: UseOtlpExporterRegistration is added to the service collection - // to detect repeated calls to "UseOtlpExporter" and to throw if - // "AddOtlpExporter" extensions are called - services.AddSingleton(); - - services.RegisterOptionsFactory(configuration => new SdkLimitOptions(configuration)); - services.RegisterOptionsFactory(configuration => new ExperimentalOptions(configuration)); - services.RegisterOptionsFactory((sp, configuration, name) => new OtlpExporterBuilderOptions( - configuration, - sp.GetRequiredService>().CurrentValue, - sp.GetRequiredService>().CurrentValue, - /* Note: We allow LogRecordExportProcessorOptions, - MetricReaderOptions, & ActivityExportProcessorOptions to be null - because those only exist if the corresponding signal is turned on. - Currently this extension turns on all signals so they will always be - there but that may change in the future so it is handled - defensively. */ - sp.GetService>()?.Get(name), - sp.GetService>()?.Get(name), - sp.GetService>()?.Get(name))); - - services.ConfigureOpenTelemetryLoggerProvider( - (sp, logging) => - { - var builderOptions = GetBuilderOptionsAndValidateRegistrations(sp, name); - - var processor = OtlpLogExporterHelperExtensions.BuildOtlpLogExporter( - sp, - builderOptions.LoggingOptionsInstance.ApplyDefaults(builderOptions.DefaultOptionsInstance), - builderOptions.LogRecordExportProcessorOptions ?? throw new InvalidOperationException("LogRecordExportProcessorOptions were missing with logging enabled"), - builderOptions.SdkLimitOptions, - builderOptions.ExperimentalOptions, - skipUseOtlpExporterRegistrationCheck: true); - - processor.PipelineWeight = DefaultProcessorPipelineWeight; + var otlpBuilder = new OtlpExporterBuilder(builder.Services, name, configuration); - logging.AddProcessor(processor); - }); + configure?.Invoke(otlpBuilder); - services.ConfigureOpenTelemetryMeterProvider( - (sp, metrics) => - { - var builderOptions = GetBuilderOptionsAndValidateRegistrations(sp, name); - - metrics.AddReader( - OtlpMetricExporterExtensions.BuildOtlpExporterMetricReader( - sp, - builderOptions.MetricsOptionsInstance.ApplyDefaults(builderOptions.DefaultOptionsInstance), - builderOptions.MetricReaderOptions ?? throw new InvalidOperationException("MetricReaderOptions were missing with metrics enabled"), - skipUseOtlpExporterRegistrationCheck: true)); - }); + return builder; + } - services.ConfigureOpenTelemetryTracerProvider( - (sp, tracing) => + private static IOpenTelemetryBuilder UseOtlpExporterWithProtocolAndBaseEndpoint( + this IOpenTelemetryBuilder builder, + OtlpExportProtocol protocol, + Uri? baseEndpoint) + { + return UseOtlpExporter(builder, name: null, configuration: null, configure: otlpBuilder => + { + otlpBuilder.ConfigureDefaultExporterOptions(o => { - var builderOptions = GetBuilderOptionsAndValidateRegistrations(sp, name); - - var processorOptions = builderOptions.ActivityExportProcessorOptions ?? throw new InvalidOperationException("ActivityExportProcessorOptions were missing with tracing enabled"); - - var processor = OtlpTraceExporterHelperExtensions.BuildOtlpExporterProcessor( - sp, - builderOptions.TracingOptionsInstance.ApplyDefaults(builderOptions.DefaultOptionsInstance), - builderOptions.SdkLimitOptions, - processorOptions.ExportProcessorType, - processorOptions.BatchExportProcessorOptions, - skipUseOtlpExporterRegistrationCheck: true); - - processor.PipelineWeight = DefaultProcessorPipelineWeight; - - tracing.AddProcessor(processor); + o.Protocol = protocol; + if (baseEndpoint != null) + { + o.Endpoint = baseEndpoint; + } }); - - static OtlpExporterBuilderOptions GetBuilderOptionsAndValidateRegistrations(IServiceProvider sp, string name) - { - sp.EnsureSingleUseOtlpExporterRegistration(); - - return sp.GetRequiredService>().Get(name); - } + }); } } diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OtlpExporterBuilder.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OtlpExporterBuilder.cs index 4c611c8ad9a..30f0d9cf2a2 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OtlpExporterBuilder.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OtlpExporterBuilder.cs @@ -6,6 +6,8 @@ using System.Diagnostics; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation; using OpenTelemetry.Internal; using OpenTelemetry.Logs; using OpenTelemetry.Metrics; @@ -15,7 +17,9 @@ namespace OpenTelemetry.Exporter; internal sealed class OtlpExporterBuilder { - private readonly string? name; + private const int DefaultProcessorPipelineWeight = 10_000; + + private readonly string name; internal OtlpExporterBuilder( IServiceCollection services, @@ -24,15 +28,22 @@ internal OtlpExporterBuilder( { Debug.Assert(services != null, "services was null"); - this.name = name; - this.Services = services!; - if (configuration != null) { - Debug.Assert(!string.IsNullOrEmpty(name), "name was null or empty"); + if (string.IsNullOrEmpty(name)) + { + name = "otlp"; + } BindConfigurationToOptions(services!, name!, configuration); } + + name ??= Options.DefaultName; + + RegisterOtlpExporterServices(services!, name!); + + this.name = name; + this.Services = services!; } public IServiceCollection Services { get; } @@ -154,4 +165,89 @@ private static void BindConfigurationToOptions(IServiceCollection services, stri services!.Configure( name, configuration.GetSection(nameof(OtlpExporterBuilderOptions.TracingOptions))); } + + private static void RegisterOtlpExporterServices(IServiceCollection services, string name) + { + Debug.Assert(services != null, "services was null"); + Debug.Assert(!string.IsNullOrEmpty(name), "name was null or empty"); + + // Note: UseOtlpExporterRegistration is added to the service collection + // to detect repeated calls to "UseOtlpExporter" and to throw if + // "AddOtlpExporter" extensions are called + services!.AddSingleton(); + + services!.RegisterOptionsFactory(configuration => new SdkLimitOptions(configuration)); + services!.RegisterOptionsFactory(configuration => new ExperimentalOptions(configuration)); + services!.RegisterOptionsFactory((sp, configuration, name) => new OtlpExporterBuilderOptions( + configuration, + sp.GetRequiredService>().CurrentValue, + sp.GetRequiredService>().CurrentValue, + /* Note: We allow LogRecordExportProcessorOptions, + MetricReaderOptions, & ActivityExportProcessorOptions to be null + because those only exist if the corresponding signal is turned on. + Currently this extension turns on all signals so they will always be + there but that may change in the future so it is handled + defensively. */ + sp.GetService>()?.Get(name), + sp.GetService>()?.Get(name), + sp.GetService>()?.Get(name))); + + services!.ConfigureOpenTelemetryLoggerProvider( + (sp, logging) => + { + var builderOptions = GetBuilderOptionsAndValidateRegistrations(sp, name); + + var processor = OtlpLogExporterHelperExtensions.BuildOtlpLogExporter( + sp, + builderOptions.LoggingOptionsInstance.ApplyDefaults(builderOptions.DefaultOptionsInstance), + builderOptions.LogRecordExportProcessorOptions ?? throw new InvalidOperationException("LogRecordExportProcessorOptions were missing with logging enabled"), + builderOptions.SdkLimitOptions, + builderOptions.ExperimentalOptions, + skipUseOtlpExporterRegistrationCheck: true); + + processor.PipelineWeight = DefaultProcessorPipelineWeight; + + logging.AddProcessor(processor); + }); + + services!.ConfigureOpenTelemetryMeterProvider( + (sp, metrics) => + { + var builderOptions = GetBuilderOptionsAndValidateRegistrations(sp, name); + + metrics.AddReader( + OtlpMetricExporterExtensions.BuildOtlpExporterMetricReader( + sp, + builderOptions.MetricsOptionsInstance.ApplyDefaults(builderOptions.DefaultOptionsInstance), + builderOptions.MetricReaderOptions ?? throw new InvalidOperationException("MetricReaderOptions were missing with metrics enabled"), + skipUseOtlpExporterRegistrationCheck: true)); + }); + + services!.ConfigureOpenTelemetryTracerProvider( + (sp, tracing) => + { + var builderOptions = GetBuilderOptionsAndValidateRegistrations(sp, name); + + var processorOptions = builderOptions.ActivityExportProcessorOptions ?? throw new InvalidOperationException("ActivityExportProcessorOptions were missing with tracing enabled"); + + var processor = OtlpTraceExporterHelperExtensions.BuildOtlpExporterProcessor( + sp, + builderOptions.TracingOptionsInstance.ApplyDefaults(builderOptions.DefaultOptionsInstance), + builderOptions.SdkLimitOptions, + processorOptions.ExportProcessorType, + processorOptions.BatchExportProcessorOptions, + skipUseOtlpExporterRegistrationCheck: true); + + processor.PipelineWeight = DefaultProcessorPipelineWeight; + + tracing.AddProcessor(processor); + }); + + static OtlpExporterBuilderOptions GetBuilderOptionsAndValidateRegistrations(IServiceProvider sp, string name) + { + sp.EnsureSingleUseOtlpExporterRegistration(); + + return sp.GetRequiredService>().Get(name); + } + } } From e577dc3b9b3a60fefa2622eeb9448dd047965703 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Wed, 13 Mar 2024 10:48:07 -0700 Subject: [PATCH 37/48] Test fixes. --- .../UseOtlpExporterExtensionTests.cs | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/UseOtlpExporterExtensionTests.cs b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/UseOtlpExporterExtensionTests.cs index b893a04f589..9b3846b16ba 100644 --- a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/UseOtlpExporterExtensionTests.cs +++ b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/UseOtlpExporterExtensionTests.cs @@ -39,19 +39,19 @@ public void UseOtlpExporterDefaultTest() } [Fact] - public void UseOtlpExporterSetEndpointTest() + public void UseOtlpExporterSetProtocolTest() { var services = new ServiceCollection(); services.AddOpenTelemetry() - .UseOtlpExporter(new Uri("http://test_base_endpoint/")); + .UseOtlpExporter(OtlpExportProtocol.HttpProtobuf); using var sp = services.BuildServiceProvider(); var exporterOptions = sp.GetRequiredService>().CurrentValue; - Assert.Equal(new Uri("http://test_base_endpoint/"), exporterOptions.DefaultOptions.Endpoint); - Assert.Equal(OtlpExporterOptions.DefaultOtlpExportProtocol, exporterOptions.DefaultOptions.Protocol); + Assert.Equal(OtlpExportProtocol.HttpProtobuf, exporterOptions.DefaultOptions.Protocol); + Assert.Equal(new(OtlpExporterOptions.DefaultHttpEndpoint), exporterOptions.DefaultOptions.Endpoint); Assert.True(((OtlpExporterOptions)exporterOptions.DefaultOptions).HasData); Assert.False(((OtlpExporterOptions)exporterOptions.LoggingOptions).HasData); @@ -73,13 +73,16 @@ public void UseOtlpExporterSetEndpointAndProtocolTest() var exporterOptions = sp.GetRequiredService>().CurrentValue; - Assert.Equal(new Uri("http://test_base_endpoint/"), exporterOptions.DefaultOptions.Endpoint); Assert.Equal(OtlpExportProtocol.HttpProtobuf, exporterOptions.DefaultOptions.Protocol); + Assert.Equal(new Uri("http://test_base_endpoint/"), exporterOptions.DefaultOptions.Endpoint); Assert.True(((OtlpExporterOptions)exporterOptions.DefaultOptions).HasData); Assert.False(((OtlpExporterOptions)exporterOptions.LoggingOptions).HasData); Assert.False(((OtlpExporterOptions)exporterOptions.MetricsOptions).HasData); Assert.False(((OtlpExporterOptions)exporterOptions.TracingOptions).HasData); + + Assert.Throws( + () => services.AddOpenTelemetry().UseOtlpExporter(OtlpExportProtocol.HttpProtobuf, baseEndpoint: null!)); } [Theory] @@ -92,19 +95,17 @@ public void UseOtlpExporterConfigureTest(string? name) if (!string.IsNullOrEmpty(name)) { services.AddOpenTelemetry() - .UseOtlpExporter(name: name, configure: Configure); + .UseOtlpExporter(name, configuration: null, configure: Configure); } else { services.AddOpenTelemetry() .UseOtlpExporter(Configure); - - name = "otlp"; } using var sp = services.BuildServiceProvider(); - VerifyOptionsApplied(sp, name!); + VerifyOptionsApplied(sp, name); static void Configure(OtlpExporterBuilder builder) { @@ -166,7 +167,7 @@ public void UseOtlpExporterConfigurationTest(string? name) if (!string.IsNullOrEmpty(name)) { services.AddOpenTelemetry() - .UseOtlpExporter(name: name, configuration: config); + .UseOtlpExporter(name: name, configuration: config, configure: null); } else { @@ -178,7 +179,7 @@ public void UseOtlpExporterConfigurationTest(string? name) using var sp = services.BuildServiceProvider(); - VerifyOptionsApplied(sp, name!); + VerifyOptionsApplied(sp, name); } [Fact] @@ -304,7 +305,7 @@ public void UseOtlpExporterAddsLoggingProcessorToPipelineEndTest() Assert.True(processors[1] is BatchLogRecordExportProcessor); } - private static void VerifyOptionsApplied(ServiceProvider serviceProvider, string name) + private static void VerifyOptionsApplied(ServiceProvider serviceProvider, string? name) { var exporterOptions = serviceProvider.GetRequiredService>().Get(name); From c4fe57c083f0b39d67b6bde603262d0494debaf8 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Wed, 13 Mar 2024 10:48:18 -0700 Subject: [PATCH 38/48] Assert fix. --- .../Builder/OtlpExporterBuilder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OtlpExporterBuilder.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OtlpExporterBuilder.cs index 30f0d9cf2a2..0a883951377 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OtlpExporterBuilder.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OtlpExporterBuilder.cs @@ -169,7 +169,7 @@ private static void BindConfigurationToOptions(IServiceCollection services, stri private static void RegisterOtlpExporterServices(IServiceCollection services, string name) { Debug.Assert(services != null, "services was null"); - Debug.Assert(!string.IsNullOrEmpty(name), "name was null or empty"); + Debug.Assert(name != null, "name was null"); // Note: UseOtlpExporterRegistration is added to the service collection // to detect repeated calls to "UseOtlpExporter" and to throw if From 084188a11e98bdd12965bd2f65184598bb05e0ef Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Wed, 13 Mar 2024 10:48:30 -0700 Subject: [PATCH 39/48] Add XML docs to capture current naming behaviors. --- ...nTelemetryBuilderOtlpExporterExtensions.cs | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderOtlpExporterExtensions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderOtlpExporterExtensions.cs index 6bae5971909..1727e970393 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderOtlpExporterExtensions.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderOtlpExporterExtensions.cs @@ -70,6 +70,11 @@ public static IOpenTelemetryBuilder UseOtlpExporter( return UseOtlpExporterWithProtocolAndBaseEndpoint(builder, protocol, baseEndpoint); } + /// + /// + /// + /// . + /// Callback action for configuring . internal static IOpenTelemetryBuilder UseOtlpExporter( this IOpenTelemetryBuilder builder, Action configure) @@ -79,6 +84,21 @@ internal static IOpenTelemetryBuilder UseOtlpExporter( return UseOtlpExporter(builder, name: null, configuration: null, configure); } + /// + /// + /// + /// . + /// + /// to bind onto . + /// Notes: + /// + /// See [TODO:Add doc link] for details on the configuration + /// schema. + /// The instance will be + /// named "otlp" by default when calling this method. + /// + /// + /// internal static IOpenTelemetryBuilder UseOtlpExporter( this IOpenTelemetryBuilder builder, IConfiguration configuration) @@ -88,6 +108,26 @@ internal static IOpenTelemetryBuilder UseOtlpExporter( return UseOtlpExporter(builder, name: null, configuration, configure: null); } + /// + /// + /// + /// . + /// Optional name which is used when retrieving options. + /// + /// Optional to bind onto . + /// Notes: + /// + /// + /// If is not set the instance will be named "otlp" by + /// default when is used. + /// + /// + /// + /// Optional callback action for configuring . internal static IOpenTelemetryBuilder UseOtlpExporter( this IOpenTelemetryBuilder builder, string? name, From c0d8ba3a1aa3517da910cf13986fdea3b21a8c46 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Wed, 13 Mar 2024 11:32:10 -0700 Subject: [PATCH 40/48] Tweak namespace. --- .../.publicApi/Stable/PublicAPI.Unshipped.txt | 8 ++++---- .../Builder/OpenTelemetryBuilderOtlpExporterExtensions.cs | 3 ++- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/.publicApi/Stable/PublicAPI.Unshipped.txt b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/.publicApi/Stable/PublicAPI.Unshipped.txt index dbc6de8075b..22284b6af9f 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/.publicApi/Stable/PublicAPI.Unshipped.txt +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/.publicApi/Stable/PublicAPI.Unshipped.txt @@ -1,4 +1,4 @@ -OpenTelemetry.Exporter.OpenTelemetryBuilderOtlpExporterExtensions -static OpenTelemetry.Exporter.OpenTelemetryBuilderOtlpExporterExtensions.UseOtlpExporter(this OpenTelemetry.IOpenTelemetryBuilder! builder) -> OpenTelemetry.IOpenTelemetryBuilder! -static OpenTelemetry.Exporter.OpenTelemetryBuilderOtlpExporterExtensions.UseOtlpExporter(this OpenTelemetry.IOpenTelemetryBuilder! builder, OpenTelemetry.Exporter.OtlpExportProtocol protocol, System.Uri! baseEndpoint) -> OpenTelemetry.IOpenTelemetryBuilder! -static OpenTelemetry.Exporter.OpenTelemetryBuilderOtlpExporterExtensions.UseOtlpExporter(this OpenTelemetry.IOpenTelemetryBuilder! builder, OpenTelemetry.Exporter.OtlpExportProtocol protocol) -> OpenTelemetry.IOpenTelemetryBuilder! +OpenTelemetry.OpenTelemetryBuilderOtlpExporterExtensions +static OpenTelemetry.OpenTelemetryBuilderOtlpExporterExtensions.UseOtlpExporter(this OpenTelemetry.IOpenTelemetryBuilder! builder) -> OpenTelemetry.IOpenTelemetryBuilder! +static OpenTelemetry.OpenTelemetryBuilderOtlpExporterExtensions.UseOtlpExporter(this OpenTelemetry.IOpenTelemetryBuilder! builder, OpenTelemetry.Exporter.OtlpExportProtocol protocol, System.Uri! baseEndpoint) -> OpenTelemetry.IOpenTelemetryBuilder! +static OpenTelemetry.OpenTelemetryBuilderOtlpExporterExtensions.UseOtlpExporter(this OpenTelemetry.IOpenTelemetryBuilder! builder, OpenTelemetry.Exporter.OtlpExportProtocol protocol) -> OpenTelemetry.IOpenTelemetryBuilder! diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderOtlpExporterExtensions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderOtlpExporterExtensions.cs index 1727e970393..d43b9797a19 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderOtlpExporterExtensions.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderOtlpExporterExtensions.cs @@ -4,9 +4,10 @@ #nullable enable using Microsoft.Extensions.Configuration; +using OpenTelemetry.Exporter; using OpenTelemetry.Internal; -namespace OpenTelemetry.Exporter; +namespace OpenTelemetry; /// /// Contains extension methods to facilitate registration of the OpenTelemetry From c11e4a2468c13b8ceb28011ba4d4a0631262b501 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Wed, 13 Mar 2024 11:57:58 -0700 Subject: [PATCH 41/48] Warning cleanup. --- .../Builder/OtlpExporterBuilder.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OtlpExporterBuilder.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OtlpExporterBuilder.cs index 0a883951377..19051898a8e 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OtlpExporterBuilder.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OtlpExporterBuilder.cs @@ -195,7 +195,7 @@ there but that may change in the future so it is handled services!.ConfigureOpenTelemetryLoggerProvider( (sp, logging) => { - var builderOptions = GetBuilderOptionsAndValidateRegistrations(sp, name); + var builderOptions = GetBuilderOptionsAndValidateRegistrations(sp, name!); var processor = OtlpLogExporterHelperExtensions.BuildOtlpLogExporter( sp, @@ -213,7 +213,7 @@ there but that may change in the future so it is handled services!.ConfigureOpenTelemetryMeterProvider( (sp, metrics) => { - var builderOptions = GetBuilderOptionsAndValidateRegistrations(sp, name); + var builderOptions = GetBuilderOptionsAndValidateRegistrations(sp, name!); metrics.AddReader( OtlpMetricExporterExtensions.BuildOtlpExporterMetricReader( @@ -226,7 +226,7 @@ there but that may change in the future so it is handled services!.ConfigureOpenTelemetryTracerProvider( (sp, tracing) => { - var builderOptions = GetBuilderOptionsAndValidateRegistrations(sp, name); + var builderOptions = GetBuilderOptionsAndValidateRegistrations(sp, name!); var processorOptions = builderOptions.ActivityExportProcessorOptions ?? throw new InvalidOperationException("ActivityExportProcessorOptions were missing with tracing enabled"); From 4f06ab89c99fe6f90c8b5b64032d6a3d4743d80f Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Wed, 13 Mar 2024 14:09:14 -0700 Subject: [PATCH 42/48] Code review. --- .../Builder/OpenTelemetryBuilderServiceProviderExtensions.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderServiceProviderExtensions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderServiceProviderExtensions.cs index 353cebb3d9b..afa534f4160 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderServiceProviderExtensions.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderServiceProviderExtensions.cs @@ -2,9 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 using Microsoft.Extensions.DependencyInjection; -using OpenTelemetry.Exporter; -namespace System; +namespace OpenTelemetry.Exporter; internal static class OpenTelemetryBuilderServiceProviderExtensions { From cd7ca4e0b9b3419c3b24b19369ce434616ccbb54 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Wed, 13 Mar 2024 14:15:00 -0700 Subject: [PATCH 43/48] Remove UseOtlpExporter overload which only changes protocol. --- .../.publicApi/Stable/PublicAPI.Unshipped.txt | 1 - ...nTelemetryBuilderOtlpExporterExtensions.cs | 37 ++++--------------- .../UseOtlpExporterExtensionTests.cs | 21 ----------- 3 files changed, 8 insertions(+), 51 deletions(-) diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/.publicApi/Stable/PublicAPI.Unshipped.txt b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/.publicApi/Stable/PublicAPI.Unshipped.txt index 22284b6af9f..b0659b798bb 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/.publicApi/Stable/PublicAPI.Unshipped.txt +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/.publicApi/Stable/PublicAPI.Unshipped.txt @@ -1,4 +1,3 @@ OpenTelemetry.OpenTelemetryBuilderOtlpExporterExtensions static OpenTelemetry.OpenTelemetryBuilderOtlpExporterExtensions.UseOtlpExporter(this OpenTelemetry.IOpenTelemetryBuilder! builder) -> OpenTelemetry.IOpenTelemetryBuilder! static OpenTelemetry.OpenTelemetryBuilderOtlpExporterExtensions.UseOtlpExporter(this OpenTelemetry.IOpenTelemetryBuilder! builder, OpenTelemetry.Exporter.OtlpExportProtocol protocol, System.Uri! baseEndpoint) -> OpenTelemetry.IOpenTelemetryBuilder! -static OpenTelemetry.OpenTelemetryBuilderOtlpExporterExtensions.UseOtlpExporter(this OpenTelemetry.IOpenTelemetryBuilder! builder, OpenTelemetry.Exporter.OtlpExportProtocol protocol) -> OpenTelemetry.IOpenTelemetryBuilder! diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderOtlpExporterExtensions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderOtlpExporterExtensions.cs index d43b9797a19..c0368cd4ae1 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderOtlpExporterExtensions.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderOtlpExporterExtensions.cs @@ -40,16 +40,6 @@ public static IOpenTelemetryBuilder UseOtlpExporter( this IOpenTelemetryBuilder builder) => UseOtlpExporter(builder, name: null, configuration: null, configure: null); - /// - /// - /// - /// . - /// . - public static IOpenTelemetryBuilder UseOtlpExporter( - this IOpenTelemetryBuilder builder, - OtlpExportProtocol protocol) - => UseOtlpExporterWithProtocolAndBaseEndpoint(builder, protocol, baseEndpoint: null); - /// /// /// @@ -68,7 +58,14 @@ public static IOpenTelemetryBuilder UseOtlpExporter( { Guard.ThrowIfNull(baseEndpoint); - return UseOtlpExporterWithProtocolAndBaseEndpoint(builder, protocol, baseEndpoint); + return UseOtlpExporter(builder, name: null, configuration: null, configure: otlpBuilder => + { + otlpBuilder.ConfigureDefaultExporterOptions(o => + { + o.Protocol = protocol; + o.Endpoint = baseEndpoint; + }); + }); } /// @@ -149,22 +146,4 @@ internal static IOpenTelemetryBuilder UseOtlpExporter( return builder; } - - private static IOpenTelemetryBuilder UseOtlpExporterWithProtocolAndBaseEndpoint( - this IOpenTelemetryBuilder builder, - OtlpExportProtocol protocol, - Uri? baseEndpoint) - { - return UseOtlpExporter(builder, name: null, configuration: null, configure: otlpBuilder => - { - otlpBuilder.ConfigureDefaultExporterOptions(o => - { - o.Protocol = protocol; - if (baseEndpoint != null) - { - o.Endpoint = baseEndpoint; - } - }); - }); - } } diff --git a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/UseOtlpExporterExtensionTests.cs b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/UseOtlpExporterExtensionTests.cs index 9b3846b16ba..b39f5ee0a95 100644 --- a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/UseOtlpExporterExtensionTests.cs +++ b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/UseOtlpExporterExtensionTests.cs @@ -38,27 +38,6 @@ public void UseOtlpExporterDefaultTest() Assert.False(((OtlpExporterOptions)exporterOptions.TracingOptions).HasData); } - [Fact] - public void UseOtlpExporterSetProtocolTest() - { - var services = new ServiceCollection(); - - services.AddOpenTelemetry() - .UseOtlpExporter(OtlpExportProtocol.HttpProtobuf); - - using var sp = services.BuildServiceProvider(); - - var exporterOptions = sp.GetRequiredService>().CurrentValue; - - Assert.Equal(OtlpExportProtocol.HttpProtobuf, exporterOptions.DefaultOptions.Protocol); - Assert.Equal(new(OtlpExporterOptions.DefaultHttpEndpoint), exporterOptions.DefaultOptions.Endpoint); - Assert.True(((OtlpExporterOptions)exporterOptions.DefaultOptions).HasData); - - Assert.False(((OtlpExporterOptions)exporterOptions.LoggingOptions).HasData); - Assert.False(((OtlpExporterOptions)exporterOptions.MetricsOptions).HasData); - Assert.False(((OtlpExporterOptions)exporterOptions.TracingOptions).HasData); - } - [Fact] public void UseOtlpExporterSetEndpointAndProtocolTest() { From 01dba400c999ee717fe47e3582c13d0dff542e37 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Wed, 13 Mar 2024 14:25:49 -0700 Subject: [PATCH 44/48] Code review. --- .../UseOtlpExporterExtensionTests.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/UseOtlpExporterExtensionTests.cs b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/UseOtlpExporterExtensionTests.cs index b39f5ee0a95..81c1f44a7bb 100644 --- a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/UseOtlpExporterExtensionTests.cs +++ b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/UseOtlpExporterExtensionTests.cs @@ -38,21 +38,23 @@ public void UseOtlpExporterDefaultTest() Assert.False(((OtlpExporterOptions)exporterOptions.TracingOptions).HasData); } - [Fact] - public void UseOtlpExporterSetEndpointAndProtocolTest() + [Theory] + [InlineData(OtlpExportProtocol.Grpc)] + [InlineData(OtlpExportProtocol.HttpProtobuf)] + public void UseOtlpExporterSetEndpointAndProtocolTest(OtlpExportProtocol protocol) { var services = new ServiceCollection(); services.AddOpenTelemetry() .UseOtlpExporter( - OtlpExportProtocol.HttpProtobuf, + protocol, new Uri("http://test_base_endpoint/")); using var sp = services.BuildServiceProvider(); var exporterOptions = sp.GetRequiredService>().CurrentValue; - Assert.Equal(OtlpExportProtocol.HttpProtobuf, exporterOptions.DefaultOptions.Protocol); + Assert.Equal(protocol, exporterOptions.DefaultOptions.Protocol); Assert.Equal(new Uri("http://test_base_endpoint/"), exporterOptions.DefaultOptions.Endpoint); Assert.True(((OtlpExporterOptions)exporterOptions.DefaultOptions).HasData); From 1c3258c115f5d6f8f6e449d358652279c8bac727 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Wed, 13 Mar 2024 15:58:29 -0700 Subject: [PATCH 45/48] Cleanup and refactoring. --- .../Builder/OtlpExporterBuilder.cs | 4 ++-- .../OtlpServiceCollectionExtensions.cs | 21 +++++++++++++++++++ .../OtlpExporterOptions.cs | 7 ------- .../OtlpLogExporterHelperExtensions.cs | 10 ++------- .../OtlpMetricExporterExtensions.cs | 5 +++-- .../OtlpTraceExporterHelperExtensions.cs | 4 ++-- 6 files changed, 30 insertions(+), 21 deletions(-) create mode 100644 src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/OtlpServiceCollectionExtensions.cs diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OtlpExporterBuilder.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OtlpExporterBuilder.cs index bbc599e6e46..31d48c1cbcd 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OtlpExporterBuilder.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OtlpExporterBuilder.cs @@ -171,13 +171,13 @@ private static void RegisterOtlpExporterServices(IServiceCollection services, st Debug.Assert(services != null, "services was null"); Debug.Assert(name != null, "name was null"); + services.AddOtlpExporterSharedServices(); + // Note: UseOtlpExporterRegistration is added to the service collection // to detect repeated calls to "UseOtlpExporter" and to throw if // "AddOtlpExporter" extensions are called services!.AddSingleton(); - services!.RegisterOptionsFactory(configuration => new SdkLimitOptions(configuration)); - services!.RegisterOptionsFactory(configuration => new ExperimentalOptions(configuration)); services!.RegisterOptionsFactory((sp, configuration, name) => new OtlpExporterBuilderOptions( configuration, sp.GetRequiredService>().CurrentValue, diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/OtlpServiceCollectionExtensions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/OtlpServiceCollectionExtensions.cs new file mode 100644 index 00000000000..6584dabc8e3 --- /dev/null +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/OtlpServiceCollectionExtensions.cs @@ -0,0 +1,21 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +using System.Diagnostics; +using Microsoft.Extensions.DependencyInjection; +using OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation; +using OpenTelemetry.Internal; + +namespace OpenTelemetry.Exporter; + +internal static class OtlpServiceCollectionExtensions +{ + public static void AddOtlpExporterSharedServices(this IServiceCollection services) + { + Debug.Assert(services != null, "services was null"); + + services.RegisterOptionsFactory(OtlpExporterOptions.CreateOtlpExporterOptions); + services.RegisterOptionsFactory(configuration => new SdkLimitOptions(configuration)); + services.RegisterOptionsFactory(configuration => new ExperimentalOptions(configuration)); + } +} diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterOptions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterOptions.cs index 4273c04f56a..2a49efb1db7 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterOptions.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterOptions.cs @@ -11,7 +11,6 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; -using OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation; using OpenTelemetry.Internal; using OpenTelemetry.Trace; @@ -163,12 +162,6 @@ internal bool HasData || this.timeoutMilliseconds.HasValue || this.httpClientFactory != null; - internal static void RegisterOtlpExporterOptionsFactory(IServiceCollection services) - { - services.RegisterOptionsFactory(CreateOtlpExporterOptions); - services.RegisterOptionsFactory(configuration => new ExperimentalOptions(configuration)); - } - internal static OtlpExporterOptions CreateOtlpExporterOptions( IServiceProvider serviceProvider, IConfiguration configuration, diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpLogExporterHelperExtensions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpLogExporterHelperExtensions.cs index 6336cca142d..144c9d9b6de 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpLogExporterHelperExtensions.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpLogExporterHelperExtensions.cs @@ -224,7 +224,7 @@ static LoggerProviderBuilder AddOtlpExporter( services.Configure(finalOptionsName, configureExporter); } - RegisterOptions(services); + services.AddOtlpExporterSharedServices(); }); return builder.AddProcessor(sp => @@ -299,7 +299,7 @@ static LoggerProviderBuilder AddOtlpExporter( { var finalOptionsName = name ?? Options.DefaultName; - builder.ConfigureServices(RegisterOptions); + builder.ConfigureServices(services => services.AddOtlpExporterSharedServices()); return builder.AddProcessor(sp => { @@ -404,12 +404,6 @@ internal static BaseProcessor BuildOtlpLogExporter( } } - private static void RegisterOptions(IServiceCollection services) - { - OtlpExporterOptions.RegisterOtlpExporterOptionsFactory(services); - services.RegisterOptionsFactory(configuration => new SdkLimitOptions(configuration)); - } - private static T GetOptions( IServiceProvider sp, string? name, diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpMetricExporterExtensions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpMetricExporterExtensions.cs index f91b2b69d8d..7f26603df30 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpMetricExporterExtensions.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpMetricExporterExtensions.cs @@ -60,7 +60,7 @@ public static MeterProviderBuilder AddOtlpExporter( services.Configure(finalOptionsName, configure); } - OtlpExporterOptions.RegisterOtlpExporterOptionsFactory(services); + services.AddOtlpExporterSharedServices(); services.AddOptions(finalOptionsName).Configure( (readerOptions, config) => @@ -138,7 +138,7 @@ public static MeterProviderBuilder AddOtlpExporter( builder.ConfigureServices(services => { - OtlpExporterOptions.RegisterOtlpExporterOptionsFactory(services); + services.AddOtlpExporterSharedServices(); services.AddOptions(finalOptionsName).Configure( (readerOptions, config) => @@ -191,6 +191,7 @@ internal static MetricReader BuildOtlpExporterMetricReader( Debug.Assert(serviceProvider != null, "serviceProvider was null"); Debug.Assert(exporterOptions != null, "exporterOptions was null"); Debug.Assert(metricReaderOptions != null, "metricReaderOptions was null"); + Debug.Assert(experimentalOptions != null, "experimentalOptions was null"); if (!skipUseOtlpExporterRegistrationCheck) { diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpTraceExporterHelperExtensions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpTraceExporterHelperExtensions.cs index 6acfa6eed1d..88e56037154 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpTraceExporterHelperExtensions.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpTraceExporterHelperExtensions.cs @@ -59,8 +59,7 @@ public static TracerProviderBuilder AddOtlpExporter( services.Configure(finalOptionsName, configure); } - OtlpExporterOptions.RegisterOtlpExporterOptionsFactory(services); - services.RegisterOptionsFactory(configuration => new SdkLimitOptions(configuration)); + services.AddOtlpExporterSharedServices(); }); return builder.AddProcessor(sp => @@ -129,6 +128,7 @@ internal static BaseProcessor BuildOtlpExporterProcessor( Debug.Assert(serviceProvider != null, "serviceProvider was null"); Debug.Assert(exporterOptions != null, "exporterOptions was null"); Debug.Assert(sdkLimitOptions != null, "sdkLimitOptions was null"); + Debug.Assert(experimentalOptions != null, "experimentalOptions was null"); Debug.Assert(batchExportProcessorOptions != null, "batchExportProcessorOptions was null"); if (!skipUseOtlpExporterRegistrationCheck) From ed09c06a5b3444bdf4ce3e200ad332a45eda6cec Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Wed, 13 Mar 2024 16:46:21 -0700 Subject: [PATCH 46/48] Code review. --- .../Builder/OtlpExporterBuilder.cs | 7 ++- .../OtlpServiceCollectionExtensions.cs | 45 ++++++++++++++++++- .../OtlpLogExporterHelperExtensions.cs | 4 +- .../OtlpMetricExporterExtensions.cs | 27 +---------- .../OtlpTraceExporterHelperExtensions.cs | 2 +- 5 files changed, 54 insertions(+), 31 deletions(-) diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OtlpExporterBuilder.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OtlpExporterBuilder.cs index 31d48c1cbcd..4c3d37cf282 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OtlpExporterBuilder.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OtlpExporterBuilder.cs @@ -171,7 +171,9 @@ private static void RegisterOtlpExporterServices(IServiceCollection services, st Debug.Assert(services != null, "services was null"); Debug.Assert(name != null, "name was null"); - services.AddOtlpExporterSharedServices(); + services!.AddOtlpExporterLoggingServices(); + services!.AddOtlpExporterMetricsServices(name!); + services!.AddOtlpExporterTracingServices(); // Note: UseOtlpExporterRegistration is added to the service collection // to detect repeated calls to "UseOtlpExporter" and to throw if @@ -180,6 +182,9 @@ private static void RegisterOtlpExporterServices(IServiceCollection services, st services!.RegisterOptionsFactory((sp, configuration, name) => new OtlpExporterBuilderOptions( configuration, + /* Note: We don't use name for SdkLimitOptions. There should only be + one provider for a given service collection so SdkLimitOptions is + treated as a single default instance. */ sp.GetRequiredService>().CurrentValue, sp.GetRequiredService>().Get(name), /* Note: We allow LogRecordExportProcessorOptions, diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/OtlpServiceCollectionExtensions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/OtlpServiceCollectionExtensions.cs index 6584dabc8e3..9aeaa7bf7df 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/OtlpServiceCollectionExtensions.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/OtlpServiceCollectionExtensions.cs @@ -1,21 +1,62 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 +#nullable enable + using System.Diagnostics; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation; using OpenTelemetry.Internal; +using OpenTelemetry.Metrics; namespace OpenTelemetry.Exporter; internal static class OtlpServiceCollectionExtensions { - public static void AddOtlpExporterSharedServices(this IServiceCollection services) + public static void AddOtlpExporterLoggingServices(this IServiceCollection services) + { + Debug.Assert(services != null, "services was null"); + + AddOtlpExporterSharedServices(services!, registerSdkLimitOptions: true); + } + + public static void AddOtlpExporterMetricsServices(this IServiceCollection services, string name) + { + Debug.Assert(services != null, "services was null"); + Debug.Assert(name != null, "name was null"); + + AddOtlpExporterSharedServices(services!, registerSdkLimitOptions: false); + + services!.AddOptions(name).Configure( + (readerOptions, config) => + { + var otlpTemporalityPreference = config[OtlpSpecConfigDefinitions.MetricsTemporalityPreferenceEnvVarName]; + if (!string.IsNullOrWhiteSpace(otlpTemporalityPreference) + && Enum.TryParse(otlpTemporalityPreference, ignoreCase: true, out var enumValue)) + { + readerOptions.TemporalityPreference = enumValue; + } + }); + } + + public static void AddOtlpExporterTracingServices(this IServiceCollection services) { Debug.Assert(services != null, "services was null"); + AddOtlpExporterSharedServices(services!, registerSdkLimitOptions: true); + } + + private static void AddOtlpExporterSharedServices( + IServiceCollection services, + bool registerSdkLimitOptions) + { services.RegisterOptionsFactory(OtlpExporterOptions.CreateOtlpExporterOptions); - services.RegisterOptionsFactory(configuration => new SdkLimitOptions(configuration)); services.RegisterOptionsFactory(configuration => new ExperimentalOptions(configuration)); + + if (registerSdkLimitOptions) + { + services.RegisterOptionsFactory(configuration => new SdkLimitOptions(configuration)); + } } } diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpLogExporterHelperExtensions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpLogExporterHelperExtensions.cs index 144c9d9b6de..ca5a759caf4 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpLogExporterHelperExtensions.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpLogExporterHelperExtensions.cs @@ -224,7 +224,7 @@ static LoggerProviderBuilder AddOtlpExporter( services.Configure(finalOptionsName, configureExporter); } - services.AddOtlpExporterSharedServices(); + services.AddOtlpExporterLoggingServices(); }); return builder.AddProcessor(sp => @@ -299,7 +299,7 @@ static LoggerProviderBuilder AddOtlpExporter( { var finalOptionsName = name ?? Options.DefaultName; - builder.ConfigureServices(services => services.AddOtlpExporterSharedServices()); + builder.ConfigureServices(services => services.AddOtlpExporterLoggingServices()); return builder.AddProcessor(sp => { diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpMetricExporterExtensions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpMetricExporterExtensions.cs index 7f26603df30..57ad3dd47ec 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpMetricExporterExtensions.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpMetricExporterExtensions.cs @@ -4,7 +4,6 @@ #nullable enable using System.Diagnostics; -using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using OpenTelemetry.Exporter; @@ -60,18 +59,7 @@ public static MeterProviderBuilder AddOtlpExporter( services.Configure(finalOptionsName, configure); } - services.AddOtlpExporterSharedServices(); - - services.AddOptions(finalOptionsName).Configure( - (readerOptions, config) => - { - var otlpTemporalityPreference = config[OtlpSpecConfigDefinitions.MetricsTemporalityPreferenceEnvVarName]; - if (!string.IsNullOrWhiteSpace(otlpTemporalityPreference) - && Enum.TryParse(otlpTemporalityPreference, ignoreCase: true, out var enumValue)) - { - readerOptions.TemporalityPreference = enumValue; - } - }); + services.AddOtlpExporterMetricsServices(finalOptionsName); }); return builder.AddReader(sp => @@ -138,18 +126,7 @@ public static MeterProviderBuilder AddOtlpExporter( builder.ConfigureServices(services => { - services.AddOtlpExporterSharedServices(); - - services.AddOptions(finalOptionsName).Configure( - (readerOptions, config) => - { - var otlpTemporalityPreference = config[OtlpSpecConfigDefinitions.MetricsTemporalityPreferenceEnvVarName]; - if (!string.IsNullOrWhiteSpace(otlpTemporalityPreference) - && Enum.TryParse(otlpTemporalityPreference, ignoreCase: true, out var enumValue)) - { - readerOptions.TemporalityPreference = enumValue; - } - }); + services.AddOtlpExporterMetricsServices(finalOptionsName); }); return builder.AddReader(sp => diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpTraceExporterHelperExtensions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpTraceExporterHelperExtensions.cs index 88e56037154..036e9a90dd3 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpTraceExporterHelperExtensions.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpTraceExporterHelperExtensions.cs @@ -59,7 +59,7 @@ public static TracerProviderBuilder AddOtlpExporter( services.Configure(finalOptionsName, configure); } - services.AddOtlpExporterSharedServices(); + services.AddOtlpExporterTracingServices(); }); return builder.AddProcessor(sp => From 31f77593774686c1a7a9754a81c1e4deae09a743 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Thu, 14 Mar 2024 12:05:56 -0700 Subject: [PATCH 47/48] Refactor spec env var code and add test coverage for UseOtlpExporter extension. --- .../OtlpExporterOptionsTests.cs | 125 +------ .../OtlpMetricsExporterTests.cs | 70 ++-- .../OtlpSpecConfigDefinitionTests.cs | 314 ++++++++++++++++++ .../UseOtlpExporterExtensionTests.cs | 60 +++- 4 files changed, 432 insertions(+), 137 deletions(-) create mode 100644 test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpSpecConfigDefinitionTests.cs diff --git a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpExporterOptionsTests.cs b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpExporterOptionsTests.cs index 0ba2658482d..e1c59448628 100644 --- a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpExporterOptionsTests.cs +++ b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpExporterOptionsTests.cs @@ -10,56 +10,12 @@ public class OtlpExporterOptionsTests : IDisposable { public OtlpExporterOptionsTests() { - ClearEnvVars(); - } - - public static IEnumerable GetOtlpExporterOptionsTestCases() - { - yield return new object[] - { - OtlpExporterOptionsConfigurationType.Default, - OtlpSpecConfigDefinitions.DefaultEndpointEnvVarName, - OtlpSpecConfigDefinitions.DefaultHeadersEnvVarName, - OtlpSpecConfigDefinitions.DefaultTimeoutEnvVarName, - OtlpSpecConfigDefinitions.DefaultProtocolEnvVarName, - true, - }; - - yield return new object[] - { - OtlpExporterOptionsConfigurationType.Logs, - OtlpSpecConfigDefinitions.LogsEndpointEnvVarName, - OtlpSpecConfigDefinitions.LogsHeadersEnvVarName, - OtlpSpecConfigDefinitions.LogsTimeoutEnvVarName, - OtlpSpecConfigDefinitions.LogsProtocolEnvVarName, - false, - }; - - yield return new object[] - { - OtlpExporterOptionsConfigurationType.Metrics, - OtlpSpecConfigDefinitions.MetricsEndpointEnvVarName, - OtlpSpecConfigDefinitions.MetricsHeadersEnvVarName, - OtlpSpecConfigDefinitions.MetricsTimeoutEnvVarName, - OtlpSpecConfigDefinitions.MetricsProtocolEnvVarName, - false, - }; - - yield return new object[] - { - OtlpExporterOptionsConfigurationType.Traces, - OtlpSpecConfigDefinitions.TracesEndpointEnvVarName, - OtlpSpecConfigDefinitions.TracesHeadersEnvVarName, - OtlpSpecConfigDefinitions.TracesTimeoutEnvVarName, - OtlpSpecConfigDefinitions.TracesProtocolEnvVarName, - false, - }; + OtlpSpecConfigDefinitionTests.ClearEnvVars(); } public void Dispose() { - ClearEnvVars(); - GC.SuppressFinalize(this); + OtlpSpecConfigDefinitionTests.ClearEnvVars(); } [Fact] @@ -87,58 +43,31 @@ public void OtlpExporterOptions_DefaultsForHttpProtobuf() } [Theory] - [MemberData(nameof(GetOtlpExporterOptionsTestCases))] - public void OtlpExporterOptions_EnvironmentVariableOverride( - int configurationType, - string endpointEnvVarKeyName, - string headersEnvVarKeyName, - string timeoutEnvVarKeyName, - string protocolEnvVarKeyName, - bool appendSignalPathToEndpoint) + [ClassData(typeof(OtlpSpecConfigDefinitionTests))] + public void OtlpExporterOptions_EnvironmentVariableOverride(object testDataObject) { - Environment.SetEnvironmentVariable(endpointEnvVarKeyName, "http://test:8888"); - Environment.SetEnvironmentVariable(headersEnvVarKeyName, "A=2,B=3"); - Environment.SetEnvironmentVariable(timeoutEnvVarKeyName, "2000"); - Environment.SetEnvironmentVariable(protocolEnvVarKeyName, "http/protobuf"); + var testData = testDataObject as OtlpSpecConfigDefinitionTests.TestData; + Assert.NotNull(testData); - var options = new OtlpExporterOptions((OtlpExporterOptionsConfigurationType)configurationType); + testData.SetEnvVars(); - Assert.Equal(new Uri("http://test:8888"), options.Endpoint); - Assert.Equal("A=2,B=3", options.Headers); - Assert.Equal(2000, options.TimeoutMilliseconds); - Assert.Equal(OtlpExportProtocol.HttpProtobuf, options.Protocol); - Assert.Equal(appendSignalPathToEndpoint, options.AppendSignalPathToEndpoint); + var options = new OtlpExporterOptions(testData.ConfigurationType); + + testData.AssertMatches(options); } [Theory] - [MemberData(nameof(GetOtlpExporterOptionsTestCases))] - public void OtlpExporterOptions_UsingIConfiguration( - int configurationType, - string endpointEnvVarKeyName, - string headersEnvVarKeyName, - string timeoutEnvVarKeyName, - string protocolEnvVarKeyName, - bool appendSignalPathToEndpoint) + [ClassData(typeof(OtlpSpecConfigDefinitionTests))] + public void OtlpExporterOptions_UsingIConfiguration(object testDataObject) { - var values = new Dictionary() - { - [endpointEnvVarKeyName] = "http://test:8888", - [headersEnvVarKeyName] = "A=2,B=3", - [timeoutEnvVarKeyName] = "2000", - [protocolEnvVarKeyName] = "http/protobuf", - }; + var testData = testDataObject as OtlpSpecConfigDefinitionTests.TestData; + Assert.NotNull(testData); - var configuration = new ConfigurationBuilder() - .AddInMemoryCollection(values) - .Build(); + var configuration = testData.ToConfiguration(); - var options = new OtlpExporterOptions(configuration, (OtlpExporterOptionsConfigurationType)configurationType, new()); + var options = new OtlpExporterOptions(configuration, testData.ConfigurationType, new()); - Assert.Equal(new Uri("http://test:8888"), options.Endpoint); - Assert.Equal("A=2,B=3", options.Headers); - Assert.Equal(2000, options.TimeoutMilliseconds); - Assert.Equal(OtlpExportProtocol.HttpProtobuf, options.Protocol); - Assert.Equal(appendSignalPathToEndpoint, options.AppendSignalPathToEndpoint); + testData.AssertMatches(options); } [Fact] @@ -234,15 +163,6 @@ public void OtlpExporterOptions_EndpointThrowsWhenSetToNull() Assert.Equal(OtlpExportProtocol.Grpc, options.Protocol); } - [Fact] - public void OtlpExporterOptions_EnvironmentVariableNames() - { - Assert.Equal("OTEL_EXPORTER_OTLP_ENDPOINT", OtlpSpecConfigDefinitions.DefaultEndpointEnvVarName); - Assert.Equal("OTEL_EXPORTER_OTLP_HEADERS", OtlpSpecConfigDefinitions.DefaultHeadersEnvVarName); - Assert.Equal("OTEL_EXPORTER_OTLP_TIMEOUT", OtlpSpecConfigDefinitions.DefaultTimeoutEnvVarName); - Assert.Equal("OTEL_EXPORTER_OTLP_PROTOCOL", OtlpSpecConfigDefinitions.DefaultProtocolEnvVarName); - } - [Fact] public void OtlpExporterOptions_SettingEndpointToNullResetsAppendSignalPathToEndpoint() { @@ -306,15 +226,4 @@ public void OtlpExporterOptions_ApplyDefaultsTest() Assert.NotEqual(defaultOptionsWithData.TimeoutMilliseconds, targetOptionsWithData.TimeoutMilliseconds); Assert.NotEqual(defaultOptionsWithData.HttpClientFactory, targetOptionsWithData.HttpClientFactory); } - - private static void ClearEnvVars() - { - foreach (var item in GetOtlpExporterOptionsTestCases()) - { - Environment.SetEnvironmentVariable((string)item[1], null); - Environment.SetEnvironmentVariable((string)item[2], null); - Environment.SetEnvironmentVariable((string)item[3], null); - Environment.SetEnvironmentVariable((string)item[4], null); - } - } } diff --git a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpMetricsExporterTests.cs b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpMetricsExporterTests.cs index 451362ed927..e3d67bb1aba 100644 --- a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpMetricsExporterTests.cs +++ b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpMetricsExporterTests.cs @@ -727,47 +727,61 @@ public void TestHistogramToOtlpMetric(string name, string description, string un } [Theory] - [InlineData("cumulative", MetricReaderTemporalityPreference.Cumulative)] - [InlineData("Cumulative", MetricReaderTemporalityPreference.Cumulative)] - [InlineData("CUMULATIVE", MetricReaderTemporalityPreference.Cumulative)] - [InlineData("delta", MetricReaderTemporalityPreference.Delta)] - [InlineData("Delta", MetricReaderTemporalityPreference.Delta)] - [InlineData("DELTA", MetricReaderTemporalityPreference.Delta)] - public void TestTemporalityPreferenceConfiguration(string configValue, MetricReaderTemporalityPreference expectedTemporality) + [InlineData("cuMulative", MetricReaderTemporalityPreference.Cumulative)] + [InlineData("DeltA", MetricReaderTemporalityPreference.Delta)] + [InlineData("invalid", MetricReaderTemporalityPreference.Cumulative)] + public void TestTemporalityPreferenceUsingConfiguration(string configValue, MetricReaderTemporalityPreference expectedTemporality) { - var configData = new Dictionary { ["OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE"] = configValue }; + var testExecuted = false; + + var configData = new Dictionary { [OtlpSpecConfigDefinitionTests.MetricsData.TemporalityKeyName] = configValue }; var configuration = new ConfigurationBuilder() .AddInMemoryCollection(configData) .Build(); - // Check for both the code paths: - // 1. The final extension method which accepts `Action`. - // 2. The final extension method which accepts `Action`. + using var meterProvider = Sdk.CreateMeterProviderBuilder() + .ConfigureServices(services => + { + services.AddSingleton(configuration); - // Test 1st code path - using var meterProvider1 = Sdk.CreateMeterProviderBuilder() - .ConfigureServices(services => services.AddSingleton(configuration)) - .AddOtlpExporter() // This would in turn call the extension method which accepts `Action` + services.PostConfigure(o => + { + testExecuted = true; + Assert.Equal(expectedTemporality, o.TemporalityPreference); + }); + }) + .AddOtlpExporter() .Build(); - var assembly = typeof(Sdk).Assembly; - var type = assembly.GetType("OpenTelemetry.Metrics.MeterProviderSdk"); - var fieldInfo = type.GetField("reader", BindingFlags.Instance | BindingFlags.NonPublic); - var reader = fieldInfo.GetValue(meterProvider1) as MetricReader; - var temporality = reader.TemporalityPreference; + Assert.True(testExecuted); + } - Assert.Equal(expectedTemporality, temporality); + [Theory] + [InlineData("cuMulative", MetricReaderTemporalityPreference.Cumulative)] + [InlineData("DeltA", MetricReaderTemporalityPreference.Delta)] + [InlineData("invalid", MetricReaderTemporalityPreference.Cumulative)] + public void TestTemporalityPreferenceUsingEnvVar(string configValue, MetricReaderTemporalityPreference expectedTemporality) + { + Environment.SetEnvironmentVariable(OtlpSpecConfigDefinitionTests.MetricsData.TemporalityKeyName, null); + Environment.SetEnvironmentVariable(OtlpSpecConfigDefinitionTests.MetricsData.TemporalityKeyName, configValue); + + var testExecuted = false; - // Test 2nd code path - using var meterProvider2 = Sdk.CreateMeterProviderBuilder() - .ConfigureServices(services => services.AddSingleton(configuration)) - .AddOtlpExporter((_, _) => { }) // This would in turn call the extension method which accepts `Action` + using var meterProvider = Sdk.CreateMeterProviderBuilder() + .ConfigureServices(services => + { + services.PostConfigure(o => + { + testExecuted = true; + Assert.Equal(expectedTemporality, o.TemporalityPreference); + }); + }) + .AddOtlpExporter() .Build(); - reader = fieldInfo.GetValue(meterProvider2) as MetricReader; - temporality = reader.TemporalityPreference; + Assert.True(testExecuted); - Assert.Equal(expectedTemporality, temporality); + Environment.SetEnvironmentVariable(OtlpSpecConfigDefinitionTests.MetricsData.TemporalityKeyName, null); } [Theory] diff --git a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpSpecConfigDefinitionTests.cs b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpSpecConfigDefinitionTests.cs new file mode 100644 index 00000000000..4e4e5469183 --- /dev/null +++ b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpSpecConfigDefinitionTests.cs @@ -0,0 +1,314 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#nullable enable + +using System.Collections; +using Microsoft.Extensions.Configuration; +using OpenTelemetry.Metrics; +using Xunit; + +namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests; + +public class OtlpSpecConfigDefinitionTests : IEnumerable +{ + internal static TestData DefaultData { get; } = new TestData( + OtlpExporterOptionsConfigurationType.Default, + OtlpSpecConfigDefinitions.DefaultEndpointEnvVarName, + "http://default_endpoint/", + appendSignalPathToEndpoint: true, + OtlpSpecConfigDefinitions.DefaultHeadersEnvVarName, + "key1=value1", + OtlpSpecConfigDefinitions.DefaultTimeoutEnvVarName, + "1001", + OtlpSpecConfigDefinitions.DefaultProtocolEnvVarName, + "http/protobuf"); + + internal static TestData LoggingData { get; } = new TestData( + OtlpExporterOptionsConfigurationType.Logs, + OtlpSpecConfigDefinitions.LogsEndpointEnvVarName, + "http://logs_endpoint/", + appendSignalPathToEndpoint: false, + OtlpSpecConfigDefinitions.LogsHeadersEnvVarName, + "key2=value2", + OtlpSpecConfigDefinitions.LogsTimeoutEnvVarName, + "1002", + OtlpSpecConfigDefinitions.LogsProtocolEnvVarName, + "http/protobuf"); + + internal static MetricsTestData MetricsData { get; } = new MetricsTestData( + OtlpSpecConfigDefinitions.MetricsEndpointEnvVarName, + "http://metrics_endpoint/", + appendSignalPathToEndpoint: false, + OtlpSpecConfigDefinitions.MetricsHeadersEnvVarName, + "key3=value3", + OtlpSpecConfigDefinitions.MetricsTimeoutEnvVarName, + "1003", + OtlpSpecConfigDefinitions.MetricsProtocolEnvVarName, + "http/protobuf", + OtlpSpecConfigDefinitions.MetricsTemporalityPreferenceEnvVarName, + "Delta"); + + internal static TestData TracingData { get; } = new TestData( + OtlpExporterOptionsConfigurationType.Traces, + OtlpSpecConfigDefinitions.TracesEndpointEnvVarName, + "http://traces_endpoint/", + appendSignalPathToEndpoint: false, + OtlpSpecConfigDefinitions.TracesHeadersEnvVarName, + "key4=value4", + OtlpSpecConfigDefinitions.TracesTimeoutEnvVarName, + "1004", + OtlpSpecConfigDefinitions.TracesProtocolEnvVarName, + "http/protobuf"); + + [Fact] + public void VerifyKeyNamesMatchSpec() + { + Assert.Equal("OTEL_EXPORTER_OTLP_ENDPOINT", DefaultData.EndpointKeyName); + Assert.Equal("OTEL_EXPORTER_OTLP_HEADERS", DefaultData.HeadersKeyName); + Assert.Equal("OTEL_EXPORTER_OTLP_TIMEOUT", DefaultData.TimeoutKeyName); + Assert.Equal("OTEL_EXPORTER_OTLP_PROTOCOL", DefaultData.ProtocolKeyName); + + Assert.Equal("OTEL_EXPORTER_OTLP_LOGS_ENDPOINT", LoggingData.EndpointKeyName); + Assert.Equal("OTEL_EXPORTER_OTLP_LOGS_HEADERS", LoggingData.HeadersKeyName); + Assert.Equal("OTEL_EXPORTER_OTLP_LOGS_TIMEOUT", LoggingData.TimeoutKeyName); + Assert.Equal("OTEL_EXPORTER_OTLP_LOGS_PROTOCOL", LoggingData.ProtocolKeyName); + + Assert.Equal("OTEL_EXPORTER_OTLP_METRICS_ENDPOINT", MetricsData.EndpointKeyName); + Assert.Equal("OTEL_EXPORTER_OTLP_METRICS_HEADERS", MetricsData.HeadersKeyName); + Assert.Equal("OTEL_EXPORTER_OTLP_METRICS_TIMEOUT", MetricsData.TimeoutKeyName); + Assert.Equal("OTEL_EXPORTER_OTLP_METRICS_PROTOCOL", MetricsData.ProtocolKeyName); + Assert.Equal("OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE", MetricsData.TemporalityKeyName); + + Assert.Equal("OTEL_EXPORTER_OTLP_TRACES_ENDPOINT", TracingData.EndpointKeyName); + Assert.Equal("OTEL_EXPORTER_OTLP_TRACES_HEADERS", TracingData.HeadersKeyName); + Assert.Equal("OTEL_EXPORTER_OTLP_TRACES_TIMEOUT", TracingData.TimeoutKeyName); + Assert.Equal("OTEL_EXPORTER_OTLP_TRACES_PROTOCOL", TracingData.ProtocolKeyName); + } + + public IEnumerator GetEnumerator() + { + yield return new object[] + { + DefaultData, + }; + + yield return new object[] + { + LoggingData, + }; + + yield return new object[] + { + MetricsData, + }; + + yield return new object[] + { + TracingData, + }; + } + + IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator(); + + internal static IConfiguration ToConfiguration() + { + var configBuilder = new ConfigurationBuilder(); + + DefaultData.AddToConfiguration(configBuilder); + LoggingData.AddToConfiguration(configBuilder); + MetricsData.AddToConfiguration(configBuilder); + TracingData.AddToConfiguration(configBuilder); + + return configBuilder.Build(); + } + + internal static void SetEnvVars() + { + DefaultData.SetEnvVars(); + LoggingData.SetEnvVars(); + MetricsData.SetEnvVars(); + TracingData.SetEnvVars(); + } + + internal static void ClearEnvVars() + { + DefaultData.ClearEnvVars(); + LoggingData.ClearEnvVars(); + MetricsData.ClearEnvVars(); + TracingData.ClearEnvVars(); + } + + internal class TestData + { + public TestData( + OtlpExporterOptionsConfigurationType configurationType, + string endpointKeyName, + string endpointValue, + bool appendSignalPathToEndpoint, + string headersKeyName, + string headersValue, + string timeoutKeyName, + string timeoutValue, + string protocolKeyName, + string protocolValue) + { + this.ConfigurationType = configurationType; + this.EndpointKeyName = endpointKeyName; + this.EndpointValue = endpointValue; + this.AppendSignalPathToEndpoint = appendSignalPathToEndpoint; + this.HeadersKeyName = headersKeyName; + this.HeadersValue = headersValue; + this.TimeoutKeyName = timeoutKeyName; + this.TimeoutValue = timeoutValue; + this.ProtocolKeyName = protocolKeyName; + this.ProtocolValue = protocolValue; + } + + public OtlpExporterOptionsConfigurationType ConfigurationType { get; } + + public string EndpointKeyName { get; } + + public string EndpointValue { get; } + + public bool AppendSignalPathToEndpoint { get; } + + public string HeadersKeyName { get; } + + public string HeadersValue { get; } + + public string TimeoutKeyName { get; } + + public string TimeoutValue { get; } + + public string ProtocolKeyName { get; } + + public string ProtocolValue { get; } + + public IConfiguration ToConfiguration() + { + return this.AddToConfiguration(new ConfigurationBuilder()).Build(); + } + + public ConfigurationBuilder AddToConfiguration(ConfigurationBuilder configurationBuilder) + { + Dictionary dictionary = new(); + + dictionary[this.EndpointKeyName] = this.EndpointValue; + dictionary[this.HeadersKeyName] = this.HeadersValue; + dictionary[this.TimeoutKeyName] = this.TimeoutValue; + dictionary[this.ProtocolKeyName] = this.ProtocolValue; + + this.OnAddToDictionary(dictionary); + + configurationBuilder.AddInMemoryCollection(dictionary); + + return configurationBuilder; + } + + public void SetEnvVars() + { + Environment.SetEnvironmentVariable(this.EndpointKeyName, this.EndpointValue); + Environment.SetEnvironmentVariable(this.HeadersKeyName, this.HeadersValue); + Environment.SetEnvironmentVariable(this.TimeoutKeyName, this.TimeoutValue); + Environment.SetEnvironmentVariable(this.ProtocolKeyName, this.ProtocolValue); + + this.OnSetEnvVars(); + } + + public void ClearEnvVars() + { + Environment.SetEnvironmentVariable(this.EndpointKeyName, null); + Environment.SetEnvironmentVariable(this.HeadersKeyName, null); + Environment.SetEnvironmentVariable(this.TimeoutKeyName, null); + Environment.SetEnvironmentVariable(this.ProtocolKeyName, null); + + this.OnClearEnvVars(); + } + + public void AssertMatches(IOtlpExporterOptions otlpExporterOptions) + { + Assert.Equal(new Uri(this.EndpointValue), otlpExporterOptions.Endpoint); + Assert.Equal(this.HeadersValue, otlpExporterOptions.Headers); + Assert.Equal(int.Parse(this.TimeoutValue), otlpExporterOptions.TimeoutMilliseconds); + + if (!OtlpExportProtocolParser.TryParse(this.ProtocolValue, out var protocol)) + { + Assert.Fail(); + } + + Assert.Equal(protocol, otlpExporterOptions.Protocol); + + var concreteOptions = otlpExporterOptions as OtlpExporterOptions; + Assert.NotNull(concreteOptions); + Assert.Equal(this.AppendSignalPathToEndpoint, concreteOptions.AppendSignalPathToEndpoint); + } + + protected virtual void OnSetEnvVars() + { + } + + protected virtual void OnClearEnvVars() + { + } + + protected virtual void OnAddToDictionary(Dictionary dictionary) + { + } + } + + internal sealed class MetricsTestData : TestData + { + public MetricsTestData( + string endpointKeyName, + string endpointValue, + bool appendSignalPathToEndpoint, + string headersKeyName, + string headersValue, + string timeoutKeyName, + string timeoutValue, + string protocolKeyName, + string protocolValue, + string temporalityKeyName, + string temporalityValue) + : base( + OtlpExporterOptionsConfigurationType.Metrics, + endpointKeyName, + endpointValue, + appendSignalPathToEndpoint, + headersKeyName, + headersValue, + timeoutKeyName, + timeoutValue, + protocolKeyName, + protocolValue) + { + this.TemporalityKeyName = temporalityKeyName; + this.TemporalityValue = temporalityValue; + } + + public string TemporalityKeyName { get; } + + public string TemporalityValue { get; } + + public void AssertMatches(MetricReaderOptions metricReaderOptions) + { + Assert.Equal(Enum.Parse(typeof(MetricReaderTemporalityPreference), this.TemporalityValue), metricReaderOptions.TemporalityPreference); + } + + protected override void OnSetEnvVars() + { + Environment.SetEnvironmentVariable(this.TemporalityKeyName, this.TemporalityValue); + } + + protected override void OnClearEnvVars() + { + Environment.SetEnvironmentVariable(this.TemporalityKeyName, null); + } + + protected override void OnAddToDictionary(Dictionary dictionary) + { + dictionary[this.TemporalityKeyName] = this.TemporalityValue; + } + } +} diff --git a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/UseOtlpExporterExtensionTests.cs b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/UseOtlpExporterExtensionTests.cs index 81c1f44a7bb..0f32c51c155 100644 --- a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/UseOtlpExporterExtensionTests.cs +++ b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/UseOtlpExporterExtensionTests.cs @@ -15,8 +15,18 @@ namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests; -public class UseOtlpExporterExtensionTests +public class UseOtlpExporterExtensionTests : IDisposable { + public UseOtlpExporterExtensionTests() + { + OtlpSpecConfigDefinitionTests.ClearEnvVars(); + } + + public void Dispose() + { + OtlpSpecConfigDefinitionTests.ClearEnvVars(); + } + [Fact] public void UseOtlpExporterDefaultTest() { @@ -286,6 +296,54 @@ public void UseOtlpExporterAddsLoggingProcessorToPipelineEndTest() Assert.True(processors[1] is BatchLogRecordExportProcessor); } + [Fact] + public void UseOtlpExporterRespectsSpecEnvVarsTest() + { + OtlpSpecConfigDefinitionTests.SetEnvVars(); + + var services = new ServiceCollection(); + + services.AddOpenTelemetry() + .UseOtlpExporter(); + + using var sp = services.BuildServiceProvider(); + + var exporterBuilderOptions = sp.GetRequiredService>().Get(Options.DefaultName); + + OtlpSpecConfigDefinitionTests.DefaultData.AssertMatches(exporterBuilderOptions.DefaultOptions); + OtlpSpecConfigDefinitionTests.LoggingData.AssertMatches(exporterBuilderOptions.LoggingOptions); + OtlpSpecConfigDefinitionTests.MetricsData.AssertMatches(exporterBuilderOptions.MetricsOptions); + OtlpSpecConfigDefinitionTests.TracingData.AssertMatches(exporterBuilderOptions.TracingOptions); + + var metricReaderOptions = sp.GetRequiredService>().Get(Options.DefaultName); + + OtlpSpecConfigDefinitionTests.MetricsData.AssertMatches(metricReaderOptions); + } + + [Fact] + public void UseOtlpExporterRespectsSpecEnvVarsSetUsingIConfigurationTest() + { + var services = new ServiceCollection(); + + services.AddSingleton(OtlpSpecConfigDefinitionTests.ToConfiguration()); + + services.AddOpenTelemetry() + .UseOtlpExporter(); + + using var sp = services.BuildServiceProvider(); + + var exporterBuilderOptions = sp.GetRequiredService>().Get(Options.DefaultName); + + OtlpSpecConfigDefinitionTests.DefaultData.AssertMatches(exporterBuilderOptions.DefaultOptions); + OtlpSpecConfigDefinitionTests.LoggingData.AssertMatches(exporterBuilderOptions.LoggingOptions); + OtlpSpecConfigDefinitionTests.MetricsData.AssertMatches(exporterBuilderOptions.MetricsOptions); + OtlpSpecConfigDefinitionTests.TracingData.AssertMatches(exporterBuilderOptions.TracingOptions); + + var metricReaderOptions = sp.GetRequiredService>().Get(Options.DefaultName); + + OtlpSpecConfigDefinitionTests.MetricsData.AssertMatches(metricReaderOptions); + } + private static void VerifyOptionsApplied(ServiceProvider serviceProvider, string? name) { var exporterOptions = serviceProvider.GetRequiredService>().Get(name); From c3e0e37750cd311f29ba6633a8fca2f4cb741ce0 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Thu, 14 Mar 2024 13:48:41 -0700 Subject: [PATCH 48/48] Add a test collection for tests which modify envvars. --- .../Http2UnencryptedSupportTests.cs | 7 ++++++- .../OtlpExporterOptionsTests.cs | 1 + .../OtlpMetricsExporterTests.cs | 16 +++++++++++++--- .../UseOtlpExporterExtensionTests.cs | 1 + 4 files changed, 21 insertions(+), 4 deletions(-) diff --git a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/Http2UnencryptedSupportTests.cs b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/Http2UnencryptedSupportTests.cs index 673260f281d..7ef14595f06 100644 --- a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/Http2UnencryptedSupportTests.cs +++ b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/Http2UnencryptedSupportTests.cs @@ -14,10 +14,15 @@ public Http2UnencryptedSupportTests() public void Dispose() { - AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", this.initialFlagStatus); + this.Dispose(true); GC.SuppressFinalize(this); } + protected virtual void Dispose(bool disposing) + { + AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", this.initialFlagStatus); + } + private static bool DetermineInitialFlagStatus() { if (AppContext.TryGetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", out var flag)) diff --git a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpExporterOptionsTests.cs b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpExporterOptionsTests.cs index e1c59448628..dc79c95c07b 100644 --- a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpExporterOptionsTests.cs +++ b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpExporterOptionsTests.cs @@ -6,6 +6,7 @@ namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests; +[Collection("EnvVars")] public class OtlpExporterOptionsTests : IDisposable { public OtlpExporterOptionsTests() diff --git a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpMetricsExporterTests.cs b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpMetricsExporterTests.cs index e3d67bb1aba..ad2f8a6b4b2 100644 --- a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpMetricsExporterTests.cs +++ b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpMetricsExporterTests.cs @@ -18,6 +18,7 @@ namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests; +[Collection("EnvVars")] public class OtlpMetricsExporterTests : Http2UnencryptedSupportTests { private static readonly KeyValuePair[] KeyValues = new KeyValuePair[] @@ -26,6 +27,11 @@ public class OtlpMetricsExporterTests : Http2UnencryptedSupportTests new KeyValuePair("key2", 123), }; + public OtlpMetricsExporterTests() + { + OtlpSpecConfigDefinitionTests.ClearEnvVars(); + } + [Fact] public void TestAddOtlpExporter_SetsCorrectMetricReaderDefaults() { @@ -762,7 +768,6 @@ public void TestTemporalityPreferenceUsingConfiguration(string configValue, Metr [InlineData("invalid", MetricReaderTemporalityPreference.Cumulative)] public void TestTemporalityPreferenceUsingEnvVar(string configValue, MetricReaderTemporalityPreference expectedTemporality) { - Environment.SetEnvironmentVariable(OtlpSpecConfigDefinitionTests.MetricsData.TemporalityKeyName, null); Environment.SetEnvironmentVariable(OtlpSpecConfigDefinitionTests.MetricsData.TemporalityKeyName, configValue); var testExecuted = false; @@ -780,8 +785,6 @@ public void TestTemporalityPreferenceUsingEnvVar(string configValue, MetricReade .Build(); Assert.True(testExecuted); - - Environment.SetEnvironmentVariable(OtlpSpecConfigDefinitionTests.MetricsData.TemporalityKeyName, null); } [Theory] @@ -913,6 +916,13 @@ void AssertExemplars(T value, Metric metric) } } + protected override void Dispose(bool disposing) + { + OtlpSpecConfigDefinitionTests.ClearEnvVars(); + + base.Dispose(disposing); + } + private static void VerifyExemplars(long? longValue, double? doubleValue, bool enableExemplars, Func getExemplarFunc, T state) { var exemplar = getExemplarFunc(state); diff --git a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/UseOtlpExporterExtensionTests.cs b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/UseOtlpExporterExtensionTests.cs index 0f32c51c155..025bd3b97ec 100644 --- a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/UseOtlpExporterExtensionTests.cs +++ b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/UseOtlpExporterExtensionTests.cs @@ -15,6 +15,7 @@ namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests; +[Collection("EnvVars")] public class UseOtlpExporterExtensionTests : IDisposable { public UseOtlpExporterExtensionTests()