Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
b060f51
OtlpExporter cross-cutting registration extension rough draft.
CodeBlanch Feb 24, 2024
c729fd7
Tweaks and evolving design.
CodeBlanch Feb 26, 2024
fbd97d9
Merge remote-tracking branch 'upstream/main' into otlp-crosscutting-e…
CodeBlanch Feb 28, 2024
ab87eb0
First compiling version.
CodeBlanch Feb 28, 2024
0e9709e
Tweaks.
CodeBlanch Feb 28, 2024
a19a81f
Add concept of weight to BaseProcessor.
CodeBlanch Feb 28, 2024
7e1cffb
Merge remote-tracking branch 'upstream/main' into otlp-crosscutting-e…
CodeBlanch Mar 6, 2024
77e550c
Updates based on discussion.
CodeBlanch Mar 6, 2024
ef6652b
Tweak processor pipeline weight design.
CodeBlanch Mar 7, 2024
ea8d6a1
Revert OtlpExporterOptionsBase refactor to shrink the diff of changes.
CodeBlanch Mar 7, 2024
5512bc7
Merge branch 'main' into otlp-crosscutting-extension
CodeBlanch Mar 7, 2024
b3d9db4
Reduce changes shown on diff.
CodeBlanch Mar 7, 2024
75111d2
Tweaks.
CodeBlanch Mar 7, 2024
c2db100
Code review.
CodeBlanch Mar 7, 2024
cb83c0e
Merge remote-tracking branch 'upstream/main' into otlp-crosscutting-e…
CodeBlanch Mar 7, 2024
1506a34
Merge remote-tracking branch 'upstream/main' into otlp-crosscutting-e…
CodeBlanch Mar 7, 2024
8c896a3
Merge from main.
CodeBlanch Mar 8, 2024
d55e488
Merge from main.
CodeBlanch Mar 8, 2024
2a1046a
Merge from main.
CodeBlanch Mar 8, 2024
15e0272
Merge from main.
CodeBlanch Mar 11, 2024
24cbc81
Merge fixes.
CodeBlanch Mar 11, 2024
2e7811d
Tweaks and fixes.
CodeBlanch Mar 11, 2024
7bdf936
Merge from main.
CodeBlanch Mar 11, 2024
71c56e6
Revert order changes.
CodeBlanch Mar 11, 2024
aa84a40
Tweak.
CodeBlanch Mar 11, 2024
4ba37bb
Tweak.
CodeBlanch Mar 11, 2024
7f3c3cb
API files.
CodeBlanch Mar 11, 2024
ded0687
Bug fix.
CodeBlanch Mar 11, 2024
bc27254
Make processor pipeline weight a constant.
CodeBlanch Mar 11, 2024
57cbc99
Tweak.
CodeBlanch Mar 11, 2024
356628e
Bug fix.
CodeBlanch Mar 12, 2024
3493219
Add tests.
CodeBlanch Mar 12, 2024
15ac47b
CHANGELOG update.
CodeBlanch Mar 12, 2024
f275ea2
Merge branch 'main' into otlp-crosscutting-extension
CodeBlanch Mar 12, 2024
c80c86f
Lint.
CodeBlanch Mar 12, 2024
74f24fe
Warning cleanup.
CodeBlanch Mar 12, 2024
ef38a9d
Warning cleanup.
CodeBlanch Mar 12, 2024
e767069
Docfx fix.
CodeBlanch Mar 12, 2024
f515cc0
Integration test fixes.
CodeBlanch Mar 12, 2024
b145007
Code review.
CodeBlanch Mar 12, 2024
16e0741
JSON config comment update.
CodeBlanch Mar 12, 2024
6f2c8e0
Code review.
CodeBlanch Mar 12, 2024
b75f139
Code review.
CodeBlanch Mar 12, 2024
0faac1a
CHANGELOG tweak.
CodeBlanch Mar 12, 2024
4bef0ef
Ctor tweak.
CodeBlanch Mar 13, 2024
956f537
Test tweaks.
CodeBlanch Mar 13, 2024
2c1cc37
Tweaked exposed overloads and refactored code.
CodeBlanch Mar 13, 2024
e577dc3
Test fixes.
CodeBlanch Mar 13, 2024
c4fe57c
Assert fix.
CodeBlanch Mar 13, 2024
084188a
Add XML docs to capture current naming behaviors.
CodeBlanch Mar 13, 2024
c0d8ba3
Tweak namespace.
CodeBlanch Mar 13, 2024
cc33b5a
Merge branch 'main' into otlp-crosscutting-extension
CodeBlanch Mar 13, 2024
c11e4a2
Warning cleanup.
CodeBlanch Mar 13, 2024
4f06ab8
Code review.
CodeBlanch Mar 13, 2024
cd7ca4e
Remove UseOtlpExporter overload which only changes protocol.
CodeBlanch Mar 13, 2024
01dba40
Code review.
CodeBlanch Mar 13, 2024
5dac98b
Merge from main.
CodeBlanch Mar 13, 2024
1c3258c
Cleanup and refactoring.
CodeBlanch Mar 13, 2024
ed09c06
Code review.
CodeBlanch Mar 13, 2024
31f7759
Refactor spec env var code and add test coverage for UseOtlpExporter …
CodeBlanch Mar 14, 2024
c3e0e37
Add a test collection for tests which modify envvars.
CodeBlanch Mar 14, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
OtlpExporter cross-cutting registration extension rough draft.
  • Loading branch information
CodeBlanch committed Feb 24, 2024
commit b060f5175cb4ce72aaff3e51a2e2a97fce232ef6
Original file line number Diff line number Diff line change
@@ -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<OtlpExporterBuilderOptions>? configure)
=> AddOtlpExporter(builder, name: null, configure);

