Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[onecollector] Add LoggerProviderBuilder registration extension #1876

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
OpenTelemetry.Logs.OneCollectorLoggerProviderBuilderExtensions
static OpenTelemetry.Logs.OneCollectorLoggerProviderBuilderExtensions.AddOneCollectorExporter(this OpenTelemetry.Logs.LoggerProviderBuilder! builder) -> OpenTelemetry.Logs.LoggerProviderBuilder!
static OpenTelemetry.Logs.OneCollectorLoggerProviderBuilderExtensions.AddOneCollectorExporter(this OpenTelemetry.Logs.LoggerProviderBuilder! builder, Microsoft.Extensions.Configuration.IConfiguration! configuration) -> OpenTelemetry.Logs.LoggerProviderBuilder!
static OpenTelemetry.Logs.OneCollectorLoggerProviderBuilderExtensions.AddOneCollectorExporter(this OpenTelemetry.Logs.LoggerProviderBuilder! builder, Microsoft.Extensions.Configuration.IConfiguration! configuration, System.Action<OpenTelemetry.Logs.OneCollectorLogExportProcessorBuilder!>! configure) -> OpenTelemetry.Logs.LoggerProviderBuilder!
static OpenTelemetry.Logs.OneCollectorLoggerProviderBuilderExtensions.AddOneCollectorExporter(this OpenTelemetry.Logs.LoggerProviderBuilder! builder, string! connectionString) -> OpenTelemetry.Logs.LoggerProviderBuilder!
static OpenTelemetry.Logs.OneCollectorLoggerProviderBuilderExtensions.AddOneCollectorExporter(this OpenTelemetry.Logs.LoggerProviderBuilder! builder, string! connectionString, System.Action<OpenTelemetry.Logs.OneCollectorLogExportProcessorBuilder!>! configure) -> OpenTelemetry.Logs.LoggerProviderBuilder!
static OpenTelemetry.Logs.OneCollectorLoggerProviderBuilderExtensions.AddOneCollectorExporter(this OpenTelemetry.Logs.LoggerProviderBuilder! builder, string? name, string? connectionString, Microsoft.Extensions.Configuration.IConfiguration? configuration, System.Action<OpenTelemetry.Logs.OneCollectorLogExportProcessorBuilder!>? configure) -> OpenTelemetry.Logs.LoggerProviderBuilder!
static OpenTelemetry.Logs.OneCollectorLoggerProviderBuilderExtensions.AddOneCollectorExporter(this OpenTelemetry.Logs.LoggerProviderBuilder! builder, System.Action<OpenTelemetry.Logs.OneCollectorLogExportProcessorBuilder!>! configure) -> OpenTelemetry.Logs.LoggerProviderBuilder!
6 changes: 6 additions & 0 deletions src/OpenTelemetry.Exporter.OneCollector/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@

## Unreleased

