From 958f5624a47fad32131ea649fa7dc4d65fd4bcc7 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Mon, 10 Jun 2024 13:59:48 -0700 Subject: [PATCH 1/6] Added LoggerProviderBuilder.AddOneCollectorExporter registration extension. --- .../.publicApi/PublicAPI.Unshipped.txt | 8 + .../CHANGELOG.md | 6 + .../LogRecordCommonSchemaJsonSerializer.cs | 7 + .../OneCollectorLogExportProcessorBuilder.cs | 162 +++++++---- ...ollectorLoggerProviderBuilderExtensions.cs | 174 ++++++++++++ ...torOpenTelemetryLoggerOptionsExtensions.cs | 7 +- .../OneCollectorExporterTransportOptions.cs | 22 ++ ...OpenTelemetry.Exporter.OneCollector.csproj | 7 +- ...ogRecordCommonSchemaJsonSerializerTests.cs | 3 + ...torLoggerProviderBuilderExtensionsTests.cs | 266 ++++++++++++++++++ 10 files changed, 595 insertions(+), 67 deletions(-) create mode 100644 src/OpenTelemetry.Exporter.OneCollector/Logs/OneCollectorLoggerProviderBuilderExtensions.cs create mode 100644 test/OpenTelemetry.Exporter.OneCollector.Tests/OneCollectorLoggerProviderBuilderExtensionsTests.cs diff --git a/src/OpenTelemetry.Exporter.OneCollector/.publicApi/PublicAPI.Unshipped.txt b/src/OpenTelemetry.Exporter.OneCollector/.publicApi/PublicAPI.Unshipped.txt index e69de29bb2..9700b06ef0 100644 --- a/src/OpenTelemetry.Exporter.OneCollector/.publicApi/PublicAPI.Unshipped.txt +++ b/src/OpenTelemetry.Exporter.OneCollector/.publicApi/PublicAPI.Unshipped.txt @@ -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! 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! 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? configure) -> OpenTelemetry.Logs.LoggerProviderBuilder! +static OpenTelemetry.Logs.OneCollectorLoggerProviderBuilderExtensions.AddOneCollectorExporter(this OpenTelemetry.Logs.LoggerProviderBuilder! builder, System.Action! configure) -> OpenTelemetry.Logs.LoggerProviderBuilder! diff --git a/src/OpenTelemetry.Exporter.OneCollector/CHANGELOG.md b/src/OpenTelemetry.Exporter.OneCollector/CHANGELOG.md index 6781b8e445..13615601c1 100644 --- a/src/OpenTelemetry.Exporter.OneCollector/CHANGELOG.md +++ b/src/OpenTelemetry.Exporter.OneCollector/CHANGELOG.md @@ -2,6 +2,12 @@ ## Unreleased +* Update OpenTelemetry SDK version to `1.9.0-rc.1`. + ([#XXXX](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/XXXX)) + +* Added `LoggerProviderBuilder.AddOneCollectorExporter` registration extension. + ([#XXXX](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/XXXX)) + ## 1.8.0 Released 2024-Apr-22 diff --git a/src/OpenTelemetry.Exporter.OneCollector/Internal/Serialization/LogRecordCommonSchemaJsonSerializer.cs b/src/OpenTelemetry.Exporter.OneCollector/Internal/Serialization/LogRecordCommonSchemaJsonSerializer.cs index 6edcaba58b..33ac460af9 100644 --- a/src/OpenTelemetry.Exporter.OneCollector/Internal/Serialization/LogRecordCommonSchemaJsonSerializer.cs +++ b/src/OpenTelemetry.Exporter.OneCollector/Internal/Serialization/LogRecordCommonSchemaJsonSerializer.cs @@ -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]); diff --git a/src/OpenTelemetry.Exporter.OneCollector/Logs/OneCollectorLogExportProcessorBuilder.cs b/src/OpenTelemetry.Exporter.OneCollector/Logs/OneCollectorLogExportProcessorBuilder.cs index 659a56e5c7..3e61545d34 100644 --- a/src/OpenTelemetry.Exporter.OneCollector/Logs/OneCollectorLogExportProcessorBuilder.cs +++ b/src/OpenTelemetry.Exporter.OneCollector/Logs/OneCollectorLogExportProcessorBuilder.cs @@ -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; @@ -17,19 +17,34 @@ namespace OpenTelemetry.Logs; /// public sealed class OneCollectorLogExportProcessorBuilder { - private static readonly Func DefaultHttpClientFactory = () => new HttpClient(); - private readonly OneCollectorLogExporterOptions exporterOptions = new(); - private readonly BatchExportProcessorOptions batchOptions = new(); - private readonly List>> configureExporterActions = new(); - private Func? 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(this.name, configuration); + this.services.Configure( + this.name, + batchOptions => configuration.GetSection("BatchOptions").Bind(batchOptions)); } } @@ -47,7 +62,9 @@ public OneCollectorLogExportProcessorBuilder ConfigureBatchOptions( { Guard.ThrowIfNull(configure); - configure(this.batchOptions); + this.services.Configure( + this.name, + batchOptions => configure(batchOptions)); return this; } @@ -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; } @@ -86,7 +106,9 @@ public OneCollectorLogExportProcessorBuilder ConfigureSerializationOptions( { Guard.ThrowIfNull(configure); - configure(this.exporterOptions.SerializationOptions); + this.services.Configure( + this.name, + exporterOptions => configure(exporterOptions.SerializationOptions)); return this; } @@ -105,7 +127,9 @@ public OneCollectorLogExportProcessorBuilder ConfigureTransportOptions( { Guard.ThrowIfNull(configure); - configure(this.exporterOptions.TransportOptions); + this.services.Configure( + this.name, + exporterOptions => configure(exporterOptions.TransportOptions)); return this; } @@ -126,7 +150,9 @@ public OneCollectorLogExportProcessorBuilder SetConnectionString( { Guard.ThrowIfNullOrWhitespace(connectionString); - this.exporterOptions.ConnectionString = connectionString; + this.services.Configure( + this.name, + exporterOptions => exporterOptions.ConnectionString = connectionString); return this; } @@ -148,61 +174,62 @@ public OneCollectorLogExportProcessorBuilder SetDefaultEventName( { Guard.ThrowIfNullOrWhitespace(defaultEventName); - this.exporterOptions.DefaultEventName = defaultEventName; + this.services.Configure( + this.name, + exporterOptions => exporterOptions.DefaultEventName = defaultEventName); return this; } - /// - /// Sets the factory function called to create the - /// instance that will be used at runtime to transmit telemetry over HTTP - /// transports. The returned instance will be reused for all export - /// invocations. - /// - /// - /// Note: The default behavior is an will be - /// instantiated directly. - /// - /// Factory function which returns the instance to use. - /// The supplied for call - /// chaining. - internal OneCollectorLogExportProcessorBuilder SetHttpClientFactory( - Func httpClientFactory) + internal BaseProcessor 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>().Get(this.name); + var batchOptions = (ownedServiceProvider ?? serviceProvider!).GetRequiredService>().Get(this.name); - internal BaseProcessor 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()), + batchOptions.MaxQueueSize, + batchOptions.ScheduledDelayMilliseconds, + batchOptions.ExporterTimeoutMilliseconds, + batchOptions.MaxExportBatchSize); #pragma warning restore CA2000 // Dispose objects before losing scope + } + finally + { + ownedServiceProvider?.Dispose(); + } } - private OneCollectorExporter BuildExporter() + private static OneCollectorExporter CreateExporter( + string? name, + IServiceProvider serviceProvider, + OneCollectorLogExporterOptions exporterOptions, + IEnumerable configurations) { #pragma warning disable CA2000 // Dispose objects before losing scope - var exporter = new OneCollectorExporter(this.CreateSink()); + var exporter = new OneCollectorExporter(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 @@ -214,27 +241,40 @@ private OneCollectorExporter BuildExporter() return exporter; } - private WriteDirectlyToTransportSink CreateSink() + private static WriteDirectlyToTransportSink 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( 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> configure) + { + this.Name = name; + this.Configure = configure; + } + + public string? Name { get; } + + public Action> Configure { get; } + } } diff --git a/src/OpenTelemetry.Exporter.OneCollector/Logs/OneCollectorLoggerProviderBuilderExtensions.cs b/src/OpenTelemetry.Exporter.OneCollector/Logs/OneCollectorLoggerProviderBuilderExtensions.cs new file mode 100644 index 0000000000..deb99885ac --- /dev/null +++ b/src/OpenTelemetry.Exporter.OneCollector/Logs/OneCollectorLoggerProviderBuilderExtensions.cs @@ -0,0 +1,174 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +using Microsoft.Extensions.Configuration; +using OpenTelemetry.Exporter.OneCollector; +using OpenTelemetry.Internal; + +namespace OpenTelemetry.Logs; + +/// +/// Contains extension methods to register the OneCollector log exporter. +/// +public static class OneCollectorLoggerProviderBuilderExtensions +{ + /// + /// Add OneCollector exporter to the . + /// + /// . + /// The supplied for call + /// chaining. + public static LoggerProviderBuilder AddOneCollectorExporter( + this LoggerProviderBuilder builder) + { + return AddOneCollectorExporter(builder, name: null, connectionString: null, configuration: null, configure: null); + } + + /// + /// Add OneCollector exporter to the . + /// + /// . + /// Callback action for configuring . + /// The supplied for call + /// chaining. + public static LoggerProviderBuilder AddOneCollectorExporter( + this LoggerProviderBuilder builder, + Action configure) + { + Guard.ThrowIfNull(configure); + + return AddOneCollectorExporter(builder, name: null, connectionString: null, configuration: null, configure); + } + + /// + /// Add OneCollector exporter to the . + /// + /// . + /// OneCollector connection string. + /// The supplied for call + /// chaining. + public static LoggerProviderBuilder AddOneCollectorExporter( + this LoggerProviderBuilder builder, + string connectionString) + { + Guard.ThrowIfNullOrWhitespace(connectionString); + + return AddOneCollectorExporter(builder, name: null, connectionString, configuration: null, configure: null); + } + + /// + /// Add OneCollector exporter to the . + /// + /// . + /// OneCollector connection string. + /// Callback action for configuring . + /// The supplied for call + /// chaining. + public static LoggerProviderBuilder AddOneCollectorExporter( + this LoggerProviderBuilder builder, + string connectionString, + Action configure) + { + Guard.ThrowIfNullOrWhitespace(connectionString); + + return AddOneCollectorExporter(builder, name: null, connectionString, configuration: null, configure); + } + + /// + /// Add OneCollector exporter to the . + /// + /// Note: Batch options () are bound to the "BatchOptions" + /// sub-section of the supplied in the + /// parameter. + /// . + /// Configuration used to build and . + /// The supplied for call + /// chaining. + public static LoggerProviderBuilder AddOneCollectorExporter( + this LoggerProviderBuilder builder, + IConfiguration configuration) + { + Guard.ThrowIfNull(configuration); + + return AddOneCollectorExporter(builder, name: null, connectionString: null, configuration, configure: null); + } + + /// + /// Add OneCollector exporter to the . + /// + /// + /// . + /// + /// Callback action for configuring . + /// The supplied for call + /// chaining. + public static LoggerProviderBuilder AddOneCollectorExporter( + this LoggerProviderBuilder builder, + IConfiguration configuration, + Action configure) + { + Guard.ThrowIfNull(configuration); + + return AddOneCollectorExporter(builder, name: null, connectionString: null, configuration, configure); + } + + /// + /// Add OneCollector exporter to the . + /// + /// + /// . + /// Optional name which is used when retrieving + /// options. + /// Optional OneCollector connection + /// string. + /// Optional configuration used to build and . + /// Optional callback action for configuring . + /// The supplied for call + /// chaining. + public static LoggerProviderBuilder AddOneCollectorExporter( + this LoggerProviderBuilder builder, + string? name, + string? connectionString, + IConfiguration? configuration, + Action? configure) + { + Guard.ThrowIfNull(builder); + + builder.ConfigureServices(services => + { + var processorBuilder = new OneCollectorLogExportProcessorBuilder(name, services, configuration); + + if (!string.IsNullOrWhiteSpace(connectionString)) + { + processorBuilder.SetConnectionString(connectionString!); + } + + configure?.Invoke(processorBuilder); + + builder.AddProcessor(processorBuilder.BuildProcessor); + }); + + return builder; + } +} diff --git a/src/OpenTelemetry.Exporter.OneCollector/Logs/OneCollectorOpenTelemetryLoggerOptionsExtensions.cs b/src/OpenTelemetry.Exporter.OneCollector/Logs/OneCollectorOpenTelemetryLoggerOptionsExtensions.cs index 1b4287def2..36169f0cc5 100644 --- a/src/OpenTelemetry.Exporter.OneCollector/Logs/OneCollectorOpenTelemetryLoggerOptionsExtensions.cs +++ b/src/OpenTelemetry.Exporter.OneCollector/Logs/OneCollectorOpenTelemetryLoggerOptionsExtensions.cs @@ -123,7 +123,7 @@ private static OpenTelemetryLoggerOptions AddOneCollectorExporter( { Guard.ThrowIfNull(options); - var builder = new OneCollectorLogExportProcessorBuilder(configuration); + var builder = new OneCollectorLogExportProcessorBuilder(name: null, services: null, configuration); if (!string.IsNullOrWhiteSpace(connectionString)) { @@ -132,10 +132,7 @@ private static OpenTelemetryLoggerOptions AddOneCollectorExporter( configure?.Invoke(builder); -#pragma warning disable CA2000 // Dispose objects before losing scope - options.AddProcessor( - builder.BuildProcessor()); -#pragma warning restore CA2000 // Dispose objects before losing scope + options.AddProcessor(builder.BuildProcessor); return options; } diff --git a/src/OpenTelemetry.Exporter.OneCollector/OneCollectorExporterTransportOptions.cs b/src/OpenTelemetry.Exporter.OneCollector/OneCollectorExporterTransportOptions.cs index 6f818948a5..28a14abb53 100644 --- a/src/OpenTelemetry.Exporter.OneCollector/OneCollectorExporterTransportOptions.cs +++ b/src/OpenTelemetry.Exporter.OneCollector/OneCollectorExporterTransportOptions.cs @@ -2,6 +2,9 @@ // SPDX-License-Identifier: Apache-2.0 using System.ComponentModel.DataAnnotations; +#if NETFRAMEWORK +using System.Net.Http; +#endif namespace OpenTelemetry.Exporter.OneCollector; @@ -14,6 +17,8 @@ public sealed class OneCollectorExporterTransportOptions internal const int DefaultMaxPayloadSizeInBytes = 1024 * 1024 * 4; internal const int DefaultMaxNumberOfItemsPerPayload = 1500; + private static readonly Func DefaultHttpClientFactory = () => new HttpClient(); + internal OneCollectorExporterTransportOptions() { } @@ -59,6 +64,23 @@ internal OneCollectorExporterTransportOptions() /// internal OneCollectorExporterHttpTransportCompressionType HttpCompression { get; set; } = OneCollectorExporterHttpTransportCompressionType.Deflate; + /// + /// Gets or sets the factory function called to create the instance that will be used at runtime to transmit + /// telemetry over HTTP transports. The returned instance will be reused for + /// all export invocations. + /// + /// + /// Note: The default behavior is an will be + /// instantiated directly. + /// + internal Func? HttpClientFactory { get; set; } + + internal HttpClient GetHttpClient() + { + return (this.HttpClientFactory ?? DefaultHttpClientFactory)() ?? throw new NotSupportedException("HttpClientFactory cannot return a null instance."); + } + internal void Validate() { if (this.Endpoint == null) diff --git a/src/OpenTelemetry.Exporter.OneCollector/OpenTelemetry.Exporter.OneCollector.csproj b/src/OpenTelemetry.Exporter.OneCollector/OpenTelemetry.Exporter.OneCollector.csproj index 8aceaff990..3a3a2cffb9 100644 --- a/src/OpenTelemetry.Exporter.OneCollector/OpenTelemetry.Exporter.OneCollector.csproj +++ b/src/OpenTelemetry.Exporter.OneCollector/OpenTelemetry.Exporter.OneCollector.csproj @@ -18,8 +18,13 @@ 1.8.0 + + $(OpenTelemetryCoreLatestPrereleaseVersion) + $(DefineConstants);EXPOSE_EXPERIMENTAL_FEATURES + + - + diff --git a/test/OpenTelemetry.Exporter.OneCollector.Tests/LogRecordCommonSchemaJsonSerializerTests.cs b/test/OpenTelemetry.Exporter.OneCollector.Tests/LogRecordCommonSchemaJsonSerializerTests.cs index d706e6355e..c3f2e75645 100644 --- a/test/OpenTelemetry.Exporter.OneCollector.Tests/LogRecordCommonSchemaJsonSerializerTests.cs +++ b/test/OpenTelemetry.Exporter.OneCollector.Tests/LogRecordCommonSchemaJsonSerializerTests.cs @@ -46,7 +46,10 @@ public void LogRecordLogLevelJsonTest(LogLevel logLevel, string severityText, in { string json = GetLogRecordJson(1, (index, logRecord) => { +#pragma warning disable CS0618 // Type or member is obsolete + // TODO: Update to use LogRecord.Severity logRecord.LogLevel = logLevel; +#pragma warning restore CS0618 // Type or member is obsolete }); Assert.Equal( diff --git a/test/OpenTelemetry.Exporter.OneCollector.Tests/OneCollectorLoggerProviderBuilderExtensionsTests.cs b/test/OpenTelemetry.Exporter.OneCollector.Tests/OneCollectorLoggerProviderBuilderExtensionsTests.cs new file mode 100644 index 0000000000..4650dfbef3 --- /dev/null +++ b/test/OpenTelemetry.Exporter.OneCollector.Tests/OneCollectorLoggerProviderBuilderExtensionsTests.cs @@ -0,0 +1,266 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using OpenTelemetry.Logs; +using Xunit; + +namespace OpenTelemetry.Exporter.OneCollector.Tests; + +public class OneCollectorLoggerProviderBuilderExtensionsTests +{ + [Fact] + public void ConfigureBatchOptionsTest() + { + int configurationInvocations = 0; + + using var loggerFactory = CreateLoggerFactoryWithOneCollectorExporter(builder => + { + builder.AddOneCollectorExporter( + "InstrumentationKey=token-extrainformation", + configure => configure.ConfigureBatchOptions(o => configurationInvocations++)); + }); + + Assert.Equal(1, configurationInvocations); + } + + [Fact] + public void ConfigureExporterTest() + { + OneCollectorExporter? exporterInstance = null; + + using var loggerFactory = CreateLoggerFactoryWithOneCollectorExporter(builder => + { + builder.AddOneCollectorExporter( + "InstrumentationKey=token-extrainformation", + configure => configure.ConfigureExporter(exporter => exporterInstance = exporter)); + }); + + Assert.NotNull(exporterInstance); + + using var payloadTransmittedRegistration = exporterInstance.RegisterPayloadTransmittedCallback(OnPayloadTransmitted); + + Assert.NotNull(payloadTransmittedRegistration); + + static void OnPayloadTransmitted(in OneCollectorExporterPayloadTransmittedCallbackArguments args) + { + } + } + + [Fact] + public void ConfigureSerializationOptionsTest() + { + int configurationInvocations = 0; + + using var loggerFactory = CreateLoggerFactoryWithOneCollectorExporter(builder => + { + builder.AddOneCollectorExporter( + "InstrumentationKey=token-extrainformation", + configure => configure.ConfigureSerializationOptions(o => configurationInvocations++)); + }); + + Assert.Equal(1, configurationInvocations); + } + + [Fact] + public void ConfigureTransportOptionsTest() + { + int configurationInvocations = 0; + + using var loggerFactory = CreateLoggerFactoryWithOneCollectorExporter(builder => + { + builder.AddOneCollectorExporter( + "InstrumentationKey=token-extrainformation", + configure => configure.ConfigureTransportOptions(o => configurationInvocations++)); + }); + + Assert.Equal(1, configurationInvocations); + } + + [Fact] + public void SetConnectionStringTest() + { + OneCollectorLogExporterOptions? options = null; + + using var loggerFactory = CreateLoggerFactoryWithOneCollectorExporter( + builder => + { + builder.AddOneCollectorExporter( + configure => configure.SetConnectionString("InstrumentationKey=token-extrainformation")); + }, + services => services.Configure(o => options = o)); + + Assert.NotNull(options); + Assert.Equal("InstrumentationKey=token-extrainformation", options.ConnectionString); + } + + [Fact] + public void SetDefaultEventNameTest() + { + OneCollectorLogExporterOptions? options = null; + + using var loggerFactory = CreateLoggerFactoryWithOneCollectorExporter( + builder => + { + builder.AddOneCollectorExporter( + "InstrumentationKey=token-extrainformation", + configure => configure.SetDefaultEventName("MyDefaultEventName")); + }, + services => services.Configure(o => options = o)); + + Assert.NotNull(options); + Assert.Equal("MyDefaultEventName", options.DefaultEventName); + } + + [Theory] + [InlineData(null)] + [InlineData("CustomName")] + public void ConfigurationBindingTest(string? name) + { + var configuration = new ConfigurationBuilder() + .AddInMemoryCollection(new Dictionary() + { + ["ConnectionString"] = "InstrumentationKey=token-extrainformation", + ["SerializationOptions:ExceptionStackTraceHandling"] = "IncludeAsString", + ["TransportOptions:Endpoint"] = "http://myendpoint.com/", + ["BatchOptions:ScheduledDelayMilliseconds"] = "18", + }) + .Build(); + + OneCollectorLogExporterOptions? exporterOptions = null; + BatchExportLogRecordProcessorOptions? batchOptions = null; + + using var loggerFactory = CreateLoggerFactoryWithOneCollectorExporter( + builder => builder.AddOneCollectorExporter(name, connectionString: null, configuration: configuration, configure: null), + services => + { + services.Configure(name, o => exporterOptions = o); + services.Configure(name, o => batchOptions = o); + }); + + Assert.NotNull(exporterOptions); + Assert.NotNull(batchOptions); + + Assert.Equal("InstrumentationKey=token-extrainformation", exporterOptions.ConnectionString); + Assert.Equal(OneCollectorExporterSerializationExceptionStackTraceHandlingType.IncludeAsString, exporterOptions.SerializationOptions.ExceptionStackTraceHandling); + Assert.Equal("http://myendpoint.com/", exporterOptions.TransportOptions.Endpoint.ToString()); + Assert.Equal(18, batchOptions.ScheduledDelayMilliseconds); + } + + [Fact] + public void InstrumentationKeyAndTenantTokenValidationTest() + { + { + using var loggerFactory = CreateLoggerFactoryWithOneCollectorExporter(builder => + { + builder.AddOneCollectorExporter("InstrumentationKey=token-extrainformation"); + }); + } + + { + using var loggerFactory = CreateLoggerFactoryWithOneCollectorExporter(builder => + { + builder.AddOneCollectorExporter(configure => configure.SetConnectionString("InstrumentationKey=token-extrainformation")); + }); + } + + Assert.Throws(() => + { + using var loggerFactory = CreateLoggerFactoryWithOneCollectorExporter(builder => + { + builder.AddOneCollectorExporter(configure => { }); + }); + }); + + Assert.Throws(() => + { + using var loggerFactory = CreateLoggerFactoryWithOneCollectorExporter(builder => + { + builder.AddOneCollectorExporter("InstrumentationKey=invalidinstrumentationkey"); + }); + }); + + Assert.Throws(() => + { + using var loggerFactory = CreateLoggerFactoryWithOneCollectorExporter(builder => + { + builder.AddOneCollectorExporter("UnknownKey=invalidinstrumentationkey"); + }); + }); + } + + [Fact] + public void OptionsTest() + { + int configurationInvocations = 0; + + using var loggerFactory = CreateLoggerFactoryWithOneCollectorExporter( + builder => + { + builder.AddOneCollectorExporter(); + }, + services => + { + services.Configure( + o => + { + o.ConnectionString = "InstrumentationKey=token-extrainformation"; + configurationInvocations++; + }); + + services.Configure( + o => configurationInvocations++); + }); + + Assert.Equal(2, configurationInvocations); + } + + [Fact] + public void NamedOptionsTest() + { + int configurationInvocations = 0; + + using var loggerFactory = CreateLoggerFactoryWithOneCollectorExporter( + builder => + { + builder.AddOneCollectorExporter( + name: "MyOneCollectorExporter", + connectionString: null, + configuration: null, + configure: null); + }, + services => + { + services.Configure( + "MyOneCollectorExporter", + o => + { + o.ConnectionString = "InstrumentationKey=token-extrainformation"; + configurationInvocations++; + }); + + services.Configure( + "MyOneCollectorExporter", + o => configurationInvocations++); + }); + + Assert.Equal(2, configurationInvocations); + } + + private static ILoggerFactory CreateLoggerFactoryWithOneCollectorExporter( + Action configureLogging, + Action? configureServices = null) + { + return LoggerFactory.Create(builder => + { + builder.AddOpenTelemetry(); + + builder.Services.ConfigureOpenTelemetryLoggerProvider(configureLogging); + + configureServices?.Invoke(builder.Services); + }); + } +} From 563873a37c7cd76499952a7d720bb52ef524cd57 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Mon, 10 Jun 2024 14:03:23 -0700 Subject: [PATCH 2/6] CHANGELOG patch. --- src/OpenTelemetry.Exporter.OneCollector/CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/OpenTelemetry.Exporter.OneCollector/CHANGELOG.md b/src/OpenTelemetry.Exporter.OneCollector/CHANGELOG.md index 13615601c1..a0f00e0852 100644 --- a/src/OpenTelemetry.Exporter.OneCollector/CHANGELOG.md +++ b/src/OpenTelemetry.Exporter.OneCollector/CHANGELOG.md @@ -3,10 +3,10 @@ ## Unreleased * Update OpenTelemetry SDK version to `1.9.0-rc.1`. - ([#XXXX](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/XXXX)) + ([#1876](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/1876)) * Added `LoggerProviderBuilder.AddOneCollectorExporter` registration extension. - ([#XXXX](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/XXXX)) + ([#1876](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/1876)) ## 1.8.0 From a3f7496818b18941f5cc8de17e38330f4e4874b0 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Mon, 10 Jun 2024 14:31:11 -0700 Subject: [PATCH 3/6] Lint. --- .../OneCollectorLoggerProviderBuilderExtensionsTests.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/test/OpenTelemetry.Exporter.OneCollector.Tests/OneCollectorLoggerProviderBuilderExtensionsTests.cs b/test/OpenTelemetry.Exporter.OneCollector.Tests/OneCollectorLoggerProviderBuilderExtensionsTests.cs index 4650dfbef3..92218513cc 100644 --- a/test/OpenTelemetry.Exporter.OneCollector.Tests/OneCollectorLoggerProviderBuilderExtensionsTests.cs +++ b/test/OpenTelemetry.Exporter.OneCollector.Tests/OneCollectorLoggerProviderBuilderExtensionsTests.cs @@ -4,7 +4,6 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; using OpenTelemetry.Logs; using Xunit; From 6ba5d53145fc2f4e8a7279e80c8cf439f1fad4f7 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Mon, 10 Jun 2024 14:36:29 -0700 Subject: [PATCH 4/6] Warning fix. --- .../LogRecordCommonSchemaJsonHttpPostBenchmarks.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/OpenTelemetry.Exporter.OneCollector.Benchmarks/LogRecordCommonSchemaJsonHttpPostBenchmarks.cs b/test/OpenTelemetry.Exporter.OneCollector.Benchmarks/LogRecordCommonSchemaJsonHttpPostBenchmarks.cs index 51643933c0..91ec2c8306 100644 --- a/test/OpenTelemetry.Exporter.OneCollector.Benchmarks/LogRecordCommonSchemaJsonHttpPostBenchmarks.cs +++ b/test/OpenTelemetry.Exporter.OneCollector.Benchmarks/LogRecordCommonSchemaJsonHttpPostBenchmarks.cs @@ -91,7 +91,10 @@ private static LogRecord CreateLogRecord(int index) logRecord.Timestamp = DateTime.UtcNow; logRecord.CategoryName = typeof(LogRecordCommonSchemaJsonHttpPostBenchmarks).FullName; +#pragma warning disable CS0618 // Type or member is obsolete + // TODO: Update to use LogRecord.Severity logRecord.LogLevel = LogLevel.Information; +#pragma warning restore CS0618 // Type or member is obsolete if (index % 2 == 0) { From a053f4ed7c014b39eac46438926561bb283c6832 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Mon, 10 Jun 2024 16:57:32 -0700 Subject: [PATCH 5/6] Tweaks. --- .../Logs/OneCollectorLoggerProviderBuilderExtensions.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/OpenTelemetry.Exporter.OneCollector/Logs/OneCollectorLoggerProviderBuilderExtensions.cs b/src/OpenTelemetry.Exporter.OneCollector/Logs/OneCollectorLoggerProviderBuilderExtensions.cs index deb99885ac..ea580ec2b1 100644 --- a/src/OpenTelemetry.Exporter.OneCollector/Logs/OneCollectorLoggerProviderBuilderExtensions.cs +++ b/src/OpenTelemetry.Exporter.OneCollector/Logs/OneCollectorLoggerProviderBuilderExtensions.cs @@ -76,6 +76,7 @@ public static LoggerProviderBuilder AddOneCollectorExporter( Action configure) { Guard.ThrowIfNullOrWhitespace(connectionString); + Guard.ThrowIfNull(configure); return AddOneCollectorExporter(builder, name: null, connectionString, configuration: null, configure); } @@ -124,6 +125,7 @@ public static LoggerProviderBuilder AddOneCollectorExporter( Action configure) { Guard.ThrowIfNull(configuration); + Guard.ThrowIfNull(configure); return AddOneCollectorExporter(builder, name: null, connectionString: null, configuration, configure); } From f1f4a4c2354a6b41496aa4c034ab4b876858de92 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Mon, 10 Jun 2024 16:59:29 -0700 Subject: [PATCH 6/6] Tweak. --- .../Logs/OneCollectorLoggerProviderBuilderExtensions.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/OpenTelemetry.Exporter.OneCollector/Logs/OneCollectorLoggerProviderBuilderExtensions.cs b/src/OpenTelemetry.Exporter.OneCollector/Logs/OneCollectorLoggerProviderBuilderExtensions.cs index ea580ec2b1..5ac3e607c5 100644 --- a/src/OpenTelemetry.Exporter.OneCollector/Logs/OneCollectorLoggerProviderBuilderExtensions.cs +++ b/src/OpenTelemetry.Exporter.OneCollector/Logs/OneCollectorLoggerProviderBuilderExtensions.cs @@ -157,7 +157,7 @@ public static LoggerProviderBuilder AddOneCollectorExporter( { Guard.ThrowIfNull(builder); - builder.ConfigureServices(services => + return builder.ConfigureServices(services => { var processorBuilder = new OneCollectorLogExportProcessorBuilder(name, services, configuration); @@ -170,7 +170,5 @@ public static LoggerProviderBuilder AddOneCollectorExporter( builder.AddProcessor(processorBuilder.BuildProcessor); }); - - return builder; } }