public static IOpenTelemetryBuilder AddOtlpExporter(
this IOpenTelemetryBuilder builder,
string? name,
Action<OtlpExporterBuilderOptions>? 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<IOptionsMonitor<ActivityExportProcessorOptions>>().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<IOptionsMonitor<LogRecordExportProcessorOptions>>().Get(name),
sp.GetRequiredService<IOptionsMonitor<SdkLimitOptions>>().CurrentValue,
sp.GetRequiredService<IOptionsMonitor<ExperimentalOptions>>().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<IOptionsMonitor<MetricReaderOptions>>().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<IOptionsMonitor<SdkLimitOptions>>().CurrentValue,
sp));
});

return builder;

static OtlpExporterBuilderOptions GetOptions(IServiceProvider sp, string name)
{
return sp.GetRequiredService<IOptionsMonitor<OtlpExporterBuilderOptions>>().Get(name);
}
}
}
Original file line number Diff line number Diff line change
@@ -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<OtlpExportProtocol>(
"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; }
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

#nullable enable

using System.Diagnostics;
using System.Reflection;
#if NETFRAMEWORK
Expand Down Expand Up @@ -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<Activity>? batchExportProcessorOptions;
private Func<HttpClient>? httpClientFactory;

/// <summary>
/// Initializes a new instance of the <see cref="OtlpExporterOptions"/> class.
/// </summary>
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))
{
Expand All @@ -79,15 +86,15 @@ internal OtlpExporterOptions(
this.Protocol = protocol;
}

this.HttpClientFactory = this.DefaultHttpClientFactory = () =>
this.DefaultHttpClientFactory = () =>
{
return new HttpClient
{
Timeout = TimeSpan.FromMilliseconds(this.TimeoutMilliseconds),
};
};

this.BatchExportProcessorOptions = defaultBatchOptions;
this.defaultProcessorOptions = defaultExportProcessorOptions;
}

/// <summary>
Expand Down Expand Up @@ -122,29 +129,54 @@ public Uri Endpoint
/// Gets or sets optional headers for the connection. Refer to the <a href="https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/protocol/exporter.md#specifying-headers-via-environment-variables">
/// specification</a> for information on the expected format for Headers.
/// </summary>
public string Headers { get; set; }
public string? Headers { get; set; }

/// <summary>
/// Gets or sets the max waiting time (in milliseconds) for the backend to process each batch. The default value is 10000.
/// </summary>
public int TimeoutMilliseconds { get; set; } = 10000;
public int TimeoutMilliseconds
{
get => this.timeoutMilliseconds ?? 10000;
set => this.timeoutMilliseconds = value;
}

/// <summary>
/// Gets or sets the the OTLP transport protocol. Supported values: Grpc and HttpProtobuf.
/// </summary>
public OtlpExportProtocol Protocol { get; set; } = DefaultOtlpExportProtocol;
public OtlpExportProtocol Protocol
{
get => this.protocol ?? DefaultOtlpExportProtocol;
set => this.protocol = value;
}

/// <summary>
/// Gets or sets the export processor type to be used with the OpenTelemetry Protocol Exporter. The default value is <see cref="ExportProcessorType.Batch"/>.
/// </summary>
/// <remarks>Note: This only applies when exporting traces.</remarks>
public ExportProcessorType ExportProcessorType { get; set; } = ExportProcessorType.Batch;
public ExportProcessorType ExportProcessorType
{
get => this.defaultProcessorOptions?.ExportProcessorType ?? ExportProcessorType.Batch;
set => (this.defaultProcessorOptions ??= new()).ExportProcessorType = value;
}

/// <summary>
/// Gets or sets the BatchExportProcessor options. Ignored unless ExportProcessorType is Batch.
/// </summary>
/// <remarks>Note: This only applies when exporting traces.</remarks>
public BatchExportProcessorOptions<Activity> BatchExportProcessorOptions { get; set; }
public BatchExportProcessorOptions<Activity> BatchExportProcessorOptions
{
get
{
if (this.batchExportProcessorOptions != null)
{
return this.batchExportProcessorOptions;
}

return (this.defaultProcessorOptions ??= new()).BatchExportProcessorOptions;
}

set => this.batchExportProcessorOptions = value;
}

/// <summary>
/// Gets or sets the factory function called to create the <see
Expand Down Expand Up @@ -177,7 +209,19 @@ public Uri Endpoint
/// directly.</item>
/// </list>
/// </remarks>
public Func<HttpClient> HttpClientFactory { get; set; }
public Func<HttpClient> HttpClientFactory
{
get => this.httpClientFactory ??= this.DefaultHttpClientFactory;
set
{
this.httpClientFactory = value ?? NullHttpClientFactory;

static HttpClient NullHttpClientFactory()
{
return null!;
}
}
}

/// <summary>
/// Gets a value indicating whether <see cref="Endpoint" /> was modified via its setter.
Expand All @@ -195,14 +239,57 @@ internal static OtlpExporterOptions CreateOtlpExporterOptions(
string name)
=> new(
configuration,
serviceProvider.GetRequiredService<IOptionsMonitor<BatchExportActivityProcessorOptions>>().Get(name));
serviceProvider.GetRequiredService<IOptionsMonitor<ActivityExportProcessorOptions>>().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<AssemblyInformationalVersionAttribute>();
var informationalVersion = assemblyVersion.InformationalVersion;
var informationalVersion = assemblyVersion?.InformationalVersion;
return string.IsNullOrEmpty(informationalVersion) ? UserAgentProduct : $"{UserAgentProduct}/{informationalVersion}";
}
catch (Exception)
Expand Down
Loading