* Update OpenTelemetry SDK version to `1.9.0-rc.1`.
([#1876](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/1876))

* Added `LoggerProviderBuilder.AddOneCollectorExporter` registration extension.
([#1876](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/1876))

## 1.8.0

Released 2024-Apr-22
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,14 @@ protected override void SerializeItemToJson(Resource resource, LogRecord item, C
writer.WriteNumber(EventIdProperty, item.EventId.Id);
}

#if EXPOSE_EXPERIMENTAL_FEATURES
#pragma warning disable CS0618 // Type or member is obsolete
// TODO: Update to use LogRecord.Severity
var logLevel = (int)item.LogLevel;
#pragma warning restore CS0618 // Type or member is obsolete
#else
var logLevel = (int)item.LogLevel;
#endif
writer.WriteString(SeverityTextProperty, LogLevelToSeverityTextMappings[logLevel]);
writer.WriteNumber(SeverityNumberProperty, LogLevelToSeverityNumberMappings[logLevel]);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

#if NETFRAMEWORK
using System.Net.Http;
#endif
using System.Diagnostics;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using OpenTelemetry.Exporter.OneCollector;
using OpenTelemetry.Internal;

Expand All @@ -17,19 +17,34 @@ namespace OpenTelemetry.Logs;
/// </summary>
public sealed class OneCollectorLogExportProcessorBuilder
{
private static readonly Func<HttpClient> DefaultHttpClientFactory = () => new HttpClient();
private readonly OneCollectorLogExporterOptions exporterOptions = new();
private readonly BatchExportProcessorOptions<LogRecord> batchOptions = new();
private readonly List<Action<OneCollectorExporter<LogRecord>>> configureExporterActions = new();
private Func<HttpClient>? httpClientFactory;
private readonly string? name;
private readonly IServiceCollection services;
private readonly bool ownsServices;

internal OneCollectorLogExportProcessorBuilder(
string? name,
IServiceCollection? services,
IConfiguration? configuration)
{
this.name = name;

if (services == null)
{
this.services = new ServiceCollection();
this.services.AddOptions();
this.ownsServices = true;
}
else
{
this.services = services;
}

if (configuration != null)
{
configuration.Bind(this.exporterOptions);
configuration.GetSection("BatchOptions").Bind(this.batchOptions);
this.services.Configure<OneCollectorLogExporterOptions>(this.name, configuration);
this.services.Configure<BatchExportLogRecordProcessorOptions>(
this.name,
batchOptions => configuration.GetSection("BatchOptions").Bind(batchOptions));
}
}

Expand All @@ -47,7 +62,9 @@ public OneCollectorLogExportProcessorBuilder ConfigureBatchOptions(
{
Guard.ThrowIfNull(configure);

configure(this.batchOptions);
this.services.Configure<BatchExportLogRecordProcessorOptions>(
this.name,
batchOptions => configure(batchOptions));

return this;
}
Expand All @@ -66,7 +83,10 @@ public OneCollectorLogExportProcessorBuilder ConfigureExporter(
{
Guard.ThrowIfNull(configure);

this.configureExporterActions.Add(configure);
this.services.AddSingleton(
new ConfigureOneCollectorExporter(
this.name,
(sp, e) => configure(e)));

return this;
}
Expand All @@ -86,7 +106,9 @@ public OneCollectorLogExportProcessorBuilder ConfigureSerializationOptions(
{
Guard.ThrowIfNull(configure);

configure(this.exporterOptions.SerializationOptions);
this.services.Configure<OneCollectorLogExporterOptions>(
this.name,
exporterOptions => configure(exporterOptions.SerializationOptions));

return this;
}
Expand All @@ -105,7 +127,9 @@ public OneCollectorLogExportProcessorBuilder ConfigureTransportOptions(
{
Guard.ThrowIfNull(configure);

configure(this.exporterOptions.TransportOptions);
this.services.Configure<OneCollectorLogExporterOptions>(
this.name,
exporterOptions => configure(exporterOptions.TransportOptions));

return this;
}
Expand All @@ -126,7 +150,9 @@ public OneCollectorLogExportProcessorBuilder SetConnectionString(
{
Guard.ThrowIfNullOrWhitespace(connectionString);

this.exporterOptions.ConnectionString = connectionString;
this.services.Configure<OneCollectorLogExporterOptions>(
this.name,
exporterOptions => exporterOptions.ConnectionString = connectionString);

return this;
}
Expand All @@ -148,61 +174,62 @@ public OneCollectorLogExportProcessorBuilder SetDefaultEventName(
{
Guard.ThrowIfNullOrWhitespace(defaultEventName);

this.exporterOptions.DefaultEventName = defaultEventName;
this.services.Configure<OneCollectorLogExporterOptions>(
this.name,
exporterOptions => exporterOptions.DefaultEventName = defaultEventName);

return this;
}

/// <summary>
/// Sets the factory function called to create the <see cref="HttpClient"/>
/// instance that will be used at runtime to transmit telemetry over HTTP
/// transports. The returned instance will be reused for all export
/// invocations.
/// </summary>
/// <remarks>
/// Note: The default behavior is an <see cref="HttpClient"/> will be
/// instantiated directly.
/// </remarks>
/// <param name="httpClientFactory">Factory function which returns the <see
/// cref="HttpClient"/> instance to use.</param>
/// <returns>The supplied <see
/// cref="OneCollectorLogExportProcessorBuilder"/> for call
/// chaining.</returns>
internal OneCollectorLogExportProcessorBuilder SetHttpClientFactory(
Func<HttpClient> httpClientFactory)
internal BaseProcessor<LogRecord> BuildProcessor(
IServiceProvider serviceProvider)
{
Guard.ThrowIfNull(httpClientFactory);
Debug.Assert(serviceProvider != null, "serviceProvider was null");

this.httpClientFactory = httpClientFactory;
ServiceProvider? ownedServiceProvider = null;
if (this.ownsServices)
{
ownedServiceProvider = this.services.BuildServiceProvider();
}

return this;
}
var exporterOptions = (ownedServiceProvider ?? serviceProvider!).GetRequiredService<IOptionsMonitor<OneCollectorLogExporterOptions>>().Get(this.name);
var batchOptions = (ownedServiceProvider ?? serviceProvider!).GetRequiredService<IOptionsMonitor<BatchExportLogRecordProcessorOptions>>().Get(this.name);

internal BaseProcessor<LogRecord> BuildProcessor()
{
try
{
#pragma warning disable CA2000 // Dispose objects before losing scope
return new BatchLogRecordExportProcessor(
this.BuildExporter(),
this.batchOptions.MaxQueueSize,
this.batchOptions.ScheduledDelayMilliseconds,
this.batchOptions.ExporterTimeoutMilliseconds,
this.batchOptions.MaxExportBatchSize);
return new BatchLogRecordExportProcessor(
CreateExporter(this.name, serviceProvider!, exporterOptions, (ownedServiceProvider ?? serviceProvider!).GetServices<ConfigureOneCollectorExporter>()),
batchOptions.MaxQueueSize,
batchOptions.ScheduledDelayMilliseconds,
batchOptions.ExporterTimeoutMilliseconds,
batchOptions.MaxExportBatchSize);
#pragma warning restore CA2000 // Dispose objects before losing scope
}
finally
{
ownedServiceProvider?.Dispose();
}
}

private OneCollectorExporter<LogRecord> BuildExporter()
private static OneCollectorExporter<LogRecord> CreateExporter(
string? name,
IServiceProvider serviceProvider,
OneCollectorLogExporterOptions exporterOptions,
IEnumerable<ConfigureOneCollectorExporter> configurations)
{
#pragma warning disable CA2000 // Dispose objects before losing scope
var exporter = new OneCollectorExporter<LogRecord>(this.CreateSink());
var exporter = new OneCollectorExporter<LogRecord>(CreateSink(exporterOptions));
#pragma warning restore CA2000 // Dispose objects before losing scope

try
{
int index = 0;
while (index < this.configureExporterActions.Count)
foreach (var configuration in configurations)
{
var action = this.configureExporterActions[index++];
action(exporter);
if (name == configuration.Name)
{
configuration.Configure(serviceProvider, exporter);
}
}
}
catch
Expand All @@ -214,27 +241,40 @@ private OneCollectorExporter<LogRecord> BuildExporter()
return exporter;
}

private WriteDirectlyToTransportSink<LogRecord> CreateSink()
private static WriteDirectlyToTransportSink<LogRecord> CreateSink(OneCollectorLogExporterOptions exporterOptions)
{
this.exporterOptions.Validate();

var transportOptions = this.exporterOptions.TransportOptions;
exporterOptions.Validate();

var httpClient = (this.httpClientFactory ?? DefaultHttpClientFactory)() ?? throw new NotSupportedException("HttpClientFactory cannot return a null instance.");
var transportOptions = exporterOptions.TransportOptions;

#pragma warning disable CA2000 // Dispose objects before losing scope
return new WriteDirectlyToTransportSink<LogRecord>(
new LogRecordCommonSchemaJsonSerializer(
new EventNameManager(this.exporterOptions.DefaultEventNamespace, this.exporterOptions.DefaultEventName),
this.exporterOptions.TenantToken!,
this.exporterOptions.SerializationOptions.ExceptionStackTraceHandling,
new EventNameManager(exporterOptions.DefaultEventNamespace, exporterOptions.DefaultEventName),
exporterOptions.TenantToken!,
exporterOptions.SerializationOptions.ExceptionStackTraceHandling,
transportOptions.MaxPayloadSizeInBytes == -1 ? int.MaxValue : transportOptions.MaxPayloadSizeInBytes,
transportOptions.MaxNumberOfItemsPerPayload == -1 ? int.MaxValue : transportOptions.MaxNumberOfItemsPerPayload),
new HttpJsonPostTransport(
this.exporterOptions.InstrumentationKey!,
exporterOptions.InstrumentationKey!,
transportOptions.Endpoint,
transportOptions.HttpCompression,
new HttpClientWrapper(httpClient)));
new HttpClientWrapper(transportOptions.GetHttpClient())));
#pragma warning restore CA2000 // Dispose objects before losing scope
}

private sealed class ConfigureOneCollectorExporter
{
public ConfigureOneCollectorExporter(
string? name,
Action<IServiceProvider, OneCollectorExporter<LogRecord>> configure)
{
this.Name = name;
this.Configure = configure;
}

public string? Name { get; }

public Action<IServiceProvider, OneCollectorExporter<LogRecord>> Configure { get; }
}
}
Loading
Loading