From b9be07a27d4bb2384fa28a7d29dea9ebe3c32ca1 Mon Sep 17 00:00:00 2001 From: Rajkumar Rangaraj Date: Mon, 2 Dec 2024 16:21:30 -0800 Subject: [PATCH 1/7] [otlp] Remove the Google.Protobuf / Grpc packages, and replace the logs and metrics with the new implementation (#6005) Co-authored-by: Mikel Blanchard --- .editorconfig | 2 +- OpenTelemetry.sln | 6 + .../ExportClient/BaseOtlpGrpcExportClient.cs | 67 --- .../ExportClient/BaseOtlpHttpExportClient.cs | 110 ---- .../ExportClient/IExportClient.cs | 8 +- .../ExportClient/IProtobufExportClient.cs | 30 - ...tlpExportClient.cs => OtlpExportClient.cs} | 19 +- ...xportClient.cs => OtlpGrpcExportClient.cs} | 4 +- .../ExportClient/OtlpGrpcLogExportClient.cs | 45 -- .../OtlpGrpcMetricsExportClient.cs | 45 -- ...xportClient.cs => OtlpHttpExportClient.cs} | 4 +- .../ExportClient/OtlpHttpLogExportClient.cs | 69 --- .../OtlpHttpMetricsExportClient.cs | 69 --- .../Implementation/ExportClient/OtlpRetry.cs | 57 +- .../Implementation/MetricItemExtensions.cs | 442 -------------- .../OtlpLogRecordTransformer.cs | 281 --------- .../Implementation/OtlpTagWriter.cs | 105 ---- .../Implementation/README.md | 7 - .../Implementation/ResourceExtensions.cs | 36 -- ...terPersistentStorageTransmissionHandler.cs | 54 +- .../OtlpExporterRetryTransmissionHandler.cs | 8 +- .../OtlpExporterTransmissionHandler.cs | 32 +- ...terPersistentStorageTransmissionHandler.cs | 149 ----- ...bufOtlpExporterRetryTransmissionHandler.cs | 35 -- ...ProtobufOtlpExporterTransmissionHandler.cs | 136 ----- ...etry.Exporter.OpenTelemetryProtocol.csproj | 13 - .../OtlpExporterOptionsExtensions.cs | 154 +---- .../OtlpLogExporter.cs | 81 ++- .../OtlpLogExporterHelperExtensions.cs | 14 +- .../OtlpMetricExporter.cs | 70 ++- .../OtlpMetricExporterExtensions.cs | 11 +- .../OtlpTraceExporter.cs | 6 +- .../ProtobufOtlpLogExporter.cs | 127 ---- .../ProtobufOtlpMetricExporter.cs | 120 ---- src/Shared/Proto/README.md | 5 + .../Proto}/google/rpc/error_details.proto | 0 .../Proto}/google/rpc/status.proto | 0 .../opentelemetry/proto/collector/README.md | 0 .../collector/logs/v1/logs_service.proto | 0 .../collector/logs/v1/logs_service_http.yaml | 0 .../metrics/v1/metrics_service.proto | 0 .../metrics/v1/metrics_service_http.yaml | 0 .../v1experimental/profiles_service.proto | 0 .../v1experimental/profiles_service_http.yaml | 0 .../collector/trace/v1/trace_service.proto | 0 .../trace/v1/trace_service_http.yaml | 0 .../proto/common/v1/common.proto | 0 .../opentelemetry/proto/logs/v1/logs.proto | 0 .../proto/metrics/v1/metrics.proto | 0 .../v1experimental/pprofextended.proto | 0 .../profiles/v1experimental/profiles.proto | 0 .../proto/resource/v1/resource.proto | 0 .../opentelemetry/proto/trace/v1/trace.proto | 0 test/Benchmarks/Benchmarks.csproj | 15 +- .../Exporter/OtlpGrpcExporterBenchmarks.cs | 2 +- .../Exporter/OtlpHttpExporterBenchmarks.cs | 2 +- .../Exporter/OtlpLogExporterBenchmarks.cs | 2 +- .../Exporter/OtlpTraceExporterBenchmarks.cs | 2 +- test/Benchmarks/TestTraceServiceClient.cs | 17 - .../OtlpHttpTraceExportClientTests.cs | 6 +- .../ResourceProtoSerializerTests.cs | 66 --- .../MockCollectorIntegrationTests.cs | 20 +- ...xporter.OpenTelemetryProtocol.Tests.csproj | 10 + .../OtlpAttributeTests.cs | 21 +- .../OtlpExporterOptionsExtensionsTests.cs | 144 +---- ...tTests.cs => OtlpHttpExportClientTests.cs} | 17 +- .../OtlpLogExporterTests.cs | 543 +++++------------- .../OtlpMetricsExporterTests.cs | 270 +++------ .../OtlpResourceTests.cs | 28 +- .../OtlpRetryTests.cs | 57 +- .../OtlpTraceExporterTests.cs | 4 +- .../TestExportClient.cs | 4 +- .../TestProtobufExportClient.cs | 40 -- 73 files changed, 514 insertions(+), 3177 deletions(-) delete mode 100644 src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/BaseOtlpGrpcExportClient.cs delete mode 100644 src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/BaseOtlpHttpExportClient.cs delete mode 100644 src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/IProtobufExportClient.cs rename src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/{ProtobufOtlpExportClient.cs => OtlpExportClient.cs} (84%) rename src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/{ProtobufOtlpGrpcExportClient.cs => OtlpGrpcExportClient.cs} (97%) delete mode 100644 src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/OtlpGrpcLogExportClient.cs delete mode 100644 src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/OtlpGrpcMetricsExportClient.cs rename src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/{ProtobufOtlpHttpExportClient.cs => OtlpHttpExportClient.cs} (90%) delete mode 100644 src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/OtlpHttpLogExportClient.cs delete mode 100644 src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/OtlpHttpMetricsExportClient.cs delete mode 100644 src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/MetricItemExtensions.cs delete mode 100644 src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/OtlpLogRecordTransformer.cs delete mode 100644 src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/OtlpTagWriter.cs delete mode 100644 src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/README.md delete mode 100644 src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ResourceExtensions.cs delete mode 100644 src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Transmission/ProtobufOtlpExporterPersistentStorageTransmissionHandler.cs delete mode 100644 src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Transmission/ProtobufOtlpExporterRetryTransmissionHandler.cs delete mode 100644 src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Transmission/ProtobufOtlpExporterTransmissionHandler.cs delete mode 100644 src/OpenTelemetry.Exporter.OpenTelemetryProtocol/ProtobufOtlpLogExporter.cs delete mode 100644 src/OpenTelemetry.Exporter.OpenTelemetryProtocol/ProtobufOtlpMetricExporter.cs create mode 100644 src/Shared/Proto/README.md rename src/{OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation => Shared/Proto}/google/rpc/error_details.proto (100%) rename src/{OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation => Shared/Proto}/google/rpc/status.proto (100%) rename src/{OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation => Shared/Proto}/opentelemetry/proto/collector/README.md (100%) rename src/{OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation => Shared/Proto}/opentelemetry/proto/collector/logs/v1/logs_service.proto (100%) rename src/{OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation => Shared/Proto}/opentelemetry/proto/collector/logs/v1/logs_service_http.yaml (100%) rename src/{OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation => Shared/Proto}/opentelemetry/proto/collector/metrics/v1/metrics_service.proto (100%) rename src/{OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation => Shared/Proto}/opentelemetry/proto/collector/metrics/v1/metrics_service_http.yaml (100%) rename src/{OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation => Shared/Proto}/opentelemetry/proto/collector/profiles/v1experimental/profiles_service.proto (100%) rename src/{OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation => Shared/Proto}/opentelemetry/proto/collector/profiles/v1experimental/profiles_service_http.yaml (100%) rename src/{OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation => Shared/Proto}/opentelemetry/proto/collector/trace/v1/trace_service.proto (100%) rename src/{OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation => Shared/Proto}/opentelemetry/proto/collector/trace/v1/trace_service_http.yaml (100%) rename src/{OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation => Shared/Proto}/opentelemetry/proto/common/v1/common.proto (100%) rename src/{OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation => Shared/Proto}/opentelemetry/proto/logs/v1/logs.proto (100%) rename src/{OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation => Shared/Proto}/opentelemetry/proto/metrics/v1/metrics.proto (100%) rename src/{OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation => Shared/Proto}/opentelemetry/proto/profiles/v1experimental/pprofextended.proto (100%) rename src/{OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation => Shared/Proto}/opentelemetry/proto/profiles/v1experimental/profiles.proto (100%) rename src/{OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation => Shared/Proto}/opentelemetry/proto/resource/v1/resource.proto (100%) rename src/{OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation => Shared/Proto}/opentelemetry/proto/trace/v1/trace.proto (100%) delete mode 100644 test/Benchmarks/TestTraceServiceClient.cs delete mode 100644 test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/Implementation/Serializer/ResourceProtoSerializerTests.cs rename test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/{BaseOtlpHttpExportClientTests.cs => OtlpHttpExportClientTests.cs} (73%) delete mode 100644 test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/TestProtobufExportClient.cs diff --git a/.editorconfig b/.editorconfig index 45d0806a20b..cdfa4e32468 100644 --- a/.editorconfig +++ b/.editorconfig @@ -156,7 +156,7 @@ dotnet_diagnostic.IDE0005.severity = warning # RS0041: Public members should not use oblivious types dotnet_diagnostic.RS0041.severity = suggestion -[obj/**.cs] +[**/obj/**.cs] generated_code = true [*.csproj] diff --git a/OpenTelemetry.sln b/OpenTelemetry.sln index 1b3928c1163..c109bf10753 100644 --- a/OpenTelemetry.sln +++ b/OpenTelemetry.sln @@ -345,6 +345,11 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "scripts", "scripts", "{4498 EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "exemplars", "docs\metrics\exemplars\exemplars.csproj", "{79C12C80-B27B-41FB-AE79-A3BB74CFA782}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Proto", "Proto", "{02EA681E-C7D8-13C7-8484-4AC65E1B71E8}" + ProjectSection(SolutionItems) = preProject + src\Shared\Proto\README.md = src\Shared\Proto\README.md + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -661,6 +666,7 @@ Global {993E65E5-E71B-40FD-871C-60A9EBD59724} = {A49299FB-C5CD-4E0E-B7E1-B7867BBD67CC} {44982E0D-C8C6-42DC-9F8F-714981F27CE6} = {7CB2F02E-03FA-4FFF-89A5-C51F107623FD} {79C12C80-B27B-41FB-AE79-A3BB74CFA782} = {3277B1C0-BDFE-4460-9B0D-D9A661FB48DB} + {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} = {A49299FB-C5CD-4E0E-B7E1-B7867BBD67CC} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {55639B5C-0770-4A22-AB56-859604650521} diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/BaseOtlpGrpcExportClient.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/BaseOtlpGrpcExportClient.cs deleted file mode 100644 index 2eab9778852..00000000000 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/BaseOtlpGrpcExportClient.cs +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -using Grpc.Core; -using OpenTelemetry.Internal; -#if NETSTANDARD2_1 || NET -using Grpc.Net.Client; -#endif - -namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.ExportClient; - -/// Base class for sending OTLP export request over gRPC. -/// Type of export request. -internal abstract class BaseOtlpGrpcExportClient : IExportClient -{ - protected static readonly ExportClientGrpcResponse SuccessExportResponse - = new( - success: true, - deadlineUtc: default, - exception: null, - status: null, - grpcStatusDetailsHeader: null); - - protected BaseOtlpGrpcExportClient(OtlpExporterOptions options) - { - Guard.ThrowIfNull(options); - Guard.ThrowIfInvalidTimeout(options.TimeoutMilliseconds); - - this.Endpoint = new UriBuilder(options.Endpoint).Uri; - this.Headers = options.GetMetadataFromHeaders(); - this.TimeoutMilliseconds = options.TimeoutMilliseconds; - } - -#if NETSTANDARD2_1 || NET - internal GrpcChannel? Channel { get; set; } -#else - internal Channel? Channel { get; set; } -#endif - - internal Uri Endpoint { get; } - - internal Metadata Headers { get; } - - internal int TimeoutMilliseconds { get; } - - /// - public abstract ExportClientResponse SendExportRequest(TRequest request, DateTime deadlineUtc, CancellationToken cancellationToken = default); - - /// - public virtual bool Shutdown(int timeoutMilliseconds) - { - if (this.Channel == null) - { - return true; - } - - if (timeoutMilliseconds == -1) - { - this.Channel.ShutdownAsync().Wait(); - return true; - } - else - { - return Task.WaitAny(new Task[] { this.Channel.ShutdownAsync(), Task.Delay(timeoutMilliseconds) }) == 0; - } - } -} diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/BaseOtlpHttpExportClient.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/BaseOtlpHttpExportClient.cs deleted file mode 100644 index 7e975fa1339..00000000000 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/BaseOtlpHttpExportClient.cs +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -#if NETFRAMEWORK -using System.Net.Http; -#endif -using OpenTelemetry.Internal; - -namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.ExportClient; - -/// Base class for sending OTLP export request over HTTP. -/// Type of export request. -internal abstract class BaseOtlpHttpExportClient : IExportClient -{ - private static readonly ExportClientHttpResponse SuccessExportResponse = new ExportClientHttpResponse(success: true, deadlineUtc: default, response: null, exception: null); -#if NET - private readonly bool synchronousSendSupportedByCurrentPlatform; -#endif - - protected BaseOtlpHttpExportClient(OtlpExporterOptions options, HttpClient httpClient, string signalPath) - { - Guard.ThrowIfNull(options); - Guard.ThrowIfNull(httpClient); - Guard.ThrowIfNull(signalPath); - Guard.ThrowIfInvalidTimeout(options.TimeoutMilliseconds); - - Uri exporterEndpoint = options.AppendSignalPathToEndpoint - ? options.Endpoint.AppendPathIfNotPresent(signalPath) - : options.Endpoint; - this.Endpoint = new UriBuilder(exporterEndpoint).Uri; - this.Headers = options.GetHeaders>((d, k, v) => d.Add(k, v)); - this.HttpClient = httpClient; - -#if NET - // See: https://github.com/dotnet/runtime/blob/280f2a0c60ce0378b8db49adc0eecc463d00fe5d/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.AnyMobile.cs#L767 - this.synchronousSendSupportedByCurrentPlatform = !OperatingSystem.IsAndroid() - && !OperatingSystem.IsIOS() - && !OperatingSystem.IsTvOS() - && !OperatingSystem.IsBrowser(); -#endif - } - - internal HttpClient HttpClient { get; } - - internal Uri Endpoint { get; set; } - - internal IReadOnlyDictionary Headers { get; } - - /// - public ExportClientResponse SendExportRequest(TRequest request, DateTime deadlineUtc, CancellationToken cancellationToken = default) - { - try - { - using var httpRequest = this.CreateHttpRequest(request); - - using var httpResponse = this.SendHttpRequest(httpRequest, cancellationToken); - - try - { - httpResponse.EnsureSuccessStatusCode(); - } - catch (HttpRequestException ex) - { - return new ExportClientHttpResponse(success: false, deadlineUtc: deadlineUtc, response: httpResponse, ex); - } - - // We do not need to return back response and deadline for successful response so using cached value. - return SuccessExportResponse; - } - catch (HttpRequestException ex) - { - OpenTelemetryProtocolExporterEventSource.Log.FailedToReachCollector(this.Endpoint, ex); - - return new ExportClientHttpResponse(success: false, deadlineUtc: deadlineUtc, response: null, exception: ex); - } - } - - /// - public bool Shutdown(int timeoutMilliseconds) - { - this.HttpClient.CancelPendingRequests(); - return true; - } - - protected abstract HttpContent CreateHttpContent(TRequest exportRequest); - - protected HttpRequestMessage CreateHttpRequest(TRequest exportRequest) - { - var request = new HttpRequestMessage(HttpMethod.Post, this.Endpoint); - foreach (var header in this.Headers) - { - request.Headers.Add(header.Key, header.Value); - } - - request.Content = this.CreateHttpContent(exportRequest); - - return request; - } - - protected HttpResponseMessage SendHttpRequest(HttpRequestMessage request, CancellationToken cancellationToken) - { -#if NET - return this.synchronousSendSupportedByCurrentPlatform - ? this.HttpClient.Send(request, cancellationToken) - : this.HttpClient.SendAsync(request, cancellationToken).GetAwaiter().GetResult(); -#else - return this.HttpClient.SendAsync(request, cancellationToken).GetAwaiter().GetResult(); -#endif - } -} diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/IExportClient.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/IExportClient.cs index 26c7c5e33c0..9df60c0e41c 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/IExportClient.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/IExportClient.cs @@ -4,17 +4,17 @@ namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.ExportClient; /// Export client interface. -/// Type of export request. -internal interface IExportClient +internal interface IExportClient { /// /// Method for sending export request to the server. /// - /// The request to send to the server. + /// The request body to send to the server. + /// length of the content. /// The deadline time in utc for export request to finish. /// An optional token for canceling the call. /// . - ExportClientResponse SendExportRequest(TRequest request, DateTime deadlineUtc, CancellationToken cancellationToken = default); + ExportClientResponse SendExportRequest(byte[] buffer, int contentLength, DateTime deadlineUtc, CancellationToken cancellationToken = default); /// /// Method for shutting down the export client. diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/IProtobufExportClient.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/IProtobufExportClient.cs deleted file mode 100644 index 617e5122134..00000000000 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/IProtobufExportClient.cs +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.ExportClient; - -/// Export client interface. -internal interface IProtobufExportClient -{ - /// - /// Method for sending export request to the server. - /// - /// The request body to send to the server. - /// length of the content. - /// The deadline time in utc for export request to finish. - /// An optional token for canceling the call. - /// . - ExportClientResponse SendExportRequest(byte[] buffer, int contentLength, DateTime deadlineUtc, CancellationToken cancellationToken = default); - - /// - /// Method for shutting down the export client. - /// - /// - /// The number of milliseconds to wait, or Timeout.Infinite to - /// wait indefinitely. - /// - /// - /// Returns true if shutdown succeeded; otherwise, false. - /// - bool Shutdown(int timeoutMilliseconds); -} diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/ProtobufOtlpExportClient.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/OtlpExportClient.cs similarity index 84% rename from src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/ProtobufOtlpExportClient.cs rename to src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/OtlpExportClient.cs index 519afcd548a..7347c69b271 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/ProtobufOtlpExportClient.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/OtlpExportClient.cs @@ -9,14 +9,14 @@ namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.ExportClient; -internal abstract class ProtobufOtlpExportClient : IProtobufExportClient +internal abstract class OtlpExportClient : IExportClient { private static readonly Version Http2RequestVersion = new(2, 0); #if NET private static readonly bool SynchronousSendSupportedByCurrentPlatform; - static ProtobufOtlpExportClient() + static OtlpExportClient() { #if NET // See: https://github.com/dotnet/runtime/blob/280f2a0c60ce0378b8db49adc0eecc463d00fe5d/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.AnyMobile.cs#L767 @@ -28,13 +28,24 @@ static ProtobufOtlpExportClient() } #endif - protected ProtobufOtlpExportClient(OtlpExporterOptions options, HttpClient httpClient, string signalPath) + protected OtlpExportClient(OtlpExporterOptions options, HttpClient httpClient, string signalPath) { Guard.ThrowIfNull(options); Guard.ThrowIfNull(httpClient); Guard.ThrowIfNull(signalPath); - Uri exporterEndpoint = options.Endpoint.AppendPathIfNotPresent(signalPath); + Uri exporterEndpoint; + if (options.Protocol == OtlpExportProtocol.Grpc) + { + exporterEndpoint = options.Endpoint.AppendPathIfNotPresent(signalPath); + } + else + { + exporterEndpoint = options.AppendSignalPathToEndpoint + ? options.Endpoint.AppendPathIfNotPresent(signalPath) + : options.Endpoint; + } + this.Endpoint = new UriBuilder(exporterEndpoint).Uri; this.Headers = options.GetHeaders>((d, k, v) => d.Add(k, v)); this.HttpClient = httpClient; diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/ProtobufOtlpGrpcExportClient.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/OtlpGrpcExportClient.cs similarity index 97% rename from src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/ProtobufOtlpGrpcExportClient.cs rename to src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/OtlpGrpcExportClient.cs index d70149e8344..1e645c2559f 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/ProtobufOtlpGrpcExportClient.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/OtlpGrpcExportClient.cs @@ -10,7 +10,7 @@ namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.ExportClient; /// Base class for sending OTLP export request over gRPC. -internal sealed class ProtobufOtlpGrpcExportClient : ProtobufOtlpExportClient +internal sealed class OtlpGrpcExportClient : OtlpExportClient { public const string GrpcStatusDetailsHeader = "grpc-status-details-bin"; private static readonly ExportClientHttpResponse SuccessExportResponse = new(success: true, deadlineUtc: default, response: null, exception: null); @@ -24,7 +24,7 @@ private static readonly ExportClientGrpcResponse DefaultExceptionExportClientGrp status: null, grpcStatusDetailsHeader: null); - public ProtobufOtlpGrpcExportClient(OtlpExporterOptions options, HttpClient httpClient, string signalPath) + public OtlpGrpcExportClient(OtlpExporterOptions options, HttpClient httpClient, string signalPath) : base(options, httpClient, signalPath) { } diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/OtlpGrpcLogExportClient.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/OtlpGrpcLogExportClient.cs deleted file mode 100644 index dc9d31feeb3..00000000000 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/OtlpGrpcLogExportClient.cs +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -using Grpc.Core; -using OtlpCollector = OpenTelemetry.Proto.Collector.Logs.V1; - -namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.ExportClient; - -/// Class for sending OTLP Logs export request over gRPC. -internal sealed class OtlpGrpcLogExportClient : BaseOtlpGrpcExportClient -{ - private readonly OtlpCollector.LogsService.LogsServiceClient logsClient; - - public OtlpGrpcLogExportClient(OtlpExporterOptions options, OtlpCollector.LogsService.LogsServiceClient? logsServiceClient = null) - : base(options) - { - if (logsServiceClient != null) - { - this.logsClient = logsServiceClient; - } - else - { - this.Channel = options.CreateChannel(); - this.logsClient = new OtlpCollector.LogsService.LogsServiceClient(this.Channel); - } - } - - /// - public override ExportClientResponse SendExportRequest(OtlpCollector.ExportLogsServiceRequest request, DateTime deadlineUtc, CancellationToken cancellationToken = default) - { - try - { - this.logsClient.Export(request, headers: this.Headers, deadline: deadlineUtc, cancellationToken: cancellationToken); - - // We do not need to return back response and deadline for successful response so using cached value. - return SuccessExportResponse; - } - catch (RpcException ex) - { - OpenTelemetryProtocolExporterEventSource.Log.FailedToReachCollector(this.Endpoint, ex); - - return new ExportClientGrpcResponse(success: false, deadlineUtc, ex, null, null); - } - } -} diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/OtlpGrpcMetricsExportClient.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/OtlpGrpcMetricsExportClient.cs deleted file mode 100644 index b67b0789da0..00000000000 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/OtlpGrpcMetricsExportClient.cs +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -using Grpc.Core; -using OtlpCollector = OpenTelemetry.Proto.Collector.Metrics.V1; - -namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.ExportClient; - -/// Class for sending OTLP metrics export request over gRPC. -internal sealed class OtlpGrpcMetricsExportClient : BaseOtlpGrpcExportClient -{ - private readonly OtlpCollector.MetricsService.MetricsServiceClient metricsClient; - - public OtlpGrpcMetricsExportClient(OtlpExporterOptions options, OtlpCollector.MetricsService.MetricsServiceClient? metricsServiceClient = null) - : base(options) - { - if (metricsServiceClient != null) - { - this.metricsClient = metricsServiceClient; - } - else - { - this.Channel = options.CreateChannel(); - this.metricsClient = new OtlpCollector.MetricsService.MetricsServiceClient(this.Channel); - } - } - - /// - public override ExportClientResponse SendExportRequest(OtlpCollector.ExportMetricsServiceRequest request, DateTime deadlineUtc, CancellationToken cancellationToken = default) - { - try - { - this.metricsClient.Export(request, headers: this.Headers, deadline: deadlineUtc, cancellationToken: cancellationToken); - - // We do not need to return back response and deadline for successful response so using cached value. - return SuccessExportResponse; - } - catch (RpcException ex) - { - OpenTelemetryProtocolExporterEventSource.Log.FailedToReachCollector(this.Endpoint, ex); - - return new ExportClientGrpcResponse(false, deadlineUtc, ex, null, null); - } - } -} diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/ProtobufOtlpHttpExportClient.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/OtlpHttpExportClient.cs similarity index 90% rename from src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/ProtobufOtlpHttpExportClient.cs rename to src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/OtlpHttpExportClient.cs index 11bc932fa5a..0d938a90f89 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/ProtobufOtlpHttpExportClient.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/OtlpHttpExportClient.cs @@ -9,12 +9,12 @@ namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.ExportClient; /// Class for sending OTLP trace export request over HTTP. -internal sealed class ProtobufOtlpHttpExportClient : ProtobufOtlpExportClient +internal sealed class OtlpHttpExportClient : OtlpExportClient { internal static readonly MediaTypeHeaderValue MediaHeaderValue = new("application/x-protobuf"); private static readonly ExportClientHttpResponse SuccessExportResponse = new(success: true, deadlineUtc: default, response: null, exception: null); - internal ProtobufOtlpHttpExportClient(OtlpExporterOptions options, HttpClient httpClient, string signalPath) + internal OtlpHttpExportClient(OtlpExporterOptions options, HttpClient httpClient, string signalPath) : base(options, httpClient, signalPath) { } diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/OtlpHttpLogExportClient.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/OtlpHttpLogExportClient.cs deleted file mode 100644 index 6347ece8f3b..00000000000 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/OtlpHttpLogExportClient.cs +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -using System.Net; -#if NETFRAMEWORK -using System.Net.Http; -#endif -using System.Net.Http.Headers; -using System.Runtime.CompilerServices; -using Google.Protobuf; -using OtlpCollector = OpenTelemetry.Proto.Collector.Logs.V1; - -namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.ExportClient; - -/// Class for sending OTLP log export request over HTTP. -internal sealed class OtlpHttpLogExportClient : BaseOtlpHttpExportClient -{ - internal const string MediaContentType = "application/x-protobuf"; - private const string LogsExportPath = "v1/logs"; - - public OtlpHttpLogExportClient(OtlpExporterOptions options, HttpClient httpClient) - : base(options, httpClient, LogsExportPath) - { - } - - protected override HttpContent CreateHttpContent(OtlpCollector.ExportLogsServiceRequest exportRequest) - { - return new ExportRequestContent(exportRequest); - } - - internal sealed class ExportRequestContent : HttpContent - { - private static readonly MediaTypeHeaderValue ProtobufMediaTypeHeader = new(MediaContentType); - - private readonly OtlpCollector.ExportLogsServiceRequest exportRequest; - - public ExportRequestContent(OtlpCollector.ExportLogsServiceRequest exportRequest) - { - this.exportRequest = exportRequest; - this.Headers.ContentType = ProtobufMediaTypeHeader; - } - -#if NET - protected override void SerializeToStream(Stream stream, TransportContext? context, CancellationToken cancellationToken) - { - this.SerializeToStreamInternal(stream); - } -#endif - - protected override Task SerializeToStreamAsync(Stream stream, TransportContext? context) - { - this.SerializeToStreamInternal(stream); - return Task.CompletedTask; - } - - protected override bool TryComputeLength(out long length) - { - // We can't know the length of the content being pushed to the output stream. - length = -1; - return false; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void SerializeToStreamInternal(Stream stream) - { - this.exportRequest.WriteTo(stream); - } - } -} diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/OtlpHttpMetricsExportClient.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/OtlpHttpMetricsExportClient.cs deleted file mode 100644 index 7bd2b1610d4..00000000000 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/OtlpHttpMetricsExportClient.cs +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -using System.Net; -#if NETFRAMEWORK -using System.Net.Http; -#endif -using System.Net.Http.Headers; -using System.Runtime.CompilerServices; -using Google.Protobuf; -using OtlpCollector = OpenTelemetry.Proto.Collector.Metrics.V1; - -namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.ExportClient; - -/// Class for sending OTLP metrics export request over HTTP. -internal sealed class OtlpHttpMetricsExportClient : BaseOtlpHttpExportClient -{ - internal const string MediaContentType = "application/x-protobuf"; - private const string MetricsExportPath = "v1/metrics"; - - public OtlpHttpMetricsExportClient(OtlpExporterOptions options, HttpClient httpClient) - : base(options, httpClient, MetricsExportPath) - { - } - - protected override HttpContent CreateHttpContent(OtlpCollector.ExportMetricsServiceRequest exportRequest) - { - return new ExportRequestContent(exportRequest); - } - - internal sealed class ExportRequestContent : HttpContent - { - private static readonly MediaTypeHeaderValue ProtobufMediaTypeHeader = new(MediaContentType); - - private readonly OtlpCollector.ExportMetricsServiceRequest exportRequest; - - public ExportRequestContent(OtlpCollector.ExportMetricsServiceRequest exportRequest) - { - this.exportRequest = exportRequest; - this.Headers.ContentType = ProtobufMediaTypeHeader; - } - -#if NET - protected override void SerializeToStream(Stream stream, TransportContext? context, CancellationToken cancellationToken) - { - this.SerializeToStreamInternal(stream); - } -#endif - - protected override Task SerializeToStreamAsync(Stream stream, TransportContext? context) - { - this.SerializeToStreamInternal(stream); - return Task.CompletedTask; - } - - protected override bool TryComputeLength(out long length) - { - // We can't know the length of the content being pushed to the output stream. - length = -1; - return false; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void SerializeToStreamInternal(Stream stream) - { - this.exportRequest.WriteTo(stream); - } - } -} diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/OtlpRetry.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/OtlpRetry.cs index 2639333bce7..89afecb4e9d 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/OtlpRetry.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/OtlpRetry.cs @@ -1,12 +1,9 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 -using System.Diagnostics; using System.Net; using System.Net.Http.Headers; -using Google.Rpc; -using Grpc.Core; -using Status = Google.Rpc.Status; +using OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.ExportClient.Grpc; namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.ExportClient; @@ -85,11 +82,7 @@ public static bool TryGetGrpcRetryResult(ExportClientGrpcResponse response, int { retryResult = default; - if (response.Exception is RpcException rpcException) - { - return TryGetRetryResult(rpcException.StatusCode, IsGrpcStatusCodeRetryable, response.DeadlineUtc, rpcException.Trailers, TryGetGrpcRetryDelay, retryDelayMilliseconds, out retryResult); - } - else if (response.Status != null) + if (response.Status != null) { var nextRetryDelayMilliseconds = retryDelayMilliseconds; @@ -98,7 +91,7 @@ public static bool TryGetGrpcRetryResult(ExportClientGrpcResponse response, int return false; } - var throttleDelay = Grpc.GrpcStatusDeserializer.TryGetGrpcRetryDelay(response.GrpcStatusDetailsHeader); + var throttleDelay = GrpcStatusDeserializer.TryGetGrpcRetryDelay(response.GrpcStatusDetailsHeader); var retryable = IsGrpcStatusCodeRetryable(response.Status.Value.StatusCode, throttleDelay.HasValue); if (!retryable) @@ -203,32 +196,6 @@ private static int CalculateNextRetryDelay(int nextRetryDelayMilliseconds) return Convert.ToInt32(nextMilliseconds); } - private static TimeSpan? TryGetGrpcRetryDelay(StatusCode statusCode, Metadata trailers) - { - Debug.Assert(trailers != null, "trailers was null"); - - if (statusCode != StatusCode.ResourceExhausted && statusCode != StatusCode.Unavailable) - { - return null; - } - - var statusDetails = trailers!.Get(GrpcStatusDetailsHeader); - if (statusDetails != null && statusDetails.IsBinary) - { - var status = Status.Parser.ParseFrom(statusDetails.ValueBytes); - foreach (var item in status.Details) - { - var success = item.TryUnpack(out var retryInfo); - if (success) - { - return retryInfo.RetryDelay.ToTimeSpan(); - } - } - } - - return null; - } - private static TimeSpan? TryGetHttpRetryDelay(HttpStatusCode statusCode, HttpResponseHeaders? responseHeaders) { #if NETSTANDARD2_1_OR_GREATER || NET @@ -258,24 +225,6 @@ private static bool IsGrpcStatusCodeRetryable(StatusCode statusCode, bool hasRet } } - private static bool IsGrpcStatusCodeRetryable(Grpc.StatusCode statusCode, bool hasRetryDelay) - { - switch (statusCode) - { - case Grpc.StatusCode.Cancelled: - case Grpc.StatusCode.DeadlineExceeded: - case Grpc.StatusCode.Aborted: - case Grpc.StatusCode.OutOfRange: - case Grpc.StatusCode.Unavailable: - case Grpc.StatusCode.DataLoss: - return true; - case Grpc.StatusCode.ResourceExhausted: - return hasRetryDelay; - default: - return false; - } - } - private static bool IsHttpStatusCodeRetryable(HttpStatusCode statusCode, bool hasRetryDelay) { switch (statusCode) diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/MetricItemExtensions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/MetricItemExtensions.cs deleted file mode 100644 index ea014ec5b29..00000000000 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/MetricItemExtensions.cs +++ /dev/null @@ -1,442 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -using System.Collections.Concurrent; -using System.Diagnostics; -using System.Runtime.CompilerServices; -using Google.Protobuf; -using Google.Protobuf.Collections; -using OpenTelemetry.Metrics; -using OpenTelemetry.Proto.Metrics.V1; -using AggregationTemporality = OpenTelemetry.Metrics.AggregationTemporality; -using Metric = OpenTelemetry.Metrics.Metric; -using OtlpCollector = OpenTelemetry.Proto.Collector.Metrics.V1; -using OtlpCommon = OpenTelemetry.Proto.Common.V1; -using OtlpMetrics = OpenTelemetry.Proto.Metrics.V1; -using OtlpResource = OpenTelemetry.Proto.Resource.V1; - -namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation; - -internal static class MetricItemExtensions -{ - private static readonly ConcurrentBag MetricListPool = new(); - - internal static void AddMetrics( - this OtlpCollector.ExportMetricsServiceRequest request, - OtlpResource.Resource processResource, - in Batch metrics) - { - var metricsByLibrary = new Dictionary(); - var resourceMetrics = new ResourceMetrics - { - Resource = processResource, - }; - request.ResourceMetrics.Add(resourceMetrics); - - foreach (var metric in metrics) - { - var otlpMetric = metric.ToOtlpMetric(); - - // TODO: Replace null check with exception handling. - if (otlpMetric == null) - { - OpenTelemetryProtocolExporterEventSource.Log.CouldNotTranslateMetric( - nameof(MetricItemExtensions), - nameof(AddMetrics)); - continue; - } - - var meterName = metric.MeterName; - if (!metricsByLibrary.TryGetValue(meterName, out var scopeMetrics)) - { - scopeMetrics = GetMetricListFromPool(meterName, metric.MeterVersion, metric.MeterTags); - - metricsByLibrary.Add(meterName, scopeMetrics); - resourceMetrics.ScopeMetrics.Add(scopeMetrics); - } - - scopeMetrics.Metrics.Add(otlpMetric); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static void Return(this OtlpCollector.ExportMetricsServiceRequest request) - { - var resourceMetrics = request.ResourceMetrics.FirstOrDefault(); - if (resourceMetrics == null) - { - return; - } - - foreach (var scopeMetrics in resourceMetrics.ScopeMetrics) - { - scopeMetrics.Metrics.Clear(); - scopeMetrics.Scope.Attributes.Clear(); - MetricListPool.Add(scopeMetrics); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static ScopeMetrics GetMetricListFromPool(string name, string version, IEnumerable>? meterTags) - { - if (!MetricListPool.TryTake(out var scopeMetrics)) - { - scopeMetrics = new ScopeMetrics - { - Scope = new OtlpCommon.InstrumentationScope - { - Name = name, // Name is enforced to not be null, but it can be empty. - Version = version ?? string.Empty, // NRE throw by proto - }, - }; - - if (meterTags != null) - { - AddScopeAttributes(meterTags, scopeMetrics.Scope.Attributes); - } - } - else - { - scopeMetrics.Scope.Name = name; - scopeMetrics.Scope.Version = version ?? string.Empty; - if (meterTags != null) - { - AddScopeAttributes(meterTags, scopeMetrics.Scope.Attributes); - } - } - - return scopeMetrics; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static OtlpMetrics.Metric ToOtlpMetric(this Metric metric) - { - var otlpMetric = new OtlpMetrics.Metric - { - Name = metric.Name, - }; - - if (metric.Description != null) - { - otlpMetric.Description = metric.Description; - } - - if (metric.Unit != null) - { - otlpMetric.Unit = metric.Unit; - } - - OtlpMetrics.AggregationTemporality temporality; - if (metric.Temporality == AggregationTemporality.Delta) - { - temporality = OtlpMetrics.AggregationTemporality.Delta; - } - else - { - temporality = OtlpMetrics.AggregationTemporality.Cumulative; - } - - switch (metric.MetricType) - { - case MetricType.LongSum: - case MetricType.LongSumNonMonotonic: - { - var sum = new Sum - { - IsMonotonic = metric.MetricType == MetricType.LongSum, - AggregationTemporality = temporality, - }; - - foreach (ref readonly var metricPoint in metric.GetMetricPoints()) - { - var dataPoint = new NumberDataPoint - { - StartTimeUnixNano = (ulong)metricPoint.StartTime.ToUnixTimeNanoseconds(), - TimeUnixNano = (ulong)metricPoint.EndTime.ToUnixTimeNanoseconds(), - }; - - AddAttributes(metricPoint.Tags, dataPoint.Attributes); - - dataPoint.AsInt = metricPoint.GetSumLong(); - - if (metricPoint.TryGetExemplars(out var exemplars)) - { - foreach (ref readonly var exemplar in exemplars) - { - dataPoint.Exemplars.Add( - ToOtlpExemplar(exemplar.LongValue, in exemplar)); - } - } - - sum.DataPoints.Add(dataPoint); - } - - otlpMetric.Sum = sum; - break; - } - - case MetricType.DoubleSum: - case MetricType.DoubleSumNonMonotonic: - { - var sum = new Sum - { - IsMonotonic = metric.MetricType == MetricType.DoubleSum, - AggregationTemporality = temporality, - }; - - foreach (ref readonly var metricPoint in metric.GetMetricPoints()) - { - var dataPoint = new NumberDataPoint - { - StartTimeUnixNano = (ulong)metricPoint.StartTime.ToUnixTimeNanoseconds(), - TimeUnixNano = (ulong)metricPoint.EndTime.ToUnixTimeNanoseconds(), - }; - - AddAttributes(metricPoint.Tags, dataPoint.Attributes); - - dataPoint.AsDouble = metricPoint.GetSumDouble(); - - if (metricPoint.TryGetExemplars(out var exemplars)) - { - foreach (ref readonly var exemplar in exemplars) - { - dataPoint.Exemplars.Add( - ToOtlpExemplar(exemplar.DoubleValue, in exemplar)); - } - } - - sum.DataPoints.Add(dataPoint); - } - - otlpMetric.Sum = sum; - break; - } - - case MetricType.LongGauge: - { - var gauge = new Gauge(); - foreach (ref readonly var metricPoint in metric.GetMetricPoints()) - { - var dataPoint = new NumberDataPoint - { - StartTimeUnixNano = (ulong)metricPoint.StartTime.ToUnixTimeNanoseconds(), - TimeUnixNano = (ulong)metricPoint.EndTime.ToUnixTimeNanoseconds(), - }; - - AddAttributes(metricPoint.Tags, dataPoint.Attributes); - - dataPoint.AsInt = metricPoint.GetGaugeLastValueLong(); - - if (metricPoint.TryGetExemplars(out var exemplars)) - { - foreach (ref readonly var exemplar in exemplars) - { - dataPoint.Exemplars.Add( - ToOtlpExemplar(exemplar.LongValue, in exemplar)); - } - } - - gauge.DataPoints.Add(dataPoint); - } - - otlpMetric.Gauge = gauge; - break; - } - - case MetricType.DoubleGauge: - { - var gauge = new Gauge(); - foreach (ref readonly var metricPoint in metric.GetMetricPoints()) - { - var dataPoint = new NumberDataPoint - { - StartTimeUnixNano = (ulong)metricPoint.StartTime.ToUnixTimeNanoseconds(), - TimeUnixNano = (ulong)metricPoint.EndTime.ToUnixTimeNanoseconds(), - }; - - AddAttributes(metricPoint.Tags, dataPoint.Attributes); - - dataPoint.AsDouble = metricPoint.GetGaugeLastValueDouble(); - - if (metricPoint.TryGetExemplars(out var exemplars)) - { - foreach (ref readonly var exemplar in exemplars) - { - dataPoint.Exemplars.Add( - ToOtlpExemplar(exemplar.DoubleValue, in exemplar)); - } - } - - gauge.DataPoints.Add(dataPoint); - } - - otlpMetric.Gauge = gauge; - break; - } - - case MetricType.Histogram: - { - var histogram = new Histogram - { - AggregationTemporality = temporality, - }; - - foreach (ref readonly var metricPoint in metric.GetMetricPoints()) - { - var dataPoint = new HistogramDataPoint - { - StartTimeUnixNano = (ulong)metricPoint.StartTime.ToUnixTimeNanoseconds(), - TimeUnixNano = (ulong)metricPoint.EndTime.ToUnixTimeNanoseconds(), - }; - - AddAttributes(metricPoint.Tags, dataPoint.Attributes); - dataPoint.Count = (ulong)metricPoint.GetHistogramCount(); - dataPoint.Sum = metricPoint.GetHistogramSum(); - - if (metricPoint.TryGetHistogramMinMaxValues(out double min, out double max)) - { - dataPoint.Min = min; - dataPoint.Max = max; - } - - foreach (var histogramMeasurement in metricPoint.GetHistogramBuckets()) - { - dataPoint.BucketCounts.Add((ulong)histogramMeasurement.BucketCount); - if (histogramMeasurement.ExplicitBound != double.PositiveInfinity) - { - dataPoint.ExplicitBounds.Add(histogramMeasurement.ExplicitBound); - } - } - - if (metricPoint.TryGetExemplars(out var exemplars)) - { - foreach (ref readonly var exemplar in exemplars) - { - dataPoint.Exemplars.Add( - ToOtlpExemplar(exemplar.DoubleValue, in exemplar)); - } - } - - histogram.DataPoints.Add(dataPoint); - } - - otlpMetric.Histogram = histogram; - break; - } - - case MetricType.ExponentialHistogram: - { - var histogram = new ExponentialHistogram - { - AggregationTemporality = temporality, - }; - - foreach (ref readonly var metricPoint in metric.GetMetricPoints()) - { - var dataPoint = new ExponentialHistogramDataPoint - { - StartTimeUnixNano = (ulong)metricPoint.StartTime.ToUnixTimeNanoseconds(), - TimeUnixNano = (ulong)metricPoint.EndTime.ToUnixTimeNanoseconds(), - }; - - AddAttributes(metricPoint.Tags, dataPoint.Attributes); - dataPoint.Count = (ulong)metricPoint.GetHistogramCount(); - dataPoint.Sum = metricPoint.GetHistogramSum(); - - if (metricPoint.TryGetHistogramMinMaxValues(out double min, out double max)) - { - dataPoint.Min = min; - dataPoint.Max = max; - } - - var exponentialHistogramData = metricPoint.GetExponentialHistogramData(); - dataPoint.Scale = exponentialHistogramData.Scale; - dataPoint.ZeroCount = (ulong)exponentialHistogramData.ZeroCount; - - dataPoint.Positive = new ExponentialHistogramDataPoint.Types.Buckets(); - dataPoint.Positive.Offset = exponentialHistogramData.PositiveBuckets.Offset; - foreach (var bucketCount in exponentialHistogramData.PositiveBuckets) - { - dataPoint.Positive.BucketCounts.Add((ulong)bucketCount); - } - - if (metricPoint.TryGetExemplars(out var exemplars)) - { - foreach (ref readonly var exemplar in exemplars) - { - dataPoint.Exemplars.Add( - ToOtlpExemplar(exemplar.DoubleValue, in exemplar)); - } - } - - histogram.DataPoints.Add(dataPoint); - } - - otlpMetric.ExponentialHistogram = histogram; - break; - } - } - - return otlpMetric; - } - - internal static OtlpMetrics.Exemplar ToOtlpExemplar(T value, in Metrics.Exemplar exemplar) - where T : struct - { - var otlpExemplar = new OtlpMetrics.Exemplar - { - TimeUnixNano = (ulong)exemplar.Timestamp.ToUnixTimeNanoseconds(), - }; - - if (exemplar.TraceId != default) - { - byte[] traceIdBytes = new byte[16]; - exemplar.TraceId.CopyTo(traceIdBytes); - - byte[] spanIdBytes = new byte[8]; - exemplar.SpanId.CopyTo(spanIdBytes); - - otlpExemplar.TraceId = UnsafeByteOperations.UnsafeWrap(traceIdBytes); - otlpExemplar.SpanId = UnsafeByteOperations.UnsafeWrap(spanIdBytes); - } - - if (typeof(T) == typeof(long)) - { - otlpExemplar.AsInt = (long)(object)value; - } - else if (typeof(T) == typeof(double)) - { - otlpExemplar.AsDouble = (double)(object)value; - } - else - { - Debug.Fail("Unexpected type"); - otlpExemplar.AsDouble = Convert.ToDouble(value); - } - - var otlpExemplarFilteredAttributes = otlpExemplar.FilteredAttributes; - - foreach (var tag in exemplar.FilteredTags) - { - OtlpTagWriter.Instance.TryWriteTag(ref otlpExemplarFilteredAttributes, tag); - } - - return otlpExemplar; - } - - private static void AddAttributes(ReadOnlyTagCollection tags, RepeatedField attributes) - { - foreach (var tag in tags) - { - OtlpTagWriter.Instance.TryWriteTag(ref attributes, tag); - } - } - - private static void AddScopeAttributes(IEnumerable> meterTags, RepeatedField attributes) - { - foreach (var tag in meterTags) - { - OtlpTagWriter.Instance.TryWriteTag(ref attributes, tag); - } - } -} diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/OtlpLogRecordTransformer.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/OtlpLogRecordTransformer.cs deleted file mode 100644 index e6942286e22..00000000000 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/OtlpLogRecordTransformer.cs +++ /dev/null @@ -1,281 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -using System.Collections.Concurrent; -using System.Runtime.CompilerServices; -using Google.Protobuf; -using OpenTelemetry.Internal; -using OpenTelemetry.Logs; -using OpenTelemetry.Trace; -using OtlpCollector = OpenTelemetry.Proto.Collector.Logs.V1; -using OtlpCommon = OpenTelemetry.Proto.Common.V1; -using OtlpLogs = OpenTelemetry.Proto.Logs.V1; -using OtlpResource = OpenTelemetry.Proto.Resource.V1; - -namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation; - -internal sealed class OtlpLogRecordTransformer -{ - internal static readonly ConcurrentBag LogListPool = new(); - - private readonly SdkLimitOptions sdkLimitOptions; - private readonly ExperimentalOptions experimentalOptions; - - public OtlpLogRecordTransformer(SdkLimitOptions sdkLimitOptions, ExperimentalOptions experimentalOptions) - { - this.sdkLimitOptions = sdkLimitOptions; - this.experimentalOptions = experimentalOptions; - } - - internal OtlpCollector.ExportLogsServiceRequest BuildExportRequest( - OtlpResource.Resource processResource, - in Batch logRecordBatch) - { - // TODO: https://github.com/open-telemetry/opentelemetry-dotnet/issues/4943 - Dictionary logsByCategory = new Dictionary(); - - var request = new OtlpCollector.ExportLogsServiceRequest(); - - var resourceLogs = new OtlpLogs.ResourceLogs - { - Resource = processResource, - }; - request.ResourceLogs.Add(resourceLogs); - - foreach (var logRecord in logRecordBatch) - { - var otlpLogRecord = this.ToOtlpLog(logRecord); - if (otlpLogRecord != null) - { - var scopeName = logRecord.Logger.Name; - if (!logsByCategory.TryGetValue(scopeName, out var scopeLogs)) - { - scopeLogs = this.GetLogListFromPool(scopeName); - logsByCategory.Add(scopeName, scopeLogs); - resourceLogs.ScopeLogs.Add(scopeLogs); - } - - scopeLogs.LogRecords.Add(otlpLogRecord); - } - } - - return request; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal void Return(OtlpCollector.ExportLogsServiceRequest request) - { - var resourceLogs = request.ResourceLogs.FirstOrDefault(); - if (resourceLogs == null) - { - return; - } - - foreach (var scope in resourceLogs.ScopeLogs) - { - scope.LogRecords.Clear(); - LogListPool.Add(scope); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal OtlpLogs.ScopeLogs GetLogListFromPool(string name) - { - if (!LogListPool.TryTake(out var logs)) - { - logs = new OtlpLogs.ScopeLogs - { - Scope = new OtlpCommon.InstrumentationScope - { - Name = name, // Name is enforced to not be null, but it can be empty. - Version = string.Empty, // proto requires this to be non-null. - }, - }; - } - else - { - logs.Scope.Name = name; - logs.Scope.Version = string.Empty; - } - - return logs; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal OtlpLogs.LogRecord? ToOtlpLog(LogRecord logRecord) - { - OtlpLogs.LogRecord? otlpLogRecord = null; - - try - { - var timestamp = (ulong)logRecord.Timestamp.ToUnixTimeNanoseconds(); - otlpLogRecord = new OtlpLogs.LogRecord - { - TimeUnixNano = timestamp, - ObservedTimeUnixNano = timestamp, - SeverityNumber = GetSeverityNumber(logRecord.Severity), - }; - - if (!string.IsNullOrWhiteSpace(logRecord.SeverityText)) - { - otlpLogRecord.SeverityText = logRecord.SeverityText; - } - else if (logRecord.Severity.HasValue) - { - otlpLogRecord.SeverityText = logRecord.Severity.Value.ToShortName(); - } - - var attributeValueLengthLimit = this.sdkLimitOptions.LogRecordAttributeValueLengthLimit; - var attributeCountLimit = this.sdkLimitOptions.LogRecordAttributeCountLimit ?? int.MaxValue; - - if (this.experimentalOptions.EmitLogEventAttributes) - { - if (logRecord.EventId.Id != default) - { - AddIntAttribute(otlpLogRecord, ExperimentalOptions.LogRecordEventIdAttribute, logRecord.EventId.Id, attributeCountLimit); - } - - if (!string.IsNullOrEmpty(logRecord.EventId.Name)) - { - AddStringAttribute(otlpLogRecord, ExperimentalOptions.LogRecordEventNameAttribute, logRecord.EventId.Name, attributeCountLimit, attributeValueLengthLimit); - } - } - - if (logRecord.Exception != null) - { - AddStringAttribute(otlpLogRecord, SemanticConventions.AttributeExceptionType, logRecord.Exception.GetType().Name, attributeCountLimit, attributeValueLengthLimit); - AddStringAttribute(otlpLogRecord, SemanticConventions.AttributeExceptionMessage, logRecord.Exception.Message, attributeCountLimit, attributeValueLengthLimit); - AddStringAttribute(otlpLogRecord, SemanticConventions.AttributeExceptionStacktrace, logRecord.Exception.ToInvariantString(), attributeCountLimit, attributeValueLengthLimit); - } - - bool bodyPopulatedFromFormattedMessage = false; - if (logRecord.FormattedMessage != null) - { - otlpLogRecord.Body = new OtlpCommon.AnyValue { StringValue = logRecord.FormattedMessage }; - bodyPopulatedFromFormattedMessage = true; - } - - if (logRecord.Attributes != null) - { - foreach (var attribute in logRecord.Attributes) - { - // Special casing {OriginalFormat} - // See https://github.com/open-telemetry/opentelemetry-dotnet/pull/3182 - // for explanation. - if (attribute.Key.Equals("{OriginalFormat}") && !bodyPopulatedFromFormattedMessage) - { - otlpLogRecord.Body = new OtlpCommon.AnyValue { StringValue = attribute.Value as string }; - } - else - { - AddAttribute(otlpLogRecord, attribute, attributeCountLimit, attributeValueLengthLimit); - } - } - - // Supports setting Body directly on LogRecord for the Logs Bridge API. - if (otlpLogRecord.Body == null && logRecord.Body != null) - { - // If {OriginalFormat} is not present in the attributes, - // use logRecord.Body if it is set. - otlpLogRecord.Body = new OtlpCommon.AnyValue { StringValue = logRecord.Body }; - } - } - - if (logRecord.TraceId != default && logRecord.SpanId != default) - { - byte[] traceIdBytes = new byte[16]; - byte[] spanIdBytes = new byte[8]; - - logRecord.TraceId.CopyTo(traceIdBytes); - logRecord.SpanId.CopyTo(spanIdBytes); - - otlpLogRecord.TraceId = UnsafeByteOperations.UnsafeWrap(traceIdBytes); - otlpLogRecord.SpanId = UnsafeByteOperations.UnsafeWrap(spanIdBytes); - otlpLogRecord.Flags = (uint)logRecord.TraceFlags; - } - - logRecord.ForEachScope(ProcessScope, otlpLogRecord); - - void ProcessScope(LogRecordScope scope, OtlpLogs.LogRecord otlpLog) - { - foreach (var scopeItem in scope) - { - if (scopeItem.Key.Equals("{OriginalFormat}") || string.IsNullOrEmpty(scopeItem.Key)) - { - // Ignore if the scope key is empty. - // Ignore if the scope key is {OriginalFormat} - // Attributes should not contain duplicates, - // and it is expensive to de-dup, so this - // exporter is going to pass the scope items as is. - // {OriginalFormat} is going to be the key - // if one uses formatted string for scopes - // and if there are nested scopes, this is - // guaranteed to create duplicate keys. - // Similar for empty keys, which is what the - // key is going to be if user simply - // passes a string as scope. - // To summarize this exporter only allows - // IReadOnlyList> - // or IEnumerable>. - // and expect users to provide unique keys. - // Note: It is possible that we allow users - // to override this exporter feature. So not blocking - // empty/{OriginalFormat} in the SDK itself. - } - else - { - AddAttribute(otlpLog, scopeItem, attributeCountLimit, attributeValueLengthLimit); - } - } - } - } - catch (Exception ex) - { - OpenTelemetryProtocolExporterEventSource.Log.CouldNotTranslateLogRecord(ex.Message); - } - - return otlpLogRecord; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void AddAttribute(OtlpLogs.LogRecord logRecord, KeyValuePair attribute, int maxAttributeCount, int? maxValueLength) - { - var logRecordAttributes = logRecord.Attributes; - - if (logRecordAttributes.Count == maxAttributeCount) - { - logRecord.DroppedAttributesCount++; - } - else - { - OtlpTagWriter.Instance.TryWriteTag(ref logRecordAttributes, attribute, maxValueLength); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void AddStringAttribute(OtlpLogs.LogRecord logRecord, string key, string? value, int maxAttributeCount, int? maxValueLength) - { - var attributeItem = new KeyValuePair(key, value); - - AddAttribute(logRecord, attributeItem, maxAttributeCount, maxValueLength); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void AddIntAttribute(OtlpLogs.LogRecord logRecord, string key, int value, int maxAttributeCount) - { - var attributeItem = new KeyValuePair(key, value); - - AddAttribute(logRecord, attributeItem, maxAttributeCount, maxValueLength: null); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static OtlpLogs.SeverityNumber GetSeverityNumber(LogRecordSeverity? severity) - { - if (!severity.HasValue) - { - return OtlpLogs.SeverityNumber.Unspecified; - } - - return (OtlpLogs.SeverityNumber)(int)severity.Value; - } -} diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/OtlpTagWriter.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/OtlpTagWriter.cs deleted file mode 100644 index c713d4b646e..00000000000 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/OtlpTagWriter.cs +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -using Google.Protobuf.Collections; -using OpenTelemetry.Internal; -using OtlpCommon = OpenTelemetry.Proto.Common.V1; - -namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation; - -internal sealed class OtlpTagWriter : TagWriter, OtlpCommon.ArrayValue> -{ - private OtlpTagWriter() - : base(new OtlpArrayTagWriter()) - { - } - - public static OtlpTagWriter Instance { get; } = new(); - - internal static OtlpCommon.AnyValue ToAnyValue(long value) - => new() { IntValue = value }; - - internal static OtlpCommon.AnyValue ToAnyValue(double value) - => new() { DoubleValue = value }; - - internal static OtlpCommon.AnyValue ToAnyValue(bool value) - => new() { BoolValue = value }; - - internal static OtlpCommon.AnyValue ToAnyValue(string value) - => new() { StringValue = value }; - - protected override void WriteIntegralTag(ref RepeatedField tags, string key, long value) - { - tags.Add(new OtlpCommon.KeyValue { Key = key, Value = ToAnyValue(value) }); - } - - protected override void WriteFloatingPointTag(ref RepeatedField tags, string key, double value) - { - tags.Add(new OtlpCommon.KeyValue { Key = key, Value = ToAnyValue(value) }); - } - - protected override void WriteBooleanTag(ref RepeatedField tags, string key, bool value) - { - tags.Add(new OtlpCommon.KeyValue { Key = key, Value = ToAnyValue(value) }); - } - - protected override void WriteStringTag(ref RepeatedField tags, string key, ReadOnlySpan value) - { - tags.Add(new OtlpCommon.KeyValue { Key = key, Value = ToAnyValue(value.ToString()) }); - } - - protected override void WriteArrayTag(ref RepeatedField tags, string key, ref OtlpCommon.ArrayValue value) - { - tags.Add(new OtlpCommon.KeyValue - { - Key = key, - Value = new OtlpCommon.AnyValue - { - ArrayValue = value, - }, - }); - } - - protected override void OnUnsupportedTagDropped( - string tagKey, - string tagValueTypeFullName) - { - OpenTelemetryProtocolExporterEventSource.Log.UnsupportedAttributeType( - tagValueTypeFullName, - tagKey); - } - - private sealed class OtlpArrayTagWriter : ArrayTagWriter - { - public override OtlpCommon.ArrayValue BeginWriteArray() => new(); - - public override void WriteNullValue(ref OtlpCommon.ArrayValue array) - { - array.Values.Add(new OtlpCommon.AnyValue()); - } - - public override void WriteIntegralValue(ref OtlpCommon.ArrayValue array, long value) - { - array.Values.Add(ToAnyValue(value)); - } - - public override void WriteFloatingPointValue(ref OtlpCommon.ArrayValue array, double value) - { - array.Values.Add(ToAnyValue(value)); - } - - public override void WriteBooleanValue(ref OtlpCommon.ArrayValue array, bool value) - { - array.Values.Add(ToAnyValue(value)); - } - - public override void WriteStringValue(ref OtlpCommon.ArrayValue array, ReadOnlySpan value) - { - array.Values.Add(ToAnyValue(value.ToString())); - } - - public override void EndWriteArray(ref OtlpCommon.ArrayValue array) - { - } - } -} diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/README.md b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/README.md deleted file mode 100644 index d70d44456e2..00000000000 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/README.md +++ /dev/null @@ -1,7 +0,0 @@ -# OpenTelemetry Protocol Implementation - -`.proto` files under `Implementation\` are copied from the -[`opentelemetry-proto`](https://github.com/open-telemetry/opentelemetry-proto/commit/1a931b4b57c34e7fd8f7dddcaa9b7587840e9c08) -repo. - -Trace proto files marked as stable. diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ResourceExtensions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ResourceExtensions.cs deleted file mode 100644 index f114f072972..00000000000 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ResourceExtensions.cs +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -using OpenTelemetry.Resources; -using OtlpCommon = OpenTelemetry.Proto.Common.V1; -using OtlpResource = OpenTelemetry.Proto.Resource.V1; - -namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation; - -internal static class ResourceExtensions -{ - public static OtlpResource.Resource ToOtlpResource(this Resource resource) - { - var processResource = new OtlpResource.Resource(); - - var processResourceAttributes = processResource.Attributes; - - foreach (KeyValuePair attribute in resource.Attributes) - { - OtlpTagWriter.Instance.TryWriteTag(ref processResourceAttributes, attribute.Key, attribute.Value); - } - - if (!processResource.Attributes.Any(kvp => kvp.Key == ResourceSemanticConventions.AttributeServiceName)) - { - var serviceName = (string)ResourceBuilder.CreateDefault().Build().Attributes.FirstOrDefault( - kvp => kvp.Key == ResourceSemanticConventions.AttributeServiceName).Value; - processResource.Attributes.Add(new OtlpCommon.KeyValue - { - Key = ResourceSemanticConventions.AttributeServiceName, - Value = new OtlpCommon.AnyValue { StringValue = serviceName }, - }); - } - - return processResource; - } -} diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Transmission/OtlpExporterPersistentStorageTransmissionHandler.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Transmission/OtlpExporterPersistentStorageTransmissionHandler.cs index 199f1510b2f..c40d7577908 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Transmission/OtlpExporterPersistentStorageTransmissionHandler.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Transmission/OtlpExporterPersistentStorageTransmissionHandler.cs @@ -2,17 +2,13 @@ // SPDX-License-Identifier: Apache-2.0 using System.Diagnostics; -using Google.Protobuf; using OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.ExportClient; using OpenTelemetry.PersistentStorage.Abstractions; using OpenTelemetry.PersistentStorage.FileSystem; -using OpenTelemetry.Proto.Collector.Logs.V1; -using OpenTelemetry.Proto.Collector.Metrics.V1; -using OpenTelemetry.Proto.Collector.Trace.V1; namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.Transmission; -internal sealed class OtlpExporterPersistentStorageTransmissionHandler : OtlpExporterTransmissionHandler, IDisposable +internal sealed class OtlpExporterPersistentStorageTransmissionHandler : OtlpExporterTransmissionHandler, IDisposable { private const int RetryIntervalInMilliseconds = 60000; private readonly ManualResetEvent shutdownEvent = new(false); @@ -20,26 +16,22 @@ internal sealed class OtlpExporterPersistentStorageTransmissionHandler private readonly AutoResetEvent exportEvent = new(false); private readonly Thread thread; private readonly PersistentBlobProvider persistentBlobProvider; - private readonly Func requestFactory; private bool disposed; - public OtlpExporterPersistentStorageTransmissionHandler(IExportClient exportClient, double timeoutMilliseconds, Func requestFactory, string storagePath) - : this(new FileBlobProvider(storagePath), exportClient, timeoutMilliseconds, requestFactory) + public OtlpExporterPersistentStorageTransmissionHandler(IExportClient exportClient, double timeoutMilliseconds, string storagePath) + : this(new FileBlobProvider(storagePath), exportClient, timeoutMilliseconds) { } - internal OtlpExporterPersistentStorageTransmissionHandler(PersistentBlobProvider persistentBlobProvider, IExportClient exportClient, double timeoutMilliseconds, Func requestFactory) + internal OtlpExporterPersistentStorageTransmissionHandler(PersistentBlobProvider persistentBlobProvider, IExportClient exportClient, double timeoutMilliseconds) : base(exportClient, timeoutMilliseconds) { Debug.Assert(persistentBlobProvider != null, "persistentBlobProvider was null"); - Debug.Assert(requestFactory != null, "requestFactory was null"); - this.persistentBlobProvider = persistentBlobProvider!; - this.requestFactory = requestFactory!; this.thread = new Thread(this.RetryStoredRequests) { - Name = $"OtlpExporter Persistent Retry Storage - {typeof(TRequest)}", + Name = "OtlpExporter Persistent Retry Storage", IsBackground = true, }; @@ -54,36 +46,10 @@ internal bool InitiateAndWaitForRetryProcess(int timeOutMilliseconds) return this.dataExportNotification.WaitOne(timeOutMilliseconds); } - protected override bool OnSubmitRequestFailure(TRequest request, ExportClientResponse response) + protected override bool OnSubmitRequestFailure(byte[] request, int contentLength, ExportClientResponse response) { - if (RetryHelper.ShouldRetryRequest(response, OtlpRetry.InitialBackoffMilliseconds, out _)) - { - byte[]? data = null; - if (request is ExportTraceServiceRequest traceRequest) - { - data = traceRequest.ToByteArray(); - } - else if (request is ExportMetricsServiceRequest metricsRequest) - { - data = metricsRequest.ToByteArray(); - } - else if (request is ExportLogsServiceRequest logsRequest) - { - data = logsRequest.ToByteArray(); - } - else - { - Debug.Fail("Unexpected request type encountered"); - data = null; - } - - if (data != null) - { - return this.persistentBlobProvider.TryCreateBlob(data, out _); - } - } - - return false; + Debug.Assert(request != null, "request was null"); + return RetryHelper.ShouldRetryRequest(response, OtlpRetry.InitialBackoffMilliseconds, out _) && this.persistentBlobProvider.TryCreateBlob(request!, out _); } protected override void OnShutdown(int timeoutMilliseconds) @@ -157,9 +123,7 @@ private void RetryStoredRequests() if (blob.TryLease((int)this.TimeoutMilliseconds) && blob.TryRead(out var data)) { var deadlineUtc = DateTime.UtcNow.AddMilliseconds(this.TimeoutMilliseconds); - var request = this.requestFactory.Invoke(data); - if (this.TryRetryRequest(request, deadlineUtc, out var response) - || !RetryHelper.ShouldRetryRequest(response, OtlpRetry.InitialBackoffMilliseconds, out var retryInfo)) + if (this.TryRetryRequest(data, data.Length, deadlineUtc, out var response) || !RetryHelper.ShouldRetryRequest(response, OtlpRetry.InitialBackoffMilliseconds, out var retryInfo)) { blob.TryDelete(); } diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Transmission/OtlpExporterRetryTransmissionHandler.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Transmission/OtlpExporterRetryTransmissionHandler.cs index df54fb5f78b..6830288525e 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Transmission/OtlpExporterRetryTransmissionHandler.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Transmission/OtlpExporterRetryTransmissionHandler.cs @@ -5,14 +5,14 @@ namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.Transmission; -internal sealed class OtlpExporterRetryTransmissionHandler : OtlpExporterTransmissionHandler +internal sealed class OtlpExporterRetryTransmissionHandler : OtlpExporterTransmissionHandler { - internal OtlpExporterRetryTransmissionHandler(IExportClient exportClient, double timeoutMilliseconds) + internal OtlpExporterRetryTransmissionHandler(IExportClient exportClient, double timeoutMilliseconds) : base(exportClient, timeoutMilliseconds) { } - protected override bool OnSubmitRequestFailure(TRequest request, ExportClientResponse response) + protected override bool OnSubmitRequestFailure(byte[] request, int contentLength, ExportClientResponse response) { var nextRetryDelayMilliseconds = OtlpRetry.InitialBackoffMilliseconds; while (RetryHelper.ShouldRetryRequest(response, nextRetryDelayMilliseconds, out var retryResult)) @@ -22,7 +22,7 @@ protected override bool OnSubmitRequestFailure(TRequest request, ExportClientRes // we would fail fast and drop the data. Thread.Sleep(retryResult.RetryDelay); - if (this.TryRetryRequest(request, response.DeadlineUtc, out response)) + if (this.TryRetryRequest(request, contentLength, response.DeadlineUtc, out response)) { return true; } diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Transmission/OtlpExporterTransmissionHandler.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Transmission/OtlpExporterTransmissionHandler.cs index 9ecb6c4785f..b63175ee502 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Transmission/OtlpExporterTransmissionHandler.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Transmission/OtlpExporterTransmissionHandler.cs @@ -7,9 +7,9 @@ namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.Transmission; -internal class OtlpExporterTransmissionHandler : IDisposable +internal class OtlpExporterTransmissionHandler : IDisposable { - public OtlpExporterTransmissionHandler(IExportClient exportClient, double timeoutMilliseconds) + public OtlpExporterTransmissionHandler(IExportClient exportClient, double timeoutMilliseconds) { Guard.ThrowIfNull(exportClient); @@ -17,7 +17,7 @@ public OtlpExporterTransmissionHandler(IExportClient exportClient, dou this.TimeoutMilliseconds = timeoutMilliseconds; } - internal IExportClient ExportClient { get; } + internal IExportClient ExportClient { get; } internal double TimeoutMilliseconds { get; } @@ -25,21 +25,22 @@ public OtlpExporterTransmissionHandler(IExportClient exportClient, dou /// Attempts to send an export request to the server. /// /// The request to send to the server. + /// length of content. /// if the request is sent successfully; otherwise, . /// - public bool TrySubmitRequest(TRequest request) + public bool TrySubmitRequest(byte[] request, int contentLength) { try { var deadlineUtc = DateTime.UtcNow.AddMilliseconds(this.TimeoutMilliseconds); - var response = this.ExportClient.SendExportRequest(request, deadlineUtc); + var response = this.ExportClient.SendExportRequest(request, contentLength, deadlineUtc); if (response.Success) { return true; } - return this.OnSubmitRequestFailure(request, response); + return this.OnSubmitRequestFailure(request, contentLength, response); } catch (Exception ex) { @@ -100,32 +101,25 @@ protected virtual void OnShutdown(int timeoutMilliseconds) /// Fired when a request could not be submitted. /// /// The request that was attempted to send to the server. + /// Length of content. /// . /// If the request is resubmitted and succeeds; otherwise, . - protected virtual bool OnSubmitRequestFailure(TRequest request, ExportClientResponse response) - { - return false; - } + protected virtual bool OnSubmitRequestFailure(byte[] request, int contentLength, ExportClientResponse response) => false; /// /// Fired when resending a request to the server. /// /// The request to be resent to the server. + /// Length of content. /// The deadline time in utc for export request to finish. /// . /// If the retry succeeds; otherwise, . - protected bool TryRetryRequest(TRequest request, DateTime deadlineUtc, out ExportClientResponse response) + protected bool TryRetryRequest(byte[] request, int contentLength, DateTime deadlineUtc, out ExportClientResponse response) { - response = this.ExportClient.SendExportRequest(request, deadlineUtc); - if (!response.Success) - { - OpenTelemetryProtocolExporterEventSource.Log.ExportMethodException(response.Exception!, isRetry: true); - return false; - } - - return true; + response = this.ExportClient.SendExportRequest(request, contentLength, deadlineUtc); + return response.Success; } /// diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Transmission/ProtobufOtlpExporterPersistentStorageTransmissionHandler.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Transmission/ProtobufOtlpExporterPersistentStorageTransmissionHandler.cs deleted file mode 100644 index b3a719aa3d5..00000000000 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Transmission/ProtobufOtlpExporterPersistentStorageTransmissionHandler.cs +++ /dev/null @@ -1,149 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -using System.Diagnostics; -using OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.ExportClient; -using OpenTelemetry.PersistentStorage.Abstractions; -using OpenTelemetry.PersistentStorage.FileSystem; - -namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.Transmission; - -internal sealed class ProtobufOtlpExporterPersistentStorageTransmissionHandler : ProtobufOtlpExporterTransmissionHandler, IDisposable -{ - private const int RetryIntervalInMilliseconds = 60000; - private readonly ManualResetEvent shutdownEvent = new(false); - private readonly ManualResetEvent dataExportNotification = new(false); - private readonly AutoResetEvent exportEvent = new(false); - private readonly Thread thread; - private readonly PersistentBlobProvider persistentBlobProvider; - private bool disposed; - - public ProtobufOtlpExporterPersistentStorageTransmissionHandler(IProtobufExportClient exportClient, double timeoutMilliseconds, string storagePath) - : this(new FileBlobProvider(storagePath), exportClient, timeoutMilliseconds) - { - } - - internal ProtobufOtlpExporterPersistentStorageTransmissionHandler(PersistentBlobProvider persistentBlobProvider, IProtobufExportClient exportClient, double timeoutMilliseconds) - : base(exportClient, timeoutMilliseconds) - { - Debug.Assert(persistentBlobProvider != null, "persistentBlobProvider was null"); - this.persistentBlobProvider = persistentBlobProvider!; - - this.thread = new Thread(this.RetryStoredRequests) - { - Name = $"OtlpExporter Persistent Retry Storage", - IsBackground = true, - }; - - this.thread.Start(); - } - - // Used for test. - internal bool InitiateAndWaitForRetryProcess(int timeOutMilliseconds) - { - this.exportEvent.Set(); - - return this.dataExportNotification.WaitOne(timeOutMilliseconds); - } - - protected override bool OnSubmitRequestFailure(byte[] request, int contentLength, ExportClientResponse response) - { - Debug.Assert(request != null, "request was null"); - return RetryHelper.ShouldRetryRequest(response, OtlpRetry.InitialBackoffMilliseconds, out _) && this.persistentBlobProvider.TryCreateBlob(request!, out _); - } - - protected override void OnShutdown(int timeoutMilliseconds) - { - var sw = timeoutMilliseconds == Timeout.Infinite ? null : Stopwatch.StartNew(); - - try - { - this.shutdownEvent.Set(); - } - catch (ObjectDisposedException) - { - // Dispose was called before shutdown. - } - - this.thread.Join(timeoutMilliseconds); - - if (sw != null) - { - var timeout = timeoutMilliseconds - sw.ElapsedMilliseconds; - - base.OnShutdown((int)Math.Max(timeout, 0)); - } - else - { - base.OnShutdown(timeoutMilliseconds); - } - } - - protected override void Dispose(bool disposing) - { - if (!this.disposed) - { - if (disposing) - { - this.shutdownEvent.Dispose(); - this.exportEvent.Dispose(); - this.dataExportNotification.Dispose(); - (this.persistentBlobProvider as IDisposable)?.Dispose(); - } - - this.disposed = true; - } - } - - private void RetryStoredRequests() - { - var handles = new WaitHandle[] { this.shutdownEvent, this.exportEvent }; - while (true) - { - try - { - var index = WaitHandle.WaitAny(handles, RetryIntervalInMilliseconds); - if (index == 0) - { - // Shutdown signaled - break; - } - - int fileCount = 0; - - // TODO: Run maintenance job. - // Transmit 10 files at a time. - while (fileCount < 10 && !this.shutdownEvent.WaitOne(0)) - { - if (!this.persistentBlobProvider.TryGetBlob(out var blob)) - { - break; - } - - if (blob.TryLease((int)this.TimeoutMilliseconds) && blob.TryRead(out var data)) - { - var deadlineUtc = DateTime.UtcNow.AddMilliseconds(this.TimeoutMilliseconds); - if (this.TryRetryRequest(data, data.Length, deadlineUtc, out var response) || !RetryHelper.ShouldRetryRequest(response, OtlpRetry.InitialBackoffMilliseconds, out var retryInfo)) - { - blob.TryDelete(); - } - - // TODO: extend the lease period based on the response from server on retryAfter. - } - - fileCount++; - } - - // Set and reset the handle to notify export and wait for next signal. - // This is used for InitiateAndWaitForRetryProcess. - this.dataExportNotification.Set(); - this.dataExportNotification.Reset(); - } - catch (Exception ex) - { - OpenTelemetryProtocolExporterEventSource.Log.RetryStoredRequestException(ex); - return; - } - } - } -} diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Transmission/ProtobufOtlpExporterRetryTransmissionHandler.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Transmission/ProtobufOtlpExporterRetryTransmissionHandler.cs deleted file mode 100644 index c1d5515a53f..00000000000 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Transmission/ProtobufOtlpExporterRetryTransmissionHandler.cs +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -using OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.ExportClient; - -namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.Transmission; - -internal sealed class ProtobufOtlpExporterRetryTransmissionHandler : ProtobufOtlpExporterTransmissionHandler -{ - internal ProtobufOtlpExporterRetryTransmissionHandler(IProtobufExportClient exportClient, double timeoutMilliseconds) - : base(exportClient, timeoutMilliseconds) - { - } - - protected override bool OnSubmitRequestFailure(byte[] request, int contentLength, ExportClientResponse response) - { - var nextRetryDelayMilliseconds = OtlpRetry.InitialBackoffMilliseconds; - while (RetryHelper.ShouldRetryRequest(response, nextRetryDelayMilliseconds, out var retryResult)) - { - // Note: This delay cannot exceed the configured timeout period for otlp exporter. - // If the backend responds with `RetryAfter` duration that would result in exceeding the configured timeout period - // we would fail fast and drop the data. - Thread.Sleep(retryResult.RetryDelay); - - if (this.TryRetryRequest(request, contentLength, response.DeadlineUtc, out response)) - { - return true; - } - - nextRetryDelayMilliseconds = retryResult.NextRetryDelayMilliseconds; - } - - return false; - } -} diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Transmission/ProtobufOtlpExporterTransmissionHandler.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Transmission/ProtobufOtlpExporterTransmissionHandler.cs deleted file mode 100644 index 70dad49f9cd..00000000000 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Transmission/ProtobufOtlpExporterTransmissionHandler.cs +++ /dev/null @@ -1,136 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -using System.Diagnostics; -using OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.ExportClient; -using OpenTelemetry.Internal; - -namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.Transmission; - -internal class ProtobufOtlpExporterTransmissionHandler : IDisposable -{ - public ProtobufOtlpExporterTransmissionHandler(IProtobufExportClient exportClient, double timeoutMilliseconds) - { - Guard.ThrowIfNull(exportClient); - - this.ExportClient = exportClient; - this.TimeoutMilliseconds = timeoutMilliseconds; - } - - internal IProtobufExportClient ExportClient { get; } - - internal double TimeoutMilliseconds { get; } - - /// - /// Attempts to send an export request to the server. - /// - /// The request to send to the server. - /// length of content. - /// if the request is sent successfully; otherwise, . - /// - public bool TrySubmitRequest(byte[] request, int contentLength) - { - try - { - var deadlineUtc = DateTime.UtcNow.AddMilliseconds(this.TimeoutMilliseconds); - var response = this.ExportClient.SendExportRequest(request, contentLength, deadlineUtc); - if (response.Success) - { - return true; - } - - return this.OnSubmitRequestFailure(request, contentLength, response); - } - catch (Exception ex) - { - OpenTelemetryProtocolExporterEventSource.Log.TrySubmitRequestException(ex); - return false; - } - } - - /// - /// Attempts to shutdown the transmission handler, blocks the current thread - /// until shutdown completed or timed out. - /// - /// - /// The number (non-negative) of milliseconds to wait, or - /// Timeout.Infinite to wait indefinitely. - /// - /// - /// Returns if shutdown succeeded; otherwise, . - /// - public bool Shutdown(int timeoutMilliseconds) - { - Guard.ThrowIfInvalidTimeout(timeoutMilliseconds); - - var sw = timeoutMilliseconds == Timeout.Infinite ? null : Stopwatch.StartNew(); - - this.OnShutdown(timeoutMilliseconds); - - if (sw != null) - { - var timeout = timeoutMilliseconds - sw.ElapsedMilliseconds; - - return this.ExportClient.Shutdown((int)Math.Max(timeout, 0)); - } - - return this.ExportClient.Shutdown(timeoutMilliseconds); - } - - /// - public void Dispose() - { - this.Dispose(true); - GC.SuppressFinalize(this); - } - - /// - /// Fired when the transmission handler is shutdown. - /// - /// - /// The number (non-negative) of milliseconds to wait, or - /// Timeout.Infinite to wait indefinitely. - /// - protected virtual void OnShutdown(int timeoutMilliseconds) - { - } - - /// - /// Fired when a request could not be submitted. - /// - /// The request that was attempted to send to the server. - /// Length of content. - /// . - /// If the request is resubmitted and succeeds; otherwise, . - protected virtual bool OnSubmitRequestFailure(byte[] request, int contentLength, ExportClientResponse response) => false; - - /// - /// Fired when resending a request to the server. - /// - /// The request to be resent to the server. - /// Length of content. - /// The deadline time in utc for export request to finish. - /// . - /// If the retry succeeds; otherwise, . - protected bool TryRetryRequest(byte[] request, int contentLength, DateTime deadlineUtc, out ExportClientResponse response) - { - response = this.ExportClient.SendExportRequest(request, contentLength, deadlineUtc); - return response.Success; - } - - /// - /// Releases the unmanaged resources used by this class and optionally - /// releases the managed resources. - /// - /// - /// to release both managed and unmanaged resources; - /// to release only unmanaged resources. - /// - protected virtual void Dispose(bool disposing) - { - } -} diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OpenTelemetry.Exporter.OpenTelemetryProtocol.csproj b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OpenTelemetry.Exporter.OpenTelemetryProtocol.csproj index 6c122ffa6de..e3aa4fcb8a8 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OpenTelemetry.Exporter.OpenTelemetryProtocol.csproj +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OpenTelemetry.Exporter.OpenTelemetryProtocol.csproj @@ -23,13 +23,6 @@ - - - - - - - @@ -41,10 +34,4 @@ - - - Implementation - - - diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterOptionsExtensions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterOptionsExtensions.cs index 57198880c47..fabb5a23392 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterOptionsExtensions.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterOptionsExtensions.cs @@ -4,18 +4,11 @@ #if NETFRAMEWORK using System.Net.Http; #endif +using System.Diagnostics; using System.Reflection; -using Grpc.Core; using OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation; using OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.ExportClient; -#if NETSTANDARD2_1 || NET -using Grpc.Net.Client; -#endif -using System.Diagnostics; -using Google.Protobuf; using OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.Transmission; -using LogOtlpCollector = OpenTelemetry.Proto.Collector.Logs.V1; -using MetricsOtlpCollector = OpenTelemetry.Proto.Collector.Metrics.V1; namespace OpenTelemetry.Exporter; @@ -29,39 +22,6 @@ internal static class OtlpExporterOptionsExtensions private const string MetricsHttpServicePath = "v1/metrics"; private const string LogsHttpServicePath = "v1/logs"; -#if NETSTANDARD2_1 || NET - public static GrpcChannel CreateChannel(this OtlpExporterOptions options) -#else - public static Channel CreateChannel(this OtlpExporterOptions options) -#endif - { - if (options.Endpoint.Scheme != Uri.UriSchemeHttp && options.Endpoint.Scheme != Uri.UriSchemeHttps) - { - throw new NotSupportedException($"Endpoint URI scheme ({options.Endpoint.Scheme}) is not supported. Currently only \"http\" and \"https\" are supported."); - } - -#if NETSTANDARD2_1 || NET - return GrpcChannel.ForAddress(options.Endpoint); -#else - ChannelCredentials channelCredentials; - if (options.Endpoint.Scheme == Uri.UriSchemeHttps) - { - channelCredentials = new SslCredentials(); - } - else - { - channelCredentials = ChannelCredentials.Insecure; - } - - return new Channel(options.Endpoint.Authority, channelCredentials); -#endif - } - - public static Metadata GetMetadataFromHeaders(this OtlpExporterOptions options) - { - return options.GetHeaders((m, k, v) => m.Add(k, v)); - } - public static THeaders GetHeaders(this OtlpExporterOptions options, Action addHeader) where THeaders : new() { @@ -98,37 +58,37 @@ public static THeaders GetHeaders(this OtlpExporterOptions options, Ac return headers; } - public static ProtobufOtlpExporterTransmissionHandler GetProtobufExportTransmissionHandler(this OtlpExporterOptions options, ExperimentalOptions experimentalOptions, OtlpSignalType otlpSignalType) + public static OtlpExporterTransmissionHandler GetProtobufExportTransmissionHandler(this OtlpExporterOptions options, ExperimentalOptions experimentalOptions, OtlpSignalType otlpSignalType) { var exportClient = GetProtobufExportClient(options, otlpSignalType); // `HttpClient.Timeout.TotalMilliseconds` would be populated with the correct timeout value for both the exporter configuration cases: // 1. User provides their own HttpClient. This case is straightforward as the user wants to use their `HttpClient` and thereby the same client's timeout value. // 2. If the user configures timeout via the exporter options, then the timeout set for the `HttpClient` initialized by the exporter will be set to user provided value. - double timeoutMilliseconds = exportClient is ProtobufOtlpHttpExportClient httpTraceExportClient + double timeoutMilliseconds = exportClient is OtlpHttpExportClient httpTraceExportClient ? httpTraceExportClient.HttpClient.Timeout.TotalMilliseconds : options.TimeoutMilliseconds; if (experimentalOptions.EnableInMemoryRetry) { - return new ProtobufOtlpExporterRetryTransmissionHandler(exportClient, timeoutMilliseconds); + return new OtlpExporterRetryTransmissionHandler(exportClient, timeoutMilliseconds); } else if (experimentalOptions.EnableDiskRetry) { Debug.Assert(!string.IsNullOrEmpty(experimentalOptions.DiskRetryDirectoryPath), $"{nameof(experimentalOptions.DiskRetryDirectoryPath)} is null or empty"); - return new ProtobufOtlpExporterPersistentStorageTransmissionHandler( + return new OtlpExporterPersistentStorageTransmissionHandler( exportClient, timeoutMilliseconds, Path.Combine(experimentalOptions.DiskRetryDirectoryPath, "traces")); } else { - return new ProtobufOtlpExporterTransmissionHandler(exportClient, timeoutMilliseconds); + return new OtlpExporterTransmissionHandler(exportClient, timeoutMilliseconds); } } - public static IProtobufExportClient GetProtobufExportClient(this OtlpExporterOptions options, OtlpSignalType otlpSignalType) + public static IExportClient GetProtobufExportClient(this OtlpExporterOptions options, OtlpSignalType otlpSignalType) { var httpClient = options.HttpClientFactory?.Invoke() ?? throw new InvalidOperationException("OtlpExporterOptions was missing HttpClientFactory or it returned null."); @@ -140,109 +100,21 @@ public static IProtobufExportClient GetProtobufExportClient(this OtlpExporterOpt return otlpSignalType switch { OtlpSignalType.Traces => options.Protocol == OtlpExportProtocol.Grpc - ? new ProtobufOtlpGrpcExportClient(options, httpClient, TraceGrpcServicePath) - : new ProtobufOtlpHttpExportClient(options, httpClient, TraceHttpServicePath), + ? new OtlpGrpcExportClient(options, httpClient, TraceGrpcServicePath) + : new OtlpHttpExportClient(options, httpClient, TraceHttpServicePath), OtlpSignalType.Metrics => options.Protocol == OtlpExportProtocol.Grpc - ? new ProtobufOtlpGrpcExportClient(options, httpClient, MetricsGrpcServicePath) - : new ProtobufOtlpHttpExportClient(options, httpClient, MetricsHttpServicePath), + ? new OtlpGrpcExportClient(options, httpClient, MetricsGrpcServicePath) + : new OtlpHttpExportClient(options, httpClient, MetricsHttpServicePath), OtlpSignalType.Logs => options.Protocol == OtlpExportProtocol.Grpc - ? new ProtobufOtlpGrpcExportClient(options, httpClient, LogsGrpcServicePath) - : new ProtobufOtlpHttpExportClient(options, httpClient, LogsHttpServicePath), + ? new OtlpGrpcExportClient(options, httpClient, LogsGrpcServicePath) + : new OtlpHttpExportClient(options, httpClient, LogsHttpServicePath), _ => throw new NotSupportedException($"OtlpSignalType {otlpSignalType} is not supported."), }; } - public static OtlpExporterTransmissionHandler GetMetricsExportTransmissionHandler(this OtlpExporterOptions options, ExperimentalOptions experimentalOptions) - { - var exportClient = GetMetricsExportClient(options); - - // `HttpClient.Timeout.TotalMilliseconds` would be populated with the correct timeout value for both the exporter configuration cases: - // 1. User provides their own HttpClient. This case is straightforward as the user wants to use their `HttpClient` and thereby the same client's timeout value. - // 2. If the user configures timeout via the exporter options, then the timeout set for the `HttpClient` initialized by the exporter will be set to user provided value. - double timeoutMilliseconds = exportClient is OtlpHttpMetricsExportClient httpMetricsExportClient - ? httpMetricsExportClient.HttpClient.Timeout.TotalMilliseconds - : options.TimeoutMilliseconds; - - if (experimentalOptions.EnableInMemoryRetry) - { - return new OtlpExporterRetryTransmissionHandler(exportClient, timeoutMilliseconds); - } - else if (experimentalOptions.EnableDiskRetry) - { - Debug.Assert(!string.IsNullOrEmpty(experimentalOptions.DiskRetryDirectoryPath), $"{nameof(experimentalOptions.DiskRetryDirectoryPath)} is null or empty"); - - return new OtlpExporterPersistentStorageTransmissionHandler( - exportClient, - timeoutMilliseconds, - (byte[] data) => - { - var request = new MetricsOtlpCollector.ExportMetricsServiceRequest(); - request.MergeFrom(data); - return request; - }, - Path.Combine(experimentalOptions.DiskRetryDirectoryPath, "metrics")); - } - else - { - return new OtlpExporterTransmissionHandler(exportClient, timeoutMilliseconds); - } - } - - public static OtlpExporterTransmissionHandler GetLogsExportTransmissionHandler(this OtlpExporterOptions options, ExperimentalOptions experimentalOptions) - { - var exportClient = GetLogExportClient(options); - double timeoutMilliseconds = exportClient is OtlpHttpLogExportClient httpLogExportClient - ? httpLogExportClient.HttpClient.Timeout.TotalMilliseconds - : options.TimeoutMilliseconds; - - if (experimentalOptions.EnableInMemoryRetry) - { - return new OtlpExporterRetryTransmissionHandler(exportClient, timeoutMilliseconds); - } - else if (experimentalOptions.EnableDiskRetry) - { - Debug.Assert(!string.IsNullOrEmpty(experimentalOptions.DiskRetryDirectoryPath), $"{nameof(experimentalOptions.DiskRetryDirectoryPath)} is null or empty"); - - return new OtlpExporterPersistentStorageTransmissionHandler( - exportClient, - timeoutMilliseconds, - (byte[] data) => - { - var request = new LogOtlpCollector.ExportLogsServiceRequest(); - request.MergeFrom(data); - return request; - }, - Path.Combine(experimentalOptions.DiskRetryDirectoryPath, "logs")); - } - else - { - return new OtlpExporterTransmissionHandler(exportClient, timeoutMilliseconds); - } - } - - public static IExportClient GetMetricsExportClient(this OtlpExporterOptions options) => - options.Protocol switch - { - OtlpExportProtocol.Grpc => new OtlpGrpcMetricsExportClient(options), - OtlpExportProtocol.HttpProtobuf => new OtlpHttpMetricsExportClient( - options, - options.HttpClientFactory?.Invoke() ?? throw new InvalidOperationException("OtlpExporterOptions was missing HttpClientFactory or it returned null.")), - _ => throw new NotSupportedException($"Protocol {options.Protocol} is not supported."), - }; - - public static IExportClient GetLogExportClient(this OtlpExporterOptions options) => - options.Protocol switch - { - OtlpExportProtocol.Grpc => new OtlpGrpcLogExportClient(options), - OtlpExportProtocol.HttpProtobuf => new OtlpHttpLogExportClient( - options, - options.HttpClientFactory?.Invoke() ?? throw new InvalidOperationException("OtlpExporterOptions was missing HttpClientFactory or it returned null.")), - _ => throw new NotSupportedException($"Protocol {options.Protocol} is not supported."), - }; - public static void TryEnableIHttpClientFactoryIntegration(this OtlpExporterOptions options, IServiceProvider serviceProvider, string httpClientName) { if (serviceProvider != null diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpLogExporter.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpLogExporter.cs index dec5e8cc3e9..97d07468311 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpLogExporter.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpLogExporter.cs @@ -1,12 +1,13 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 +using System.Buffers.Binary; using System.Diagnostics; using OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation; +using OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.Serializer; using OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.Transmission; using OpenTelemetry.Logs; -using OtlpCollector = OpenTelemetry.Proto.Collector.Logs.V1; -using OtlpResource = OpenTelemetry.Proto.Resource.V1; +using OpenTelemetry.Resources; namespace OpenTelemetry.Exporter; @@ -16,10 +17,17 @@ namespace OpenTelemetry.Exporter; /// public sealed class OtlpLogExporter : BaseExporter { - private readonly OtlpExporterTransmissionHandler transmissionHandler; - private readonly OtlpLogRecordTransformer otlpLogRecordTransformer; + private readonly SdkLimitOptions sdkLimitOptions; + private readonly ExperimentalOptions experimentalOptions; + private readonly OtlpExporterTransmissionHandler transmissionHandler; + private readonly int startWritePosition; - private OtlpResource.Resource? processResource; + private Resource? resource; + + // Initial buffer size set to ~732KB. + // This choice allows us to gradually grow the buffer while targeting a final capacity of around 100 MB, + // by the 7th doubling to maintain efficient allocation without frequent resizing. + private byte[] buffer = new byte[750000]; /// /// Initializes a new instance of the class. @@ -36,24 +44,24 @@ public OtlpLogExporter(OtlpExporterOptions options) /// . /// . /// . - /// . + /// . internal OtlpLogExporter( OtlpExporterOptions exporterOptions, SdkLimitOptions sdkLimitOptions, ExperimentalOptions experimentalOptions, - OtlpExporterTransmissionHandler? transmissionHandler = null) + OtlpExporterTransmissionHandler? transmissionHandler = null) { Debug.Assert(exporterOptions != null, "exporterOptions was null"); Debug.Assert(sdkLimitOptions != null, "sdkLimitOptions was null"); Debug.Assert(experimentalOptions != null, "experimentalOptions was null"); - this.transmissionHandler = transmissionHandler ?? exporterOptions!.GetLogsExportTransmissionHandler(experimentalOptions!); - - this.otlpLogRecordTransformer = new OtlpLogRecordTransformer(sdkLimitOptions!, experimentalOptions!); + this.experimentalOptions = experimentalOptions!; + this.sdkLimitOptions = sdkLimitOptions!; + this.startWritePosition = exporterOptions!.Protocol == OtlpExportProtocol.Grpc ? 5 : 0; + this.transmissionHandler = transmissionHandler ?? exporterOptions!.GetProtobufExportTransmissionHandler(experimentalOptions!, OtlpSignalType.Logs); } - internal OtlpResource.Resource ProcessResource - => this.processResource ??= this.ParentProvider.GetResource().ToOtlpResource(); + internal Resource Resource => this.resource ??= this.ParentProvider.GetResource(); /// public override ExportResult Export(in Batch logRecordBatch) @@ -61,36 +69,59 @@ public override ExportResult Export(in Batch logRecordBatch) // Prevents the exporter's gRPC and HTTP operations from being instrumented. using var scope = SuppressInstrumentationScope.Begin(); - OtlpCollector.ExportLogsServiceRequest? request = null; - try { - request = this.otlpLogRecordTransformer.BuildExportRequest(this.ProcessResource, logRecordBatch); + int writePosition = ProtobufOtlpLogSerializer.WriteLogsData(this.buffer, this.startWritePosition, this.sdkLimitOptions, this.experimentalOptions, this.Resource, logRecordBatch); + + if (this.startWritePosition == 5) + { + // Grpc payload consists of 3 parts + // byte 0 - Specifying if the payload is compressed. + // 1-4 byte - Specifies the length of payload in big endian format. + // 5 and above - Protobuf serialized data. + Span data = new Span(this.buffer, 1, 4); + var dataLength = writePosition - 5; + BinaryPrimitives.WriteUInt32BigEndian(data, (uint)dataLength); + } - if (!this.transmissionHandler.TrySubmitRequest(request)) + if (!this.transmissionHandler.TrySubmitRequest(this.buffer, writePosition)) { return ExportResult.Failure; } } + catch (IndexOutOfRangeException) + { + if (!this.IncreaseBufferSize()) + { + throw; + } + } catch (Exception ex) { OpenTelemetryProtocolExporterEventSource.Log.ExportMethodException(ex); return ExportResult.Failure; } - finally - { - if (request != null) - { - this.otlpLogRecordTransformer.Return(request); - } - } return ExportResult.Success; } /// - protected override bool OnShutdown(int timeoutMilliseconds) + protected override bool OnShutdown(int timeoutMilliseconds) => this.transmissionHandler?.Shutdown(timeoutMilliseconds) ?? true; + + // TODO: Consider moving this to a shared utility class. + private bool IncreaseBufferSize() { - return this.transmissionHandler?.Shutdown(timeoutMilliseconds) ?? true; + var newBufferSize = this.buffer.Length * 2; + + if (newBufferSize > 100 * 1024 * 1024) + { + return false; + } + + var newBuffer = new byte[newBufferSize]; + this.buffer.CopyTo(newBuffer, 0); + this.buffer = newBuffer; + + return true; } } diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpLogExporterHelperExtensions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpLogExporterHelperExtensions.cs index 0929fe0cc70..42faf425356 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpLogExporterHelperExtensions.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpLogExporterHelperExtensions.cs @@ -305,16 +305,10 @@ internal static BaseProcessor BuildOtlpLogExporter( * "OtlpLogExporter"); */ - BaseExporter otlpExporter; - - if (experimentalOptions != null && experimentalOptions.UseCustomProtobufSerializer) - { - otlpExporter = new ProtobufOtlpLogExporter(exporterOptions!, sdkLimitOptions!, experimentalOptions!); - } - else - { - otlpExporter = new OtlpLogExporter(exporterOptions!, sdkLimitOptions!, experimentalOptions!); - } + BaseExporter otlpExporter = new OtlpLogExporter( + exporterOptions!, + sdkLimitOptions!, + experimentalOptions!); if (configureExporterInstance != null) { diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpMetricExporter.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpMetricExporter.cs index aef2bdc4fe3..1014c1c2edf 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpMetricExporter.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpMetricExporter.cs @@ -1,12 +1,13 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 +using System.Buffers.Binary; using System.Diagnostics; using OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation; +using OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.Serializer; using OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.Transmission; using OpenTelemetry.Metrics; -using OtlpCollector = OpenTelemetry.Proto.Collector.Metrics.V1; -using OtlpResource = OpenTelemetry.Proto.Resource.V1; +using OpenTelemetry.Resources; namespace OpenTelemetry.Exporter; @@ -16,9 +17,15 @@ namespace OpenTelemetry.Exporter; /// public class OtlpMetricExporter : BaseExporter { - private readonly OtlpExporterTransmissionHandler transmissionHandler; + private readonly OtlpExporterTransmissionHandler transmissionHandler; + private readonly int startWritePosition; - private OtlpResource.Resource? processResource; + private Resource? resource; + + // Initial buffer size set to ~732KB. + // This choice allows us to gradually grow the buffer while targeting a final capacity of around 100 MB, + // by the 7th doubling to maintain efficient allocation without frequent resizing. + private byte[] buffer = new byte[750000]; /// /// Initializes a new instance of the class. @@ -34,19 +41,20 @@ public OtlpMetricExporter(OtlpExporterOptions options) /// /// . /// . - /// . + /// . internal OtlpMetricExporter( OtlpExporterOptions exporterOptions, ExperimentalOptions experimentalOptions, - OtlpExporterTransmissionHandler? transmissionHandler = null) + OtlpExporterTransmissionHandler? transmissionHandler = null) { Debug.Assert(exporterOptions != null, "exporterOptions was null"); Debug.Assert(experimentalOptions != null, "experimentalOptions was null"); - this.transmissionHandler = transmissionHandler ?? exporterOptions!.GetMetricsExportTransmissionHandler(experimentalOptions!); + this.startWritePosition = exporterOptions!.Protocol == OtlpExportProtocol.Grpc ? 5 : 0; + this.transmissionHandler = transmissionHandler ?? exporterOptions!.GetProtobufExportTransmissionHandler(experimentalOptions!, OtlpSignalType.Metrics); } - internal OtlpResource.Resource ProcessResource => this.processResource ??= this.ParentProvider.GetResource().ToOtlpResource(); + internal Resource Resource => this.resource ??= this.ParentProvider.GetResource(); /// public override ExportResult Export(in Batch metrics) @@ -54,33 +62,59 @@ public override ExportResult Export(in Batch metrics) // Prevents the exporter's gRPC and HTTP operations from being instrumented. using var scope = SuppressInstrumentationScope.Begin(); - var request = new OtlpCollector.ExportMetricsServiceRequest(); - try { - request.AddMetrics(this.ProcessResource, metrics); + int writePosition = ProtobufOtlpMetricSerializer.WriteMetricsData(this.buffer, this.startWritePosition, this.Resource, metrics); + + if (this.startWritePosition == 5) + { + // Grpc payload consists of 3 parts + // byte 0 - Specifying if the payload is compressed. + // 1-4 byte - Specifies the length of payload in big endian format. + // 5 and above - Protobuf serialized data. + Span data = new Span(this.buffer, 1, 4); + var dataLength = writePosition - 5; + BinaryPrimitives.WriteUInt32BigEndian(data, (uint)dataLength); + } - if (!this.transmissionHandler.TrySubmitRequest(request)) + if (!this.transmissionHandler.TrySubmitRequest(this.buffer, writePosition)) { return ExportResult.Failure; } } + catch (IndexOutOfRangeException) + { + if (!this.IncreaseBufferSize()) + { + throw; + } + } catch (Exception ex) { OpenTelemetryProtocolExporterEventSource.Log.ExportMethodException(ex); return ExportResult.Failure; } - finally - { - request.Return(); - } return ExportResult.Success; } /// - protected override bool OnShutdown(int timeoutMilliseconds) + protected override bool OnShutdown(int timeoutMilliseconds) => this.transmissionHandler.Shutdown(timeoutMilliseconds); + + // TODO: Consider moving this to a shared utility class. + private bool IncreaseBufferSize() { - return this.transmissionHandler.Shutdown(timeoutMilliseconds); + var newBufferSize = this.buffer.Length * 2; + + if (newBufferSize > 100 * 1024 * 1024) + { + return false; + } + + var newBuffer = new byte[newBufferSize]; + this.buffer.CopyTo(newBuffer, 0); + this.buffer = newBuffer; + + return true; } } diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpMetricExporterExtensions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpMetricExporterExtensions.cs index ab42b025478..1a8ef2f1b42 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpMetricExporterExtensions.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpMetricExporterExtensions.cs @@ -175,16 +175,7 @@ internal static MetricReader BuildOtlpExporterMetricReader( exporterOptions!.TryEnableIHttpClientFactoryIntegration(serviceProvider!, "OtlpMetricExporter"); - BaseExporter metricExporter; - - if (experimentalOptions != null && experimentalOptions.UseCustomProtobufSerializer) - { - metricExporter = new ProtobufOtlpMetricExporter(exporterOptions!, experimentalOptions!); - } - else - { - metricExporter = new OtlpMetricExporter(exporterOptions!, experimentalOptions!); - } + BaseExporter metricExporter = new OtlpMetricExporter(exporterOptions!, experimentalOptions!); if (configureExporterInstance != null) { diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpTraceExporter.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpTraceExporter.cs index bc7e062f648..a9cf0eb0477 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpTraceExporter.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpTraceExporter.cs @@ -17,7 +17,7 @@ namespace OpenTelemetry.Exporter; public class OtlpTraceExporter : BaseExporter { private readonly SdkLimitOptions sdkLimitOptions; - private readonly ProtobufOtlpExporterTransmissionHandler transmissionHandler; + private readonly OtlpExporterTransmissionHandler transmissionHandler; private readonly int startWritePosition; private Resource? resource; @@ -42,12 +42,12 @@ public OtlpTraceExporter(OtlpExporterOptions options) /// . /// . /// . - /// . + /// . internal OtlpTraceExporter( OtlpExporterOptions exporterOptions, SdkLimitOptions sdkLimitOptions, ExperimentalOptions experimentalOptions, - ProtobufOtlpExporterTransmissionHandler? transmissionHandler = null) + OtlpExporterTransmissionHandler? transmissionHandler = null) { Debug.Assert(exporterOptions != null, "exporterOptions was null"); Debug.Assert(sdkLimitOptions != null, "sdkLimitOptions was null"); diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/ProtobufOtlpLogExporter.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/ProtobufOtlpLogExporter.cs deleted file mode 100644 index 32ad66ccff4..00000000000 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/ProtobufOtlpLogExporter.cs +++ /dev/null @@ -1,127 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -using System.Buffers.Binary; -using System.Diagnostics; -using OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation; -using OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.Serializer; -using OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.Transmission; -using OpenTelemetry.Logs; -using OpenTelemetry.Resources; - -namespace OpenTelemetry.Exporter; - -/// -/// Exporter consuming and exporting the data using -/// the OpenTelemetry protocol (OTLP). -/// -internal sealed class ProtobufOtlpLogExporter : BaseExporter -{ - private readonly SdkLimitOptions sdkLimitOptions; - private readonly ExperimentalOptions experimentalOptions; - private readonly ProtobufOtlpExporterTransmissionHandler transmissionHandler; - private readonly int startWritePosition; - - private Resource? resource; - - // Initial buffer size set to ~732KB. - // This choice allows us to gradually grow the buffer while targeting a final capacity of around 100 MB, - // by the 7th doubling to maintain efficient allocation without frequent resizing. - private byte[] buffer = new byte[750000]; - - /// - /// Initializes a new instance of the class. - /// - /// Configuration options for the exporter. - public ProtobufOtlpLogExporter(OtlpExporterOptions options) - : this(options, sdkLimitOptions: new(), experimentalOptions: new(), transmissionHandler: null) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// . - /// . - /// . - /// . - internal ProtobufOtlpLogExporter( - OtlpExporterOptions exporterOptions, - SdkLimitOptions sdkLimitOptions, - ExperimentalOptions experimentalOptions, - ProtobufOtlpExporterTransmissionHandler? transmissionHandler = null) - { - Debug.Assert(exporterOptions != null, "exporterOptions was null"); - Debug.Assert(sdkLimitOptions != null, "sdkLimitOptions was null"); - Debug.Assert(experimentalOptions != null, "experimentalOptions was null"); - - this.experimentalOptions = experimentalOptions!; - this.sdkLimitOptions = sdkLimitOptions!; - this.startWritePosition = exporterOptions!.Protocol == OtlpExportProtocol.Grpc ? 5 : 0; - this.transmissionHandler = transmissionHandler ?? exporterOptions!.GetProtobufExportTransmissionHandler(experimentalOptions!, OtlpSignalType.Logs); - } - - internal Resource Resource => this.resource ??= this.ParentProvider.GetResource(); - - /// - public override ExportResult Export(in Batch logRecordBatch) - { - // Prevents the exporter's gRPC and HTTP operations from being instrumented. - using var scope = SuppressInstrumentationScope.Begin(); - - try - { - int writePosition = ProtobufOtlpLogSerializer.WriteLogsData(this.buffer, this.startWritePosition, this.sdkLimitOptions, this.experimentalOptions, this.Resource, logRecordBatch); - - if (this.startWritePosition == 5) - { - // Grpc payload consists of 3 parts - // byte 0 - Specifying if the payload is compressed. - // 1-4 byte - Specifies the length of payload in big endian format. - // 5 and above - Protobuf serialized data. - Span data = new Span(this.buffer, 1, 4); - var dataLength = writePosition - 5; - BinaryPrimitives.WriteUInt32BigEndian(data, (uint)dataLength); - } - - if (!this.transmissionHandler.TrySubmitRequest(this.buffer, writePosition)) - { - return ExportResult.Failure; - } - } - catch (IndexOutOfRangeException) - { - if (!this.IncreaseBufferSize()) - { - throw; - } - } - catch (Exception ex) - { - OpenTelemetryProtocolExporterEventSource.Log.ExportMethodException(ex); - return ExportResult.Failure; - } - - return ExportResult.Success; - } - - /// - protected override bool OnShutdown(int timeoutMilliseconds) => this.transmissionHandler?.Shutdown(timeoutMilliseconds) ?? true; - - // TODO: Consider moving this to a shared utility class. - private bool IncreaseBufferSize() - { - var newBufferSize = this.buffer.Length * 2; - - if (newBufferSize > 100 * 1024 * 1024) - { - return false; - } - - var newBuffer = new byte[newBufferSize]; - this.buffer.CopyTo(newBuffer, 0); - this.buffer = newBuffer; - - return true; - } -} diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/ProtobufOtlpMetricExporter.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/ProtobufOtlpMetricExporter.cs deleted file mode 100644 index 145e03e00ed..00000000000 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/ProtobufOtlpMetricExporter.cs +++ /dev/null @@ -1,120 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -using System.Buffers.Binary; -using System.Diagnostics; -using OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation; -using OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.Serializer; -using OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.Transmission; -using OpenTelemetry.Metrics; -using OpenTelemetry.Resources; - -namespace OpenTelemetry.Exporter; - -/// -/// Exporter consuming and exporting the data using -/// the OpenTelemetry protocol (OTLP). -/// -internal sealed class ProtobufOtlpMetricExporter : BaseExporter -{ - private readonly ProtobufOtlpExporterTransmissionHandler transmissionHandler; - private readonly int startWritePosition; - - private Resource? resource; - - // Initial buffer size set to ~732KB. - // This choice allows us to gradually grow the buffer while targeting a final capacity of around 100 MB, - // by the 7th doubling to maintain efficient allocation without frequent resizing. - private byte[] buffer = new byte[750000]; - - /// - /// Initializes a new instance of the class. - /// - /// Configuration options for the exporter. - public ProtobufOtlpMetricExporter(OtlpExporterOptions options) - : this(options, experimentalOptions: new(), transmissionHandler: null) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// . - /// . - /// . - internal ProtobufOtlpMetricExporter( - OtlpExporterOptions exporterOptions, - ExperimentalOptions experimentalOptions, - ProtobufOtlpExporterTransmissionHandler? transmissionHandler = null) - { - Debug.Assert(exporterOptions != null, "exporterOptions was null"); - Debug.Assert(experimentalOptions != null, "experimentalOptions was null"); - - this.startWritePosition = exporterOptions!.Protocol == OtlpExportProtocol.Grpc ? 5 : 0; - this.transmissionHandler = transmissionHandler ?? exporterOptions!.GetProtobufExportTransmissionHandler(experimentalOptions!, OtlpSignalType.Metrics); - } - - internal Resource Resource => this.resource ??= this.ParentProvider.GetResource(); - - /// - public override ExportResult Export(in Batch metrics) - { - // Prevents the exporter's gRPC and HTTP operations from being instrumented. - using var scope = SuppressInstrumentationScope.Begin(); - - try - { - int writePosition = ProtobufOtlpMetricSerializer.WriteMetricsData(this.buffer, this.startWritePosition, this.Resource, metrics); - - if (this.startWritePosition == 5) - { - // Grpc payload consists of 3 parts - // byte 0 - Specifying if the payload is compressed. - // 1-4 byte - Specifies the length of payload in big endian format. - // 5 and above - Protobuf serialized data. - Span data = new Span(this.buffer, 1, 4); - var dataLength = writePosition - 5; - BinaryPrimitives.WriteUInt32BigEndian(data, (uint)dataLength); - } - - if (!this.transmissionHandler.TrySubmitRequest(this.buffer, writePosition)) - { - return ExportResult.Failure; - } - } - catch (IndexOutOfRangeException) - { - if (!this.IncreaseBufferSize()) - { - throw; - } - } - catch (Exception ex) - { - OpenTelemetryProtocolExporterEventSource.Log.ExportMethodException(ex); - return ExportResult.Failure; - } - - return ExportResult.Success; - } - - /// - protected override bool OnShutdown(int timeoutMilliseconds) => this.transmissionHandler.Shutdown(timeoutMilliseconds); - - // TODO: Consider moving this to a shared utility class. - private bool IncreaseBufferSize() - { - var newBufferSize = this.buffer.Length * 2; - - if (newBufferSize > 100 * 1024 * 1024) - { - return false; - } - - var newBuffer = new byte[newBufferSize]; - this.buffer.CopyTo(newBuffer, 0); - this.buffer = newBuffer; - - return true; - } -} diff --git a/src/Shared/Proto/README.md b/src/Shared/Proto/README.md new file mode 100644 index 00000000000..fdd15e95f94 --- /dev/null +++ b/src/Shared/Proto/README.md @@ -0,0 +1,5 @@ +# OpenTelemetry Protocol (OTLP) Specification + +`.proto` files are copied from the +[`opentelemetry-proto`](https://github.com/open-telemetry/opentelemetry-proto/commit/1a931b4b57c34e7fd8f7dddcaa9b7587840e9c08) +repo. diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/google/rpc/error_details.proto b/src/Shared/Proto/google/rpc/error_details.proto similarity index 100% rename from src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/google/rpc/error_details.proto rename to src/Shared/Proto/google/rpc/error_details.proto diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/google/rpc/status.proto b/src/Shared/Proto/google/rpc/status.proto similarity index 100% rename from src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/google/rpc/status.proto rename to src/Shared/Proto/google/rpc/status.proto diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/opentelemetry/proto/collector/README.md b/src/Shared/Proto/opentelemetry/proto/collector/README.md similarity index 100% rename from src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/opentelemetry/proto/collector/README.md rename to src/Shared/Proto/opentelemetry/proto/collector/README.md diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/opentelemetry/proto/collector/logs/v1/logs_service.proto b/src/Shared/Proto/opentelemetry/proto/collector/logs/v1/logs_service.proto similarity index 100% rename from src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/opentelemetry/proto/collector/logs/v1/logs_service.proto rename to src/Shared/Proto/opentelemetry/proto/collector/logs/v1/logs_service.proto diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/opentelemetry/proto/collector/logs/v1/logs_service_http.yaml b/src/Shared/Proto/opentelemetry/proto/collector/logs/v1/logs_service_http.yaml similarity index 100% rename from src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/opentelemetry/proto/collector/logs/v1/logs_service_http.yaml rename to src/Shared/Proto/opentelemetry/proto/collector/logs/v1/logs_service_http.yaml diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/opentelemetry/proto/collector/metrics/v1/metrics_service.proto b/src/Shared/Proto/opentelemetry/proto/collector/metrics/v1/metrics_service.proto similarity index 100% rename from src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/opentelemetry/proto/collector/metrics/v1/metrics_service.proto rename to src/Shared/Proto/opentelemetry/proto/collector/metrics/v1/metrics_service.proto diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/opentelemetry/proto/collector/metrics/v1/metrics_service_http.yaml b/src/Shared/Proto/opentelemetry/proto/collector/metrics/v1/metrics_service_http.yaml similarity index 100% rename from src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/opentelemetry/proto/collector/metrics/v1/metrics_service_http.yaml rename to src/Shared/Proto/opentelemetry/proto/collector/metrics/v1/metrics_service_http.yaml diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/opentelemetry/proto/collector/profiles/v1experimental/profiles_service.proto b/src/Shared/Proto/opentelemetry/proto/collector/profiles/v1experimental/profiles_service.proto similarity index 100% rename from src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/opentelemetry/proto/collector/profiles/v1experimental/profiles_service.proto rename to src/Shared/Proto/opentelemetry/proto/collector/profiles/v1experimental/profiles_service.proto diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/opentelemetry/proto/collector/profiles/v1experimental/profiles_service_http.yaml b/src/Shared/Proto/opentelemetry/proto/collector/profiles/v1experimental/profiles_service_http.yaml similarity index 100% rename from src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/opentelemetry/proto/collector/profiles/v1experimental/profiles_service_http.yaml rename to src/Shared/Proto/opentelemetry/proto/collector/profiles/v1experimental/profiles_service_http.yaml diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/opentelemetry/proto/collector/trace/v1/trace_service.proto b/src/Shared/Proto/opentelemetry/proto/collector/trace/v1/trace_service.proto similarity index 100% rename from src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/opentelemetry/proto/collector/trace/v1/trace_service.proto rename to src/Shared/Proto/opentelemetry/proto/collector/trace/v1/trace_service.proto diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/opentelemetry/proto/collector/trace/v1/trace_service_http.yaml b/src/Shared/Proto/opentelemetry/proto/collector/trace/v1/trace_service_http.yaml similarity index 100% rename from src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/opentelemetry/proto/collector/trace/v1/trace_service_http.yaml rename to src/Shared/Proto/opentelemetry/proto/collector/trace/v1/trace_service_http.yaml diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/opentelemetry/proto/common/v1/common.proto b/src/Shared/Proto/opentelemetry/proto/common/v1/common.proto similarity index 100% rename from src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/opentelemetry/proto/common/v1/common.proto rename to src/Shared/Proto/opentelemetry/proto/common/v1/common.proto diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/opentelemetry/proto/logs/v1/logs.proto b/src/Shared/Proto/opentelemetry/proto/logs/v1/logs.proto similarity index 100% rename from src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/opentelemetry/proto/logs/v1/logs.proto rename to src/Shared/Proto/opentelemetry/proto/logs/v1/logs.proto diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/opentelemetry/proto/metrics/v1/metrics.proto b/src/Shared/Proto/opentelemetry/proto/metrics/v1/metrics.proto similarity index 100% rename from src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/opentelemetry/proto/metrics/v1/metrics.proto rename to src/Shared/Proto/opentelemetry/proto/metrics/v1/metrics.proto diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/opentelemetry/proto/profiles/v1experimental/pprofextended.proto b/src/Shared/Proto/opentelemetry/proto/profiles/v1experimental/pprofextended.proto similarity index 100% rename from src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/opentelemetry/proto/profiles/v1experimental/pprofextended.proto rename to src/Shared/Proto/opentelemetry/proto/profiles/v1experimental/pprofextended.proto diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/opentelemetry/proto/profiles/v1experimental/profiles.proto b/src/Shared/Proto/opentelemetry/proto/profiles/v1experimental/profiles.proto similarity index 100% rename from src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/opentelemetry/proto/profiles/v1experimental/profiles.proto rename to src/Shared/Proto/opentelemetry/proto/profiles/v1experimental/profiles.proto diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/opentelemetry/proto/resource/v1/resource.proto b/src/Shared/Proto/opentelemetry/proto/resource/v1/resource.proto similarity index 100% rename from src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/opentelemetry/proto/resource/v1/resource.proto rename to src/Shared/Proto/opentelemetry/proto/resource/v1/resource.proto diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/opentelemetry/proto/trace/v1/trace.proto b/src/Shared/Proto/opentelemetry/proto/trace/v1/trace.proto similarity index 100% rename from src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/opentelemetry/proto/trace/v1/trace.proto rename to src/Shared/Proto/opentelemetry/proto/trace/v1/trace.proto diff --git a/test/Benchmarks/Benchmarks.csproj b/test/Benchmarks/Benchmarks.csproj index d3b36601264..34d74cb0bd7 100644 --- a/test/Benchmarks/Benchmarks.csproj +++ b/test/Benchmarks/Benchmarks.csproj @@ -1,4 +1,4 @@ - + Exe @@ -27,4 +27,17 @@ + + + + + + + + + + $(RepoRoot)\src\Shared\Proto + + + diff --git a/test/Benchmarks/Exporter/OtlpGrpcExporterBenchmarks.cs b/test/Benchmarks/Exporter/OtlpGrpcExporterBenchmarks.cs index ba7163a179a..7d880a04ddf 100644 --- a/test/Benchmarks/Exporter/OtlpGrpcExporterBenchmarks.cs +++ b/test/Benchmarks/Exporter/OtlpGrpcExporterBenchmarks.cs @@ -35,7 +35,7 @@ public void GlobalSetup() options, new SdkLimitOptions(), new ExperimentalOptions(), - new ProtobufOtlpExporterTransmissionHandler(new ProtobufOtlpGrpcExportClient(options, options.HttpClientFactory(), "opentelemetry.proto.collector.trace.v1.TraceService/Export"), options.TimeoutMilliseconds)); + new OtlpExporterTransmissionHandler(new OtlpGrpcExportClient(options, options.HttpClientFactory(), "opentelemetry.proto.collector.trace.v1.TraceService/Export"), options.TimeoutMilliseconds)); this.activity = ActivityHelper.CreateTestActivity(); this.activityBatch = new CircularBuffer(this.NumberOfSpans); diff --git a/test/Benchmarks/Exporter/OtlpHttpExporterBenchmarks.cs b/test/Benchmarks/Exporter/OtlpHttpExporterBenchmarks.cs index 9603c147ac4..02953143bcb 100644 --- a/test/Benchmarks/Exporter/OtlpHttpExporterBenchmarks.cs +++ b/test/Benchmarks/Exporter/OtlpHttpExporterBenchmarks.cs @@ -63,7 +63,7 @@ public void GlobalSetup() options, new SdkLimitOptions(), new ExperimentalOptions(), - new ProtobufOtlpExporterTransmissionHandler(new ProtobufOtlpHttpExportClient(options, options.HttpClientFactory(), "v1/traces"), options.TimeoutMilliseconds)); + new OtlpExporterTransmissionHandler(new OtlpHttpExportClient(options, options.HttpClientFactory(), "v1/traces"), options.TimeoutMilliseconds)); this.activity = ActivityHelper.CreateTestActivity(); this.activityBatch = new CircularBuffer(this.NumberOfSpans); diff --git a/test/Benchmarks/Exporter/OtlpLogExporterBenchmarks.cs b/test/Benchmarks/Exporter/OtlpLogExporterBenchmarks.cs index 1ec5f4770d4..8dc435b8fd8 100644 --- a/test/Benchmarks/Exporter/OtlpLogExporterBenchmarks.cs +++ b/test/Benchmarks/Exporter/OtlpLogExporterBenchmarks.cs @@ -16,7 +16,7 @@ using OpenTelemetry.Logs; using OpenTelemetry.Tests; using OpenTelemetryProtocol::OpenTelemetry.Exporter; -using OtlpCollector = OpenTelemetryProtocol::OpenTelemetry.Proto.Collector.Logs.V1; +using OtlpCollector = OpenTelemetry.Proto.Collector.Logs.V1; /* BenchmarkDotNet v0.13.6, Windows 11 (10.0.22621.2134/22H2/2022Update/SunValley2) (Hyper-V) diff --git a/test/Benchmarks/Exporter/OtlpTraceExporterBenchmarks.cs b/test/Benchmarks/Exporter/OtlpTraceExporterBenchmarks.cs index 5f338fa6189..cadbe1e89c4 100644 --- a/test/Benchmarks/Exporter/OtlpTraceExporterBenchmarks.cs +++ b/test/Benchmarks/Exporter/OtlpTraceExporterBenchmarks.cs @@ -16,7 +16,7 @@ using OpenTelemetry.Internal; using OpenTelemetry.Tests; using OpenTelemetryProtocol::OpenTelemetry.Exporter; -using OtlpCollector = OpenTelemetryProtocol::OpenTelemetry.Proto.Collector.Trace.V1; +using OtlpCollector = OpenTelemetry.Proto.Collector.Trace.V1; /* BenchmarkDotNet v0.13.6, Windows 11 (10.0.22621.2134/22H2/2022Update/SunValley2) (Hyper-V) diff --git a/test/Benchmarks/TestTraceServiceClient.cs b/test/Benchmarks/TestTraceServiceClient.cs deleted file mode 100644 index fc60f90278a..00000000000 --- a/test/Benchmarks/TestTraceServiceClient.cs +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -extern alias OpenTelemetryProtocol; - -using Grpc.Core; -using OpenTelemetryProtocol::OpenTelemetry.Proto.Collector.Trace.V1; - -namespace Benchmarks; - -internal class TestTraceServiceClient : TraceService.TraceServiceClient -{ - public override ExportTraceServiceResponse Export(ExportTraceServiceRequest request, Metadata? headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default) - { - return new ExportTraceServiceResponse(); - } -} diff --git a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/Implementation/ExportClient/OtlpHttpTraceExportClientTests.cs b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/Implementation/ExportClient/OtlpHttpTraceExportClientTests.cs index b156204429f..e9a4ba80c76 100644 --- a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/Implementation/ExportClient/OtlpHttpTraceExportClientTests.cs +++ b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/Implementation/ExportClient/OtlpHttpTraceExportClientTests.cs @@ -44,7 +44,7 @@ public void NewOtlpHttpTraceExportClient_OtlpExporterOptions_ExporterHasCorrectP Headers = $"{header1.Name}={header1.Value}, {header2.Name} = {header2.Value}", }; - var client = new ProtobufOtlpHttpExportClient(options, options.HttpClientFactory(), "/v1/traces"); + var client = new OtlpHttpExportClient(options, options.HttpClientFactory(), "/v1/traces"); Assert.NotNull(client.HttpClient); @@ -86,7 +86,7 @@ public void SendExportRequest_ExportTraceServiceRequest_SendsCorrectHttpRequest( var httpClient = new HttpClient(testHttpHandler); - var exportClient = new ProtobufOtlpHttpExportClient(options, httpClient, string.Empty); + var exportClient = new OtlpHttpExportClient(options, httpClient, string.Empty); var resourceBuilder = ResourceBuilder.CreateEmpty(); if (includeServiceNameInResource) @@ -159,7 +159,7 @@ void RunTest(Batch batch) // TODO: Revisit once the HttpClient part is overridden. // Assert.IsType(httpRequest.Content); Assert.NotNull(httpRequest.Content); - Assert.Contains(httpRequest.Content.Headers, h => h.Key == "Content-Type" && h.Value.First() == ProtobufOtlpHttpExportClient.MediaHeaderValue.ToString()); + Assert.Contains(httpRequest.Content.Headers, h => h.Key == "Content-Type" && h.Value.First() == OtlpHttpExportClient.MediaHeaderValue.ToString()); var exportTraceRequest = OtlpCollector.ExportTraceServiceRequest.Parser.ParseFrom(testHttpHandler.HttpRequestContent); Assert.NotNull(exportTraceRequest); diff --git a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/Implementation/Serializer/ResourceProtoSerializerTests.cs b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/Implementation/Serializer/ResourceProtoSerializerTests.cs deleted file mode 100644 index 0deb4c585a5..00000000000 --- a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/Implementation/Serializer/ResourceProtoSerializerTests.cs +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -using OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation; -using OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.Serializer; -using OpenTelemetry.Proto.Trace.V1; -using OpenTelemetry.Resources; -using Xunit; - -namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests.Implementation.Serializer; - -public class ResourceProtoSerializerTests -{ - [Fact] - public void CreateResource_SupportedAttributeTypes() - { - // Arrange - byte[] buffer = new byte[1024]; - var attributes = new Dictionary - { - { "string", "stringValue" }, - { "bool", true }, - { "double", 0.1d }, - { "long", 1L }, - - // int and float supported by conversion to long and double - { "int", 1 }, - { "short", (short)1 }, - { "float", 0.1f }, - - // natively supported array types - { "string arr", new string[] { "stringValue1", "stringValue2" } }, - { "bool arr", new bool[] { true } }, - { "double arr", new double[] { 0.1d } }, - { "long arr", new long[] { 1L } }, - - // have to convert to other primitive array types - { "int arr", new int[] { 1, 2, 3 } }, - { "short arr", new short[] { (short)1 } }, - { "float arr", new float[] { 0.1f } }, - }; - - // Act - var resource = ResourceBuilder.CreateEmpty().AddAttributes(attributes).Build(); - var writePosition = ProtobufOtlpResourceSerializer.WriteResource(buffer, 0, resource); - var otlpResource = resource.ToOtlpResource(); - var expectedResourceSpans = new ResourceSpans - { - Resource = otlpResource, - }; - - // Deserialize the ResourceSpans and validate the attributes. - ResourceSpans actualResourceSpans; - using (var stream = new MemoryStream(buffer, 0, writePosition)) - { - actualResourceSpans = ResourceSpans.Parser.ParseFrom(stream); - } - - // Assert - Assert.Equal(expectedResourceSpans.Resource.Attributes.Count, actualResourceSpans.Resource.Attributes.Count); - foreach (var actualAttribute in actualResourceSpans.Resource.Attributes) - { - Assert.Contains(actualAttribute, expectedResourceSpans.Resource.Attributes); - } - } -} diff --git a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/MockCollectorIntegrationTests.cs b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/MockCollectorIntegrationTests.cs index e0fa32e5970..ab382fe728a 100644 --- a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/MockCollectorIntegrationTests.cs +++ b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/MockCollectorIntegrationTests.cs @@ -354,22 +354,22 @@ public async Task HttpPersistentStorageRetryTests(bool usePersistentStorageTrans var exporterOptions = new OtlpExporterOptions() { Endpoint = endpoint, TimeoutMilliseconds = 20000 }; - var exportClient = new ProtobufOtlpHttpExportClient(exporterOptions, new HttpClient(), "/v1/traces"); + var exportClient = new OtlpHttpExportClient(exporterOptions, new HttpClient(), "/v1/traces"); // TODO: update this to configure via experimental environment variable. - ProtobufOtlpExporterTransmissionHandler transmissionHandler; + OtlpExporterTransmissionHandler transmissionHandler; MockFileProvider? mockProvider = null; if (usePersistentStorageTransmissionHandler) { mockProvider = new MockFileProvider(); - transmissionHandler = new ProtobufOtlpExporterPersistentStorageTransmissionHandler( + transmissionHandler = new OtlpExporterPersistentStorageTransmissionHandler( mockProvider, exportClient, exporterOptions.TimeoutMilliseconds); } else { - transmissionHandler = new ProtobufOtlpExporterTransmissionHandler(exportClient, exporterOptions.TimeoutMilliseconds); + transmissionHandler = new OtlpExporterTransmissionHandler(exportClient, exporterOptions.TimeoutMilliseconds); } var configuration = new ConfigurationBuilder() @@ -405,7 +405,7 @@ public async Task HttpPersistentStorageRetryTests(bool usePersistentStorageTrans Assert.Single(mockProvider!.TryGetBlobs()); // Force Retry - Assert.True((transmissionHandler as ProtobufOtlpExporterPersistentStorageTransmissionHandler)?.InitiateAndWaitForRetryProcess(-1)); + Assert.True((transmissionHandler as OtlpExporterPersistentStorageTransmissionHandler)?.InitiateAndWaitForRetryProcess(-1)); Assert.False(mockProvider.TryGetBlob(out _)); } @@ -494,22 +494,22 @@ public async Task GrpcPersistentStorageRetryTests(bool usePersistentStorageTrans var exporterOptions = new OtlpExporterOptions() { Endpoint = endpoint, TimeoutMilliseconds = 20000 }; - var exportClient = new ProtobufOtlpGrpcExportClient(exporterOptions, new HttpClient(), "opentelemetry.proto.collector.trace.v1.TraceService/Export"); + var exportClient = new OtlpGrpcExportClient(exporterOptions, new HttpClient(), "opentelemetry.proto.collector.trace.v1.TraceService/Export"); // TODO: update this to configure via experimental environment variable. - ProtobufOtlpExporterTransmissionHandler transmissionHandler; + OtlpExporterTransmissionHandler transmissionHandler; MockFileProvider? mockProvider = null; if (usePersistentStorageTransmissionHandler) { mockProvider = new MockFileProvider(); - transmissionHandler = new ProtobufOtlpExporterPersistentStorageTransmissionHandler( + transmissionHandler = new OtlpExporterPersistentStorageTransmissionHandler( mockProvider, exportClient, exporterOptions.TimeoutMilliseconds); } else { - transmissionHandler = new ProtobufOtlpExporterTransmissionHandler(exportClient, exporterOptions.TimeoutMilliseconds); + transmissionHandler = new OtlpExporterTransmissionHandler(exportClient, exporterOptions.TimeoutMilliseconds); } var configuration = new ConfigurationBuilder() @@ -545,7 +545,7 @@ public async Task GrpcPersistentStorageRetryTests(bool usePersistentStorageTrans Assert.Single(mockProvider.TryGetBlobs()); // Force Retry - Assert.True((transmissionHandler as ProtobufOtlpExporterPersistentStorageTransmissionHandler)?.InitiateAndWaitForRetryProcess(-1)); + Assert.True((transmissionHandler as OtlpExporterPersistentStorageTransmissionHandler)?.InitiateAndWaitForRetryProcess(-1)); Assert.False(mockProvider.TryGetBlob(out _)); } 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 1b9526a3ea7..59da00ba6aa 100644 --- a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests.csproj +++ b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests.csproj @@ -31,6 +31,10 @@ + + + + @@ -49,4 +53,10 @@ + + + $(RepoRoot)\src\Shared\Proto + + + diff --git a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpAttributeTests.cs b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpAttributeTests.cs index a325874fb30..05ffe584eb0 100644 --- a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpAttributeTests.cs +++ b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpAttributeTests.cs @@ -2,8 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 using System.Diagnostics.CodeAnalysis; -using Google.Protobuf.Collections; -using OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation; +using OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.Serializer; using Xunit; using OtlpCommon = OpenTelemetry.Proto.Common.V1; @@ -269,12 +268,22 @@ public void ExceptionInToStringIsCaught() private static bool TryTransformTag(KeyValuePair tag, [NotNullWhen(true)] out OtlpCommon.KeyValue? attribute) { - var destination = new RepeatedField(); + ProtobufOtlpTagWriter.OtlpTagWriterState otlpTagWriterState = new ProtobufOtlpTagWriter.OtlpTagWriterState + { + Buffer = new byte[1024], + WritePosition = 0, + }; - if (OtlpTagWriter.Instance.TryWriteTag(ref destination, tag)) + if (ProtobufOtlpTagWriter.Instance.TryWriteTag(ref otlpTagWriterState, tag)) { - Assert.NotEmpty(destination); - attribute = destination[0]; + // Deserialize the ResourceSpans and validate the attributes. + using (var stream = new MemoryStream(otlpTagWriterState.Buffer, 0, otlpTagWriterState.WritePosition)) + { + var keyValue = OtlpCommon.KeyValue.Parser.ParseFrom(stream); + Assert.NotNull(keyValue); + attribute = keyValue; + } + return true; } diff --git a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpExporterOptionsExtensionsTests.cs b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpExporterOptionsExtensionsTests.cs index 4e139cd99ec..207a20483a4 100644 --- a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpExporterOptionsExtensionsTests.cs +++ b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpExporterOptionsExtensionsTests.cs @@ -9,67 +9,11 @@ using OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.ExportClient; using OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.Transmission; using Xunit; -using Xunit.Sdk; namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests; public class OtlpExporterOptionsExtensionsTests { - [Theory] - [InlineData("key=value", new string[] { "key" }, new string[] { "value" })] - [InlineData("key1=value1,key2=value2", new string[] { "key1", "key2" }, new string[] { "value1", "value2" })] - [InlineData("key1 = value1, key2=value2 ", new string[] { "key1", "key2" }, new string[] { "value1", "value2" })] - [InlineData("key==value", new string[] { "key" }, new string[] { "=value" })] - [InlineData("access-token=abc=/123,timeout=1234", new string[] { "access-token", "timeout" }, new string[] { "abc=/123", "1234" })] - [InlineData("key1=value1;key2=value2", new string[] { "key1" }, new string[] { "value1;key2=value2" })] // semicolon is not treated as a delimiter (https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/protocol/exporter.md#specifying-headers-via-environment-variables) - [InlineData("Authorization=Basic%20AAA", new string[] { "authorization" }, new string[] { "Basic AAA" })] - [InlineData("Authorization=Basic AAA", new string[] { "authorization" }, new string[] { "Basic AAA" })] - public void GetMetadataFromHeadersWorksCorrectFormat(string headers, string[] keys, string[] values) - { - var options = new OtlpExporterOptions - { - Headers = headers, - }; - var metadata = options.GetMetadataFromHeaders(); - - Assert.Equal(OtlpExporterOptions.StandardHeaders.Length + keys.Length, metadata.Count); - - for (int i = 0; i < keys.Length; i++) - { - Assert.Contains(metadata, entry => entry.Key == keys[i] && entry.Value == values[i]); - } - - for (int i = 0; i < OtlpExporterOptions.StandardHeaders.Length; i++) - { - // Metadata key is always converted to lowercase. - // See: https://cloud.google.com/dotnet/docs/reference/Grpc.Core/latest/Grpc.Core.Metadata.Entry#Grpc_Core_Metadata_Entry__ctor_System_String_System_String_ - Assert.Contains(metadata, entry => entry.Key == OtlpExporterOptions.StandardHeaders[i].Key.ToLower() && entry.Value == OtlpExporterOptions.StandardHeaders[i].Value); - } - } - - [Theory] - [InlineData("headers")] - [InlineData("key,value")] - public void GetMetadataFromHeadersThrowsExceptionOnInvalidFormat(string headers) - { - try - { - var options = new OtlpExporterOptions - { - Headers = headers, - }; - var metadata = options.GetMetadataFromHeaders(); - } - catch (Exception ex) - { - Assert.IsType(ex); - Assert.Equal("Headers provided in an invalid format.", ex.Message); - return; - } - - throw new XunitException("GetMetadataFromHeaders did not throw an exception for invalid input headers"); - } - [Theory] [InlineData("")] [InlineData(null)] @@ -91,8 +35,8 @@ public void GetHeaders_NoOptionHeaders_ReturnsStandardHeaders(string? optionHead } [Theory] - [InlineData(OtlpExportProtocol.Grpc, typeof(ProtobufOtlpGrpcExportClient))] - [InlineData(OtlpExportProtocol.HttpProtobuf, typeof(ProtobufOtlpHttpExportClient))] + [InlineData(OtlpExportProtocol.Grpc, typeof(OtlpGrpcExportClient))] + [InlineData(OtlpExportProtocol.HttpProtobuf, typeof(OtlpHttpExportClient))] public void GetTraceExportClient_SupportedProtocol_ReturnsCorrectExportClient(OtlpExportProtocol protocol, Type expectedExportClientType) { var options = new OtlpExporterOptions @@ -131,33 +75,15 @@ public void AppendPathIfNotPresent_TracesPath_AppendsCorrectly(string inputUri, } [Theory] - [InlineData(OtlpExportProtocol.Grpc, typeof(ProtobufOtlpGrpcExportClient), false, 10000, null)] - [InlineData(OtlpExportProtocol.HttpProtobuf, typeof(ProtobufOtlpHttpExportClient), false, 10000, null)] - [InlineData(OtlpExportProtocol.HttpProtobuf, typeof(ProtobufOtlpHttpExportClient), true, 8000, null)] - [InlineData(OtlpExportProtocol.Grpc, typeof(OtlpGrpcMetricsExportClient), false, 10000, null)] - [InlineData(OtlpExportProtocol.HttpProtobuf, typeof(OtlpHttpMetricsExportClient), false, 10000, null)] - [InlineData(OtlpExportProtocol.HttpProtobuf, typeof(OtlpHttpMetricsExportClient), true, 8000, null)] - [InlineData(OtlpExportProtocol.Grpc, typeof(OtlpGrpcLogExportClient), false, 10000, null)] - [InlineData(OtlpExportProtocol.HttpProtobuf, typeof(OtlpHttpLogExportClient), false, 10000, null)] - [InlineData(OtlpExportProtocol.HttpProtobuf, typeof(OtlpHttpLogExportClient), true, 8000, null)] - [InlineData(OtlpExportProtocol.Grpc, typeof(ProtobufOtlpGrpcExportClient), false, 10000, "in_memory")] - [InlineData(OtlpExportProtocol.HttpProtobuf, typeof(ProtobufOtlpHttpExportClient), false, 10000, "in_memory")] - [InlineData(OtlpExportProtocol.HttpProtobuf, typeof(ProtobufOtlpHttpExportClient), true, 8000, "in_memory")] - [InlineData(OtlpExportProtocol.Grpc, typeof(OtlpGrpcMetricsExportClient), false, 10000, "in_memory")] - [InlineData(OtlpExportProtocol.HttpProtobuf, typeof(OtlpHttpMetricsExportClient), false, 10000, "in_memory")] - [InlineData(OtlpExportProtocol.HttpProtobuf, typeof(OtlpHttpMetricsExportClient), true, 8000, "in_memory")] - [InlineData(OtlpExportProtocol.Grpc, typeof(OtlpGrpcLogExportClient), false, 10000, "in_memory")] - [InlineData(OtlpExportProtocol.HttpProtobuf, typeof(OtlpHttpLogExportClient), false, 10000, "in_memory")] - [InlineData(OtlpExportProtocol.HttpProtobuf, typeof(OtlpHttpLogExportClient), true, 8000, "in_memory")] - [InlineData(OtlpExportProtocol.Grpc, typeof(ProtobufOtlpGrpcExportClient), false, 10000, "disk")] - [InlineData(OtlpExportProtocol.HttpProtobuf, typeof(ProtobufOtlpHttpExportClient), false, 10000, "disk")] - [InlineData(OtlpExportProtocol.HttpProtobuf, typeof(ProtobufOtlpHttpExportClient), true, 8000, "disk")] - [InlineData(OtlpExportProtocol.Grpc, typeof(OtlpGrpcMetricsExportClient), false, 10000, "disk")] - [InlineData(OtlpExportProtocol.HttpProtobuf, typeof(OtlpHttpMetricsExportClient), false, 10000, "disk")] - [InlineData(OtlpExportProtocol.HttpProtobuf, typeof(OtlpHttpMetricsExportClient), true, 8000, "disk")] - [InlineData(OtlpExportProtocol.Grpc, typeof(OtlpGrpcLogExportClient), false, 10000, "disk")] - [InlineData(OtlpExportProtocol.HttpProtobuf, typeof(OtlpHttpLogExportClient), false, 10000, "disk")] - [InlineData(OtlpExportProtocol.HttpProtobuf, typeof(OtlpHttpLogExportClient), true, 8000, "disk")] + [InlineData(OtlpExportProtocol.Grpc, typeof(OtlpGrpcExportClient), false, 10000, null)] + [InlineData(OtlpExportProtocol.HttpProtobuf, typeof(OtlpHttpExportClient), false, 10000, null)] + [InlineData(OtlpExportProtocol.HttpProtobuf, typeof(OtlpHttpExportClient), true, 8000, null)] + [InlineData(OtlpExportProtocol.Grpc, typeof(OtlpGrpcExportClient), false, 10000, "in_memory")] + [InlineData(OtlpExportProtocol.HttpProtobuf, typeof(OtlpHttpExportClient), false, 10000, "in_memory")] + [InlineData(OtlpExportProtocol.HttpProtobuf, typeof(OtlpHttpExportClient), true, 8000, "in_memory")] + [InlineData(OtlpExportProtocol.Grpc, typeof(OtlpGrpcExportClient), false, 10000, "disk")] + [InlineData(OtlpExportProtocol.HttpProtobuf, typeof(OtlpHttpExportClient), false, 10000, "disk")] + [InlineData(OtlpExportProtocol.HttpProtobuf, typeof(OtlpHttpExportClient), true, 8000, "disk")] public void GetTransmissionHandler_InitializesCorrectHandlerExportClientAndTimeoutValue(OtlpExportProtocol protocol, Type exportClientType, bool customHttpClient, int expectedTimeoutMilliseconds, string? retryStrategy) { var exporterOptions = new OtlpExporterOptions() { Protocol = protocol }; @@ -173,59 +99,23 @@ public void GetTransmissionHandler_InitializesCorrectHandlerExportClientAndTimeo .AddInMemoryCollection(new Dictionary { [ExperimentalOptions.OtlpRetryEnvVar] = retryStrategy }) .Build(); - if (exportClientType == typeof(ProtobufOtlpGrpcExportClient) || exportClientType == typeof(ProtobufOtlpHttpExportClient)) - { - var transmissionHandler = exporterOptions.GetProtobufExportTransmissionHandler(new ExperimentalOptions(configuration), OtlpSignalType.Traces); - - AssertTransmissionHandler(transmissionHandler, exportClientType, expectedTimeoutMilliseconds, retryStrategy); - } - else if (exportClientType == typeof(OtlpGrpcMetricsExportClient) || exportClientType == typeof(OtlpHttpMetricsExportClient)) - { - var transmissionHandler = exporterOptions.GetMetricsExportTransmissionHandler(new ExperimentalOptions(configuration)); - - AssertTransmissionHandler(transmissionHandler, exportClientType, expectedTimeoutMilliseconds, retryStrategy); - } - else - { - var transmissionHandler = exporterOptions.GetLogsExportTransmissionHandler(new ExperimentalOptions(configuration)); - - AssertTransmissionHandler(transmissionHandler, exportClientType, expectedTimeoutMilliseconds, retryStrategy); - } - } - - private static void AssertTransmissionHandler(OtlpExporterTransmissionHandler transmissionHandler, Type exportClientType, int expectedTimeoutMilliseconds, string? retryStrategy) - { - if (retryStrategy == "in_memory") - { - Assert.True(transmissionHandler is OtlpExporterRetryTransmissionHandler); - } - else if (retryStrategy == "disk") - { - Assert.True(transmissionHandler is OtlpExporterPersistentStorageTransmissionHandler); - } - else - { - Assert.True(transmissionHandler is OtlpExporterTransmissionHandler); - } - - Assert.Equal(exportClientType, transmissionHandler.ExportClient.GetType()); - - Assert.Equal(expectedTimeoutMilliseconds, transmissionHandler.TimeoutMilliseconds); + var transmissionHandler = exporterOptions.GetProtobufExportTransmissionHandler(new ExperimentalOptions(configuration), OtlpSignalType.Traces); + AssertTransmissionHandler(transmissionHandler, exportClientType, expectedTimeoutMilliseconds, retryStrategy); } - private static void AssertTransmissionHandler(ProtobufOtlpExporterTransmissionHandler transmissionHandler, Type exportClientType, int expectedTimeoutMilliseconds, string? retryStrategy) + private static void AssertTransmissionHandler(OtlpExporterTransmissionHandler transmissionHandler, Type exportClientType, int expectedTimeoutMilliseconds, string? retryStrategy) { if (retryStrategy == "in_memory") { - Assert.True(transmissionHandler is ProtobufOtlpExporterRetryTransmissionHandler); + Assert.True(transmissionHandler is OtlpExporterRetryTransmissionHandler); } else if (retryStrategy == "disk") { - Assert.True(transmissionHandler is ProtobufOtlpExporterPersistentStorageTransmissionHandler); + Assert.True(transmissionHandler is OtlpExporterPersistentStorageTransmissionHandler); } else { - Assert.True(transmissionHandler is ProtobufOtlpExporterTransmissionHandler); + Assert.True(transmissionHandler is OtlpExporterTransmissionHandler); } Assert.Equal(exportClientType, transmissionHandler.ExportClient.GetType()); diff --git a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/BaseOtlpHttpExportClientTests.cs b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpHttpExportClientTests.cs similarity index 73% rename from test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/BaseOtlpHttpExportClientTests.cs rename to test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpHttpExportClientTests.cs index 36d534df9a1..c21dbd8c8a0 100644 --- a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/BaseOtlpHttpExportClientTests.cs +++ b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpHttpExportClientTests.cs @@ -9,7 +9,7 @@ namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests; -public class BaseOtlpHttpExportClientTests +public class OtlpHttpExportClientTests { [Theory] [InlineData(null, null, "http://localhost:4318/signal/path")] @@ -30,7 +30,7 @@ public void ValidateOtlpHttpExportClientEndpoint(string? optionEndpoint, string? options.Endpoint = new Uri(optionEndpoint); } - var exporterClient = new TestOtlpHttpExportClient(options, new HttpClient()); + var exporterClient = new OtlpHttpExportClient(options, new HttpClient(), "signal/path"); Assert.Equal(new Uri(expectedExporterEndpoint), exporterClient.Endpoint); } finally @@ -38,17 +38,4 @@ public void ValidateOtlpHttpExportClientEndpoint(string? optionEndpoint, string? Environment.SetEnvironmentVariable(OtlpSpecConfigDefinitions.DefaultEndpointEnvVarName, null); } } - - internal class TestOtlpHttpExportClient : BaseOtlpHttpExportClient - { - public TestOtlpHttpExportClient(OtlpExporterOptions options, HttpClient httpClient) - : base(options, httpClient, "signal/path") - { - } - - protected override HttpContent CreateHttpContent(string exportRequest) - { - throw new NotImplementedException(); - } - } } diff --git a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpLogExporterTests.cs b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpLogExporterTests.cs index 032e88252e2..95acb36da9b 100644 --- a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpLogExporterTests.cs +++ b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpLogExporterTests.cs @@ -12,6 +12,7 @@ using OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.Transmission; using OpenTelemetry.Internal; using OpenTelemetry.Logs; +using OpenTelemetry.Proto.Trace.V1; using OpenTelemetry.Resources; using OpenTelemetry.Tests; using OpenTelemetry.Trace; @@ -244,10 +245,8 @@ public void AddOtlpLogExporterParseStateValueCanBeTurnedOffHosting(bool parseSta #pragma warning restore CS0618 // Type or member is obsolete } - [Theory] - [InlineData(true)] - [InlineData(false)] - public void OtlpLogRecordTestWhenStateValuesArePopulated(bool useCustomSerializer) + [Fact] + public void OtlpLogRecordTestWhenStateValuesArePopulated() { var logRecords = new List(); using var loggerFactory = LoggerFactory.Create(builder => @@ -266,19 +265,8 @@ public void OtlpLogRecordTestWhenStateValuesArePopulated(bool useCustomSerialize Assert.Single(logRecords); - var otlpLogRecordTransformer = new OtlpLogRecordTransformer(DefaultSdkLimitOptions, new()); - var logRecord = logRecords[0]; - OtlpLogs.LogRecord? otlpLogRecord; - - if (useCustomSerializer) - { - otlpLogRecord = ToOtlpLogs(DefaultSdkLimitOptions, new ExperimentalOptions(), logRecord); - } - else - { - otlpLogRecord = otlpLogRecordTransformer.ToOtlpLog(logRecord); - } + OtlpLogs.LogRecord? otlpLogRecord = ToOtlpLogs(DefaultSdkLimitOptions, new ExperimentalOptions(), logRecord); Assert.NotNull(otlpLogRecord); Assert.Equal("Hello from tomato 2.99.", otlpLogRecord.Body.StringValue); @@ -299,13 +287,10 @@ public void OtlpLogRecordTestWhenStateValuesArePopulated(bool useCustomSerialize } [Theory] - [InlineData("true", true)] - [InlineData("false", true)] - [InlineData(null, true)] - [InlineData("true", false)] - [InlineData("false", false)] - [InlineData(null, false)] - public void CheckToOtlpLogRecordEventId(string? emitLogEventAttributes, bool useCustomSerializer) + [InlineData("true")] + [InlineData("false")] + [InlineData(null)] + public void CheckToOtlpLogRecordEventId(string? emitLogEventAttributes) { var logRecords = new List(); using var loggerFactory = LoggerFactory.Create(builder => @@ -327,20 +312,8 @@ public void CheckToOtlpLogRecordEventId(string? emitLogEventAttributes, bool use .AddInMemoryCollection(new Dictionary { [ExperimentalOptions.EmitLogEventEnvVar] = emitLogEventAttributes }) .Build(); - var otlpLogRecordTransformer = new OtlpLogRecordTransformer(DefaultSdkLimitOptions, new(configuration)); - var logRecord = logRecords[0]; - - OtlpLogs.LogRecord? otlpLogRecord; - - if (useCustomSerializer) - { - otlpLogRecord = ToOtlpLogs(DefaultSdkLimitOptions, new(configuration), logRecord); - } - else - { - otlpLogRecord = otlpLogRecordTransformer.ToOtlpLog(logRecord); - } + OtlpLogs.LogRecord? otlpLogRecord = otlpLogRecord = ToOtlpLogs(DefaultSdkLimitOptions, new(configuration), logRecord); Assert.NotNull(otlpLogRecord); Assert.Equal("Hello from tomato 2.99.", otlpLogRecord.Body.StringValue); @@ -364,14 +337,7 @@ public void CheckToOtlpLogRecordEventId(string? emitLogEventAttributes, bool use logRecord = logRecords[0]; - if (useCustomSerializer) - { - otlpLogRecord = ToOtlpLogs(DefaultSdkLimitOptions, new(configuration), logRecord); - } - else - { - otlpLogRecord = otlpLogRecordTransformer.ToOtlpLog(logRecord); - } + otlpLogRecord = ToOtlpLogs(DefaultSdkLimitOptions, new(configuration), logRecord); Assert.NotNull(otlpLogRecord); Assert.Equal("Hello from tomato 2.99.", otlpLogRecord.Body.StringValue); @@ -392,10 +358,8 @@ public void CheckToOtlpLogRecordEventId(string? emitLogEventAttributes, bool use } } - [Theory] - [InlineData(true)] - [InlineData(false)] - public void CheckToOtlpLogRecordTimestamps(bool useCustomSerializer) + [Fact] + public void CheckToOtlpLogRecordTimestamps() { var logRecords = new List(); using var loggerFactory = LoggerFactory.Create(builder => @@ -405,29 +369,17 @@ public void CheckToOtlpLogRecordTimestamps(bool useCustomSerializer) var logger = loggerFactory.CreateLogger("OtlpLogExporterTests"); logger.LogInformation("Log message"); - var otlpLogRecordTransformer = new OtlpLogRecordTransformer(DefaultSdkLimitOptions, new()); var logRecord = logRecords[0]; - OtlpLogs.LogRecord? otlpLogRecord; - - if (useCustomSerializer) - { - otlpLogRecord = ToOtlpLogs(DefaultSdkLimitOptions, new ExperimentalOptions(), logRecord); - } - else - { - otlpLogRecord = otlpLogRecordTransformer.ToOtlpLog(logRecord); - } + OtlpLogs.LogRecord? otlpLogRecord = ToOtlpLogs(DefaultSdkLimitOptions, new ExperimentalOptions(), logRecord); Assert.NotNull(otlpLogRecord); Assert.True(otlpLogRecord.TimeUnixNano > 0); Assert.True(otlpLogRecord.ObservedTimeUnixNano > 0); } - [Theory] - [InlineData(true)] - [InlineData(false)] - public void CheckToOtlpLogRecordTraceIdSpanIdFlagWithNoActivity(bool useCustomSerializer) + [Fact] + public void CheckToOtlpLogRecordTraceIdSpanIdFlagWithNoActivity() { var logRecords = new List(); using var loggerFactory = LoggerFactory.Create(builder => @@ -438,19 +390,8 @@ public void CheckToOtlpLogRecordTraceIdSpanIdFlagWithNoActivity(bool useCustomSe var logger = loggerFactory.CreateLogger("OtlpLogExporterTests"); logger.LogInformation("Log when there is no activity."); - var otlpLogRecordTransformer = new OtlpLogRecordTransformer(DefaultSdkLimitOptions, new()); - var logRecord = logRecords[0]; - OtlpLogs.LogRecord? otlpLogRecord; - - if (useCustomSerializer) - { - otlpLogRecord = ToOtlpLogs(DefaultSdkLimitOptions, new ExperimentalOptions(), logRecord); - } - else - { - otlpLogRecord = otlpLogRecordTransformer.ToOtlpLog(logRecord); - } + OtlpLogs.LogRecord? otlpLogRecord = ToOtlpLogs(DefaultSdkLimitOptions, new ExperimentalOptions(), logRecord); Assert.Null(Activity.Current); Assert.NotNull(otlpLogRecord); @@ -459,10 +400,8 @@ public void CheckToOtlpLogRecordTraceIdSpanIdFlagWithNoActivity(bool useCustomSe Assert.Equal(0u, otlpLogRecord.Flags); } - [Theory] - [InlineData(true)] - [InlineData(false)] - public void CheckToOtlpLogRecordSpanIdTraceIdAndFlag(bool useCustomSerializer) + [Fact] + public void CheckToOtlpLogRecordSpanIdTraceIdAndFlag() { var logRecords = new List(); using var loggerFactory = LoggerFactory.Create(builder => @@ -482,20 +421,9 @@ public void CheckToOtlpLogRecordSpanIdTraceIdAndFlag(bool useCustomSerializer) expectedSpanId = activity.SpanId; } - var otlpLogRecordTransformer = new OtlpLogRecordTransformer(DefaultSdkLimitOptions, new()); - var logRecord = logRecords[0]; - OtlpLogs.LogRecord? otlpLogRecord; - - if (useCustomSerializer) - { - otlpLogRecord = ToOtlpLogs(DefaultSdkLimitOptions, new ExperimentalOptions(), logRecord); - } - else - { - otlpLogRecord = otlpLogRecordTransformer.ToOtlpLog(logRecord); - } + OtlpLogs.LogRecord? otlpLogRecord = ToOtlpLogs(DefaultSdkLimitOptions, new ExperimentalOptions(), logRecord); Assert.NotNull(otlpLogRecord); Assert.Equal(expectedTraceId.ToString(), ActivityTraceId.CreateFromBytes(otlpLogRecord.TraceId.ToByteArray()).ToString()); @@ -504,19 +432,13 @@ public void CheckToOtlpLogRecordSpanIdTraceIdAndFlag(bool useCustomSerializer) } [Theory] - [InlineData(LogLevel.Trace, true)] - [InlineData(LogLevel.Debug, true)] - [InlineData(LogLevel.Information, true)] - [InlineData(LogLevel.Warning, true)] - [InlineData(LogLevel.Error, true)] - [InlineData(LogLevel.Critical, true)] - [InlineData(LogLevel.Trace, false)] - [InlineData(LogLevel.Debug, false)] - [InlineData(LogLevel.Information, false)] - [InlineData(LogLevel.Warning, false)] - [InlineData(LogLevel.Error, false)] - [InlineData(LogLevel.Critical, false)] - public void CheckToOtlpLogRecordSeverityLevelAndText(LogLevel logLevel, bool useCustomSerializer) + [InlineData(LogLevel.Trace)] + [InlineData(LogLevel.Debug)] + [InlineData(LogLevel.Information)] + [InlineData(LogLevel.Warning)] + [InlineData(LogLevel.Error)] + [InlineData(LogLevel.Critical)] + public void CheckToOtlpLogRecordSeverityLevelAndText(LogLevel logLevel) { var logRecords = new List(); using var loggerFactory = LoggerFactory.Create(builder => @@ -532,19 +454,8 @@ public void CheckToOtlpLogRecordSeverityLevelAndText(LogLevel logLevel, bool use logger.Log(logLevel, "Hello from {name} {price}.", "tomato", 2.99); Assert.Single(logRecords); - var otlpLogRecordTransformer = new OtlpLogRecordTransformer(DefaultSdkLimitOptions, new()); - var logRecord = logRecords[0]; - OtlpLogs.LogRecord? otlpLogRecord; - - if (useCustomSerializer) - { - otlpLogRecord = ToOtlpLogs(DefaultSdkLimitOptions, new ExperimentalOptions(), logRecord); - } - else - { - otlpLogRecord = otlpLogRecordTransformer.ToOtlpLog(logRecord); - } + OtlpLogs.LogRecord? otlpLogRecord = ToOtlpLogs(DefaultSdkLimitOptions, new ExperimentalOptions(), logRecord); Assert.NotNull(otlpLogRecord); #pragma warning disable CS0618 // Type or member is obsolete @@ -576,11 +487,9 @@ public void CheckToOtlpLogRecordSeverityLevelAndText(LogLevel logLevel, bool use } [Theory] - [InlineData(true, true)] - [InlineData(false, true)] - [InlineData(true, false)] - [InlineData(false, false)] - public void CheckToOtlpLogRecordBodyIsPopulated(bool includeFormattedMessage, bool useCustomSerializer) + [InlineData(true)] + [InlineData(false)] + public void CheckToOtlpLogRecordBodyIsPopulated(bool includeFormattedMessage) { var logRecords = new List(); using var loggerFactory = LoggerFactory.Create(builder => @@ -600,19 +509,8 @@ public void CheckToOtlpLogRecordBodyIsPopulated(bool includeFormattedMessage, bo logger.LogInformation("OpenTelemetry {Greeting} {Subject}!", "Hello", "World"); Assert.Single(logRecords); - var otlpLogRecordTransformer = new OtlpLogRecordTransformer(DefaultSdkLimitOptions, new()); - var logRecord = logRecords[0]; - OtlpLogs.LogRecord? otlpLogRecord; - - if (useCustomSerializer) - { - otlpLogRecord = ToOtlpLogs(DefaultSdkLimitOptions, new ExperimentalOptions(), logRecord); - } - else - { - otlpLogRecord = otlpLogRecordTransformer.ToOtlpLog(logRecord); - } + OtlpLogs.LogRecord? otlpLogRecord = ToOtlpLogs(DefaultSdkLimitOptions, new ExperimentalOptions(), logRecord); Assert.NotNull(otlpLogRecord); if (includeFormattedMessage) @@ -631,15 +529,7 @@ public void CheckToOtlpLogRecordBodyIsPopulated(bool includeFormattedMessage, bo Assert.Single(logRecords); logRecord = logRecords[0]; - - if (useCustomSerializer) - { - otlpLogRecord = ToOtlpLogs(DefaultSdkLimitOptions, new ExperimentalOptions(), logRecord); - } - else - { - otlpLogRecord = otlpLogRecordTransformer.ToOtlpLog(logRecord); - } + otlpLogRecord = ToOtlpLogs(DefaultSdkLimitOptions, new ExperimentalOptions(), logRecord); Assert.NotNull(otlpLogRecord); @@ -655,15 +545,7 @@ public void CheckToOtlpLogRecordBodyIsPopulated(bool includeFormattedMessage, bo Assert.Single(logRecords); logRecord = logRecords[0]; - - if (useCustomSerializer) - { - otlpLogRecord = ToOtlpLogs(DefaultSdkLimitOptions, new ExperimentalOptions(), logRecord); - } - else - { - otlpLogRecord = otlpLogRecordTransformer.ToOtlpLog(logRecord); - } + otlpLogRecord = ToOtlpLogs(DefaultSdkLimitOptions, new ExperimentalOptions(), logRecord); Assert.NotNull(otlpLogRecord); @@ -699,9 +581,7 @@ public void LogRecordBodyIsExportedWhenUsingBridgeApi(bool isBodySet) Assert.Equal(2, logRecords.Count); - var otlpLogRecordTransformer = new OtlpLogRecordTransformer(DefaultSdkLimitOptions, new()); - - var otlpLogRecord = otlpLogRecordTransformer.ToOtlpLog(logRecords[0]); + var otlpLogRecord = ToOtlpLogs(DefaultSdkLimitOptions, new ExperimentalOptions(), logRecords[0]); Assert.NotNull(otlpLogRecord); if (isBodySet) @@ -713,7 +593,7 @@ public void LogRecordBodyIsExportedWhenUsingBridgeApi(bool isBodySet) Assert.Null(otlpLogRecord.Body); } - otlpLogRecord = otlpLogRecordTransformer.ToOtlpLog(logRecords[1]); + otlpLogRecord = ToOtlpLogs(DefaultSdkLimitOptions, new ExperimentalOptions(), logRecords[1]); Assert.NotNull(otlpLogRecord); Assert.Equal(2, otlpLogRecord.Attributes.Count); @@ -730,10 +610,8 @@ public void LogRecordBodyIsExportedWhenUsingBridgeApi(bool isBodySet) Assert.Equal("Hello from {name} {price}.", otlpLogRecord.Body.StringValue); } - [Theory] - [InlineData(true)] - [InlineData(false)] - public void CheckToOtlpLogRecordExceptionAttributes(bool useCustomSerializer) + [Fact] + public void CheckToOtlpLogRecordExceptionAttributes() { var logRecords = new List(); using var loggerFactory = LoggerFactory.Create(builder => @@ -747,18 +625,7 @@ public void CheckToOtlpLogRecordExceptionAttributes(bool useCustomSerializer) var logRecord = logRecords[0]; var loggedException = logRecord.Exception; - var otlpLogRecordTransformer = new OtlpLogRecordTransformer(DefaultSdkLimitOptions, new()); - - OtlpLogs.LogRecord? otlpLogRecord; - - if (useCustomSerializer) - { - otlpLogRecord = ToOtlpLogs(DefaultSdkLimitOptions, new ExperimentalOptions(), logRecord); - } - else - { - otlpLogRecord = otlpLogRecordTransformer.ToOtlpLog(logRecord); - } + OtlpLogs.LogRecord? otlpLogRecord = ToOtlpLogs(DefaultSdkLimitOptions, new ExperimentalOptions(), logRecord); Assert.NotNull(otlpLogRecord); var otlpLogRecordAttributes = otlpLogRecord.Attributes.ToString(); @@ -774,10 +641,8 @@ public void CheckToOtlpLogRecordExceptionAttributes(bool useCustomSerializer) Assert.Contains(logRecord.Exception.ToInvariantString(), otlpLogRecordAttributes); } - [Theory] - [InlineData(true)] - [InlineData(false)] - public void CheckToOtlpLogRecordRespectsAttributeLimits(bool useCustomSerializer) + [Fact] + public void CheckToOtlpLogRecordRespectsAttributeLimits() { var sdkLimitOptions = new SdkLimitOptions { @@ -796,19 +661,8 @@ public void CheckToOtlpLogRecordRespectsAttributeLimits(bool useCustomSerializer var logger = loggerFactory.CreateLogger(string.Empty); logger.LogInformation("OpenTelemetry {AttributeOne} {AttributeTwo} {AttributeThree}!", "I'm an attribute", "I too am an attribute", "I get dropped :("); - var otlpLogRecordTransformer = new OtlpLogRecordTransformer(sdkLimitOptions, new()); - var logRecord = logRecords[0]; - OtlpLogs.LogRecord? otlpLogRecord; - - if (useCustomSerializer) - { - otlpLogRecord = ToOtlpLogs(sdkLimitOptions, new(), logRecord); - } - else - { - otlpLogRecord = otlpLogRecordTransformer.ToOtlpLog(logRecord); - } + OtlpLogs.LogRecord? otlpLogRecord = ToOtlpLogs(sdkLimitOptions, new(), logRecord); Assert.NotNull(otlpLogRecord); Assert.Equal(1u, otlpLogRecord.DroppedAttributesCount); @@ -832,9 +686,9 @@ public void CheckToOtlpLogRecordRespectsAttributeLimits(bool useCustomSerializer public void Export_WhenExportClientIsProvidedInCtor_UsesProvidedExportClient() { // Arrange. - var testExportClient = new TestExportClient(); + var testExportClient = new TestExportClient(); var exporterOptions = new OtlpExporterOptions(); - var transmissionHandler = new OtlpExporterTransmissionHandler(testExportClient, exporterOptions.TimeoutMilliseconds); + var transmissionHandler = new OtlpExporterTransmissionHandler(testExportClient, exporterOptions.TimeoutMilliseconds); var emptyLogRecords = Array.Empty(); var emptyBatch = new Batch(emptyLogRecords, emptyLogRecords.Length); var sut = new OtlpLogExporter( @@ -854,9 +708,9 @@ public void Export_WhenExportClientIsProvidedInCtor_UsesProvidedExportClient() public void Export_WhenExportClientThrowsException_ReturnsExportResultFailure() { // Arrange. - var testExportClient = new TestExportClient(throwException: true); + var testExportClient = new TestExportClient(throwException: true); var exporterOptions = new OtlpExporterOptions(); - var transmissionHandler = new OtlpExporterTransmissionHandler(testExportClient, exporterOptions.TimeoutMilliseconds); + var transmissionHandler = new OtlpExporterTransmissionHandler(testExportClient, exporterOptions.TimeoutMilliseconds); var emptyLogRecords = Array.Empty(); var emptyBatch = new Batch(emptyLogRecords, emptyLogRecords.Length); var sut = new OtlpLogExporter( @@ -876,9 +730,9 @@ public void Export_WhenExportClientThrowsException_ReturnsExportResultFailure() public void Export_WhenExportIsSuccessful_ReturnsExportResultSuccess() { // Arrange. - var testExportClient = new TestExportClient(); + var testExportClient = new TestExportClient(); var exporterOptions = new OtlpExporterOptions(); - var transmissionHandler = new OtlpExporterTransmissionHandler(testExportClient, exporterOptions.TimeoutMilliseconds); + var transmissionHandler = new OtlpExporterTransmissionHandler(testExportClient, exporterOptions.TimeoutMilliseconds); var emptyLogRecords = Array.Empty(); var emptyBatch = new Batch(emptyLogRecords, emptyLogRecords.Length); var sut = new OtlpLogExporter( @@ -894,10 +748,8 @@ public void Export_WhenExportIsSuccessful_ReturnsExportResultSuccess() Assert.Equal(ExportResult.Success, result); } - [Theory] - [InlineData(true)] - [InlineData(false)] - public void ToOtlpLog_WhenOptionsIncludeScopesIsFalse_DoesNotContainScopeAttribute(bool useCustomSerializer) + [Fact] + public void ToOtlpLog_WhenOptionsIncludeScopesIsFalse_DoesNotContainScopeAttribute() { // Arrange. var logRecords = new List(1); @@ -923,17 +775,7 @@ public void ToOtlpLog_WhenOptionsIncludeScopesIsFalse_DoesNotContainScopeAttribu // Assert. var logRecord = logRecords.Single(); - var otlpLogRecordTransformer = new OtlpLogRecordTransformer(DefaultSdkLimitOptions, new()); - OtlpLogs.LogRecord? otlpLogRecord; - - if (useCustomSerializer) - { - otlpLogRecord = ToOtlpLogs(DefaultSdkLimitOptions, new ExperimentalOptions(), logRecord); - } - else - { - otlpLogRecord = otlpLogRecordTransformer.ToOtlpLog(logRecord); - } + OtlpLogs.LogRecord? otlpLogRecord = ToOtlpLogs(DefaultSdkLimitOptions, new ExperimentalOptions(), logRecord); Assert.NotNull(otlpLogRecord); var actualScope = TryGetAttribute(otlpLogRecord, expectedScopeKey); @@ -941,11 +783,9 @@ public void ToOtlpLog_WhenOptionsIncludeScopesIsFalse_DoesNotContainScopeAttribu } [Theory] - [InlineData("Some scope value", false)] - [InlineData('a', false)] - [InlineData("Some scope value", true)] - [InlineData('a', true)] - public void ToOtlpLog_WhenOptionsIncludeScopesIsTrue_ContainsScopeAttributeStringValue(object scopeValue, bool useCustomSerializer) + [InlineData("Some scope value")] + [InlineData('a')] + public void ToOtlpLog_WhenOptionsIncludeScopesIsTrue_ContainsScopeAttributeStringValue(object scopeValue) { // Arrange. var logRecords = new List(1); @@ -970,11 +810,7 @@ public void ToOtlpLog_WhenOptionsIncludeScopesIsTrue_ContainsScopeAttributeStrin // Assert. var logRecord = logRecords.Single(); - var otlpLogRecordTransformer = new OtlpLogRecordTransformer(DefaultSdkLimitOptions, new()); - - var otlpLogRecord = useCustomSerializer - ? ToOtlpLogs(DefaultSdkLimitOptions, new ExperimentalOptions(), logRecord) - : otlpLogRecordTransformer.ToOtlpLog(logRecord); + var otlpLogRecord = ToOtlpLogs(DefaultSdkLimitOptions, new ExperimentalOptions(), logRecord); Assert.NotNull(otlpLogRecord); Assert.Single(otlpLogRecord.Attributes); @@ -986,11 +822,9 @@ public void ToOtlpLog_WhenOptionsIncludeScopesIsTrue_ContainsScopeAttributeStrin } [Theory] - [InlineData(true, false)] - [InlineData(false, false)] - [InlineData(true, true)] - [InlineData(false, true)] - public void ToOtlpLog_WhenOptionsIncludeScopesIsTrue_ContainsScopeAttributeBoolValue(bool scopeValue, bool useCustomSerializer) + [InlineData(true)] + [InlineData(false)] + public void ToOtlpLog_WhenOptionsIncludeScopesIsTrue_ContainsScopeAttributeBoolValue(bool scopeValue) { // Arrange. var logRecords = new List(1); @@ -1015,11 +849,7 @@ public void ToOtlpLog_WhenOptionsIncludeScopesIsTrue_ContainsScopeAttributeBoolV // Assert. var logRecord = logRecords.Single(); - var otlpLogRecordTransformer = new OtlpLogRecordTransformer(DefaultSdkLimitOptions, new()); - - var otlpLogRecord = useCustomSerializer - ? ToOtlpLogs(DefaultSdkLimitOptions, new ExperimentalOptions(), logRecord) - : otlpLogRecordTransformer.ToOtlpLog(logRecord); + var otlpLogRecord = ToOtlpLogs(DefaultSdkLimitOptions, new ExperimentalOptions(), logRecord); Assert.NotNull(otlpLogRecord); Assert.Single(otlpLogRecord.Attributes); @@ -1031,35 +861,21 @@ public void ToOtlpLog_WhenOptionsIncludeScopesIsTrue_ContainsScopeAttributeBoolV } [Theory] - [InlineData(byte.MinValue, false)] - [InlineData(byte.MaxValue, false)] - [InlineData(sbyte.MinValue, false)] - [InlineData(sbyte.MaxValue, false)] - [InlineData(short.MinValue, false)] - [InlineData(short.MaxValue, false)] - [InlineData(ushort.MinValue, false)] - [InlineData(ushort.MaxValue, false)] - [InlineData(int.MinValue, false)] - [InlineData(int.MaxValue, false)] - [InlineData(uint.MinValue, false)] - [InlineData(uint.MaxValue, false)] - [InlineData(long.MinValue, false)] - [InlineData(long.MaxValue, false)] - [InlineData(byte.MinValue, true)] - [InlineData(byte.MaxValue, true)] - [InlineData(sbyte.MinValue, true)] - [InlineData(sbyte.MaxValue, true)] - [InlineData(short.MinValue, true)] - [InlineData(short.MaxValue, true)] - [InlineData(ushort.MinValue, true)] - [InlineData(ushort.MaxValue, true)] - [InlineData(int.MinValue, true)] - [InlineData(int.MaxValue, true)] - [InlineData(uint.MinValue, true)] - [InlineData(uint.MaxValue, true)] - [InlineData(long.MinValue, true)] - [InlineData(long.MaxValue, true)] - public void ToOtlpLog_WhenOptionsIncludeScopesIsTrue_ContainsScopeAttributeIntValue(object scopeValue, bool useCustomSerializer) + [InlineData(byte.MinValue)] + [InlineData(byte.MaxValue)] + [InlineData(sbyte.MinValue)] + [InlineData(sbyte.MaxValue)] + [InlineData(short.MinValue)] + [InlineData(short.MaxValue)] + [InlineData(ushort.MinValue)] + [InlineData(ushort.MaxValue)] + [InlineData(int.MinValue)] + [InlineData(int.MaxValue)] + [InlineData(uint.MinValue)] + [InlineData(uint.MaxValue)] + [InlineData(long.MinValue)] + [InlineData(long.MaxValue)] + public void ToOtlpLog_WhenOptionsIncludeScopesIsTrue_ContainsScopeAttributeIntValue(object scopeValue) { // Arrange. var logRecords = new List(1); @@ -1084,11 +900,7 @@ public void ToOtlpLog_WhenOptionsIncludeScopesIsTrue_ContainsScopeAttributeIntVa // Assert. var logRecord = logRecords.Single(); - var otlpLogRecordTransformer = new OtlpLogRecordTransformer(DefaultSdkLimitOptions, new()); - - var otlpLogRecord = useCustomSerializer - ? ToOtlpLogs(DefaultSdkLimitOptions, new ExperimentalOptions(), logRecord) - : otlpLogRecordTransformer.ToOtlpLog(logRecord); + var otlpLogRecord = ToOtlpLogs(DefaultSdkLimitOptions, new ExperimentalOptions(), logRecord); Assert.NotNull(otlpLogRecord); Assert.Single(otlpLogRecord.Attributes); @@ -1100,11 +912,9 @@ public void ToOtlpLog_WhenOptionsIncludeScopesIsTrue_ContainsScopeAttributeIntVa } [Theory] - [InlineData(float.MinValue, false)] - [InlineData(float.MaxValue, false)] - [InlineData(float.MinValue, true)] - [InlineData(float.MaxValue, true)] - public void ToOtlpLog_WhenOptionsIncludeScopesIsTrue_ContainsScopeAttributeDoubleValueForFloat(float scopeValue, bool useCustomSerializer) + [InlineData(float.MinValue)] + [InlineData(float.MaxValue)] + public void ToOtlpLog_WhenOptionsIncludeScopesIsTrue_ContainsScopeAttributeDoubleValueForFloat(float scopeValue) { // Arrange. var logRecords = new List(1); @@ -1129,11 +939,8 @@ public void ToOtlpLog_WhenOptionsIncludeScopesIsTrue_ContainsScopeAttributeDoubl // Assert. var logRecord = logRecords.Single(); - var otlpLogRecordTransformer = new OtlpLogRecordTransformer(DefaultSdkLimitOptions, new()); - var otlpLogRecord = useCustomSerializer - ? ToOtlpLogs(DefaultSdkLimitOptions, new ExperimentalOptions(), logRecord) - : otlpLogRecordTransformer.ToOtlpLog(logRecord); + var otlpLogRecord = ToOtlpLogs(DefaultSdkLimitOptions, new ExperimentalOptions(), logRecord); Assert.NotNull(otlpLogRecord); Assert.Single(otlpLogRecord.Attributes); @@ -1145,11 +952,9 @@ public void ToOtlpLog_WhenOptionsIncludeScopesIsTrue_ContainsScopeAttributeDoubl } [Theory] - [InlineData(double.MinValue, false)] - [InlineData(double.MaxValue, false)] - [InlineData(double.MinValue, true)] - [InlineData(double.MaxValue, true)] - public void ToOtlpLog_WhenOptionsIncludeScopesIsTrue_ContainsScopeAttributeDoubleValueForDouble(double scopeValue, bool useCustomSerializer) + [InlineData(double.MinValue)] + [InlineData(double.MaxValue)] + public void ToOtlpLog_WhenOptionsIncludeScopesIsTrue_ContainsScopeAttributeDoubleValueForDouble(double scopeValue) { // Arrange. var logRecords = new List(1); @@ -1174,11 +979,7 @@ public void ToOtlpLog_WhenOptionsIncludeScopesIsTrue_ContainsScopeAttributeDoubl // Assert. var logRecord = logRecords.Single(); - var otlpLogRecordTransformer = new OtlpLogRecordTransformer(DefaultSdkLimitOptions, new()); - - var otlpLogRecord = useCustomSerializer - ? ToOtlpLogs(DefaultSdkLimitOptions, new ExperimentalOptions(), logRecord) - : otlpLogRecordTransformer.ToOtlpLog(logRecord); + var otlpLogRecord = ToOtlpLogs(DefaultSdkLimitOptions, new ExperimentalOptions(), logRecord); Assert.NotNull(otlpLogRecord); Assert.Single(otlpLogRecord.Attributes); @@ -1188,10 +989,8 @@ public void ToOtlpLog_WhenOptionsIncludeScopesIsTrue_ContainsScopeAttributeDoubl Assert.Equal(scopeValue.ToString(), actualScope.Value.DoubleValue.ToString()); } - [Theory] - [InlineData(false)] - [InlineData(true)] - public void ToOtlpLog_WhenOptionsIncludeScopesIsTrue_AndScopeStateIsOfTypeString_ScopeIsIgnored(bool useCustomSerializer) + [Fact] + public void ToOtlpLog_WhenOptionsIncludeScopesIsTrue_AndScopeStateIsOfTypeString_ScopeIsIgnored() { // Arrange. var logRecords = new List(1); @@ -1213,28 +1012,19 @@ public void ToOtlpLog_WhenOptionsIncludeScopesIsTrue_AndScopeStateIsOfTypeString // Assert. var logRecord = logRecords.Single(); - var otlpLogRecordTransformer = new OtlpLogRecordTransformer(DefaultSdkLimitOptions, new()); - - var otlpLogRecord = useCustomSerializer - ? ToOtlpLogs(DefaultSdkLimitOptions, new ExperimentalOptions(), logRecord) - : otlpLogRecordTransformer.ToOtlpLog(logRecord); + var otlpLogRecord = ToOtlpLogs(DefaultSdkLimitOptions, new ExperimentalOptions(), logRecord); Assert.NotNull(otlpLogRecord); Assert.Empty(otlpLogRecord.Attributes); } [Theory] - [InlineData(typeof(int), false)] - [InlineData(typeof(float), false)] - [InlineData(typeof(decimal), false)] - [InlineData(typeof(char), false)] - [InlineData(typeof(bool), false)] - [InlineData(typeof(int), true)] - [InlineData(typeof(float), true)] - [InlineData(typeof(decimal), true)] - [InlineData(typeof(char), true)] - [InlineData(typeof(bool), true)] - public void ToOtlpLog_WhenOptionsIncludeScopesIsTrue_AndScopeStateIsOfPrimitiveTypes_ScopeIsIgnored(Type typeOfScopeState, bool useCustomSerializer) + [InlineData(typeof(int))] + [InlineData(typeof(float))] + [InlineData(typeof(decimal))] + [InlineData(typeof(char))] + [InlineData(typeof(bool))] + public void ToOtlpLog_WhenOptionsIncludeScopesIsTrue_AndScopeStateIsOfPrimitiveTypes_ScopeIsIgnored(Type typeOfScopeState) { // Arrange. var logRecords = new List(1); @@ -1257,20 +1047,14 @@ public void ToOtlpLog_WhenOptionsIncludeScopesIsTrue_AndScopeStateIsOfPrimitiveT // Assert. var logRecord = logRecords.Single(); - var otlpLogRecordTransformer = new OtlpLogRecordTransformer(DefaultSdkLimitOptions, new()); - - var otlpLogRecord = useCustomSerializer - ? ToOtlpLogs(DefaultSdkLimitOptions, new ExperimentalOptions(), logRecord) - : otlpLogRecordTransformer.ToOtlpLog(logRecord); + var otlpLogRecord = ToOtlpLogs(DefaultSdkLimitOptions, new ExperimentalOptions(), logRecord); Assert.NotNull(otlpLogRecord); Assert.Empty(otlpLogRecord.Attributes); } - [Theory] - [InlineData(false)] - [InlineData(true)] - public void ToOtlpLog_WhenOptionsIncludeScopesIsTrue_AndScopeStateIsOfDictionaryType_ScopeIsProcessed(bool useCustomSerializer) + [Fact] + public void ToOtlpLog_WhenOptionsIncludeScopesIsTrue_AndScopeStateIsOfDictionaryType_ScopeIsProcessed() { // Arrange. var logRecords = new List(1); @@ -1294,11 +1078,7 @@ public void ToOtlpLog_WhenOptionsIncludeScopesIsTrue_AndScopeStateIsOfDictionary // Assert. var logRecord = logRecords.Single(); - var otlpLogRecordTransformer = new OtlpLogRecordTransformer(DefaultSdkLimitOptions, new()); - - var otlpLogRecord = useCustomSerializer - ? ToOtlpLogs(DefaultSdkLimitOptions, new ExperimentalOptions(), logRecord) - : otlpLogRecordTransformer.ToOtlpLog(logRecord); + var otlpLogRecord = ToOtlpLogs(DefaultSdkLimitOptions, new ExperimentalOptions(), logRecord); Assert.NotNull(otlpLogRecord); Assert.Single(otlpLogRecord.Attributes); @@ -1309,13 +1089,10 @@ public void ToOtlpLog_WhenOptionsIncludeScopesIsTrue_AndScopeStateIsOfDictionary } [Theory] - [InlineData(typeof(List>), false)] - [InlineData(typeof(ReadOnlyCollection>), false)] - [InlineData(typeof(HashSet>), false)] - [InlineData(typeof(List>), true)] - [InlineData(typeof(ReadOnlyCollection>), true)] - [InlineData(typeof(HashSet>), true)] - public void ToOtlpLog_WhenOptionsIncludeScopesIsTrue_AndScopeStateIsOfEnumerableType_ScopeIsProcessed(Type typeOfScopeState, bool useCustomSerializer) + [InlineData(typeof(List>))] + [InlineData(typeof(ReadOnlyCollection>))] + [InlineData(typeof(HashSet>))] + public void ToOtlpLog_WhenOptionsIncludeScopesIsTrue_AndScopeStateIsOfEnumerableType_ScopeIsProcessed(Type typeOfScopeState) { // Arrange. var logRecords = new List(1); @@ -1341,11 +1118,7 @@ public void ToOtlpLog_WhenOptionsIncludeScopesIsTrue_AndScopeStateIsOfEnumerable // Assert. var logRecord = logRecords.Single(); - var otlpLogRecordTransformer = new OtlpLogRecordTransformer(DefaultSdkLimitOptions, new()); - - var otlpLogRecord = useCustomSerializer - ? ToOtlpLogs(DefaultSdkLimitOptions, new ExperimentalOptions(), logRecord) - : otlpLogRecordTransformer.ToOtlpLog(logRecord); + var otlpLogRecord = ToOtlpLogs(DefaultSdkLimitOptions, new ExperimentalOptions(), logRecord); Assert.NotNull(otlpLogRecord); Assert.Single(otlpLogRecord.Attributes); @@ -1356,11 +1129,9 @@ public void ToOtlpLog_WhenOptionsIncludeScopesIsTrue_AndScopeStateIsOfEnumerable } [Theory] - [InlineData("Same scope key", "Same scope key", false)] - [InlineData("Scope key 1", "Scope key 2", false)] - [InlineData("Same scope key", "Same scope key", true)] - [InlineData("Scope key 1", "Scope key 2", true)] - public void ToOtlpLog_WhenOptionsIncludeScopesIsTrue_AndMultipleScopesAreAdded_ContainsAllAddedScopeValues(string scopeKey1, string scopeKey2, bool useCustomSerializer) + [InlineData("Same scope key", "Same scope key")] + [InlineData("Scope key 1", "Scope key 2")] + public void ToOtlpLog_WhenOptionsIncludeScopesIsTrue_AndMultipleScopesAreAdded_ContainsAllAddedScopeValues(string scopeKey1, string scopeKey2) { // Arrange. var logRecords = new List(1); @@ -1387,11 +1158,7 @@ public void ToOtlpLog_WhenOptionsIncludeScopesIsTrue_AndMultipleScopesAreAdded_C // Assert. var logRecord = logRecords.Single(); - var otlpLogRecordTransformer = new OtlpLogRecordTransformer(DefaultSdkLimitOptions, new()); - - var otlpLogRecord = useCustomSerializer - ? ToOtlpLogs(DefaultSdkLimitOptions, new ExperimentalOptions(), logRecord) - : otlpLogRecordTransformer.ToOtlpLog(logRecord); + var otlpLogRecord = ToOtlpLogs(DefaultSdkLimitOptions, new ExperimentalOptions(), logRecord); Assert.NotNull(otlpLogRecord); var allScopeValues = otlpLogRecord.Attributes @@ -1404,11 +1171,9 @@ public void ToOtlpLog_WhenOptionsIncludeScopesIsTrue_AndMultipleScopesAreAdded_C } [Theory] - [InlineData("Same scope key", "Same scope key", false)] - [InlineData("Scope key 1", "Scope key 2", false)] - [InlineData("Same scope key", "Same scope key", true)] - [InlineData("Scope key 1", "Scope key 2", true)] - public void ToOtlpLog_WhenOptionsIncludeScopesIsTrue_AndMultipleScopeLevelsAreAdded_ContainsAllAddedScopeValues(string scopeKey1, string scopeKey2, bool useCustomSerializer) + [InlineData("Same scope key", "Same scope key")] + [InlineData("Scope key 1", "Scope key 2")] + public void ToOtlpLog_WhenOptionsIncludeScopesIsTrue_AndMultipleScopeLevelsAreAdded_ContainsAllAddedScopeValues(string scopeKey1, string scopeKey2) { // Arrange. var logRecords = new List(1); @@ -1434,11 +1199,7 @@ public void ToOtlpLog_WhenOptionsIncludeScopesIsTrue_AndMultipleScopeLevelsAreAd // Assert. var logRecord = logRecords.Single(); - var otlpLogRecordTransformer = new OtlpLogRecordTransformer(DefaultSdkLimitOptions, new()); - - var otlpLogRecord = useCustomSerializer - ? ToOtlpLogs(DefaultSdkLimitOptions, new ExperimentalOptions(), logRecord) - : otlpLogRecordTransformer.ToOtlpLog(logRecord); + var otlpLogRecord = ToOtlpLogs(DefaultSdkLimitOptions, new ExperimentalOptions(), logRecord); Assert.NotNull(otlpLogRecord); var allScopeValues = otlpLogRecord.Attributes @@ -1451,11 +1212,9 @@ public void ToOtlpLog_WhenOptionsIncludeScopesIsTrue_AndMultipleScopeLevelsAreAd } [Theory] - [InlineData("Same scope key", "Same scope key", false)] - [InlineData("Scope key 1", "Scope key 2", false)] - [InlineData("Same scope key", "Same scope key", true)] - [InlineData("Scope key 1", "Scope key 2", true)] - public void ToOtlpLog_WhenOptionsIncludeScopesIsTrue_AndScopeIsUsedInLogMethod_ContainsAllAddedScopeValues(string scopeKey1, string scopeKey2, bool useCustomSerializer) + [InlineData("Same scope key", "Same scope key")] + [InlineData("Scope key 1", "Scope key 2")] + public void ToOtlpLog_WhenOptionsIncludeScopesIsTrue_AndScopeIsUsedInLogMethod_ContainsAllAddedScopeValues(string scopeKey1, string scopeKey2) { // Arrange. var logRecords = new List(1); @@ -1486,11 +1245,7 @@ public void ToOtlpLog_WhenOptionsIncludeScopesIsTrue_AndScopeIsUsedInLogMethod_C // Assert. var logRecord = logRecords.Single(); - var otlpLogRecordTransformer = new OtlpLogRecordTransformer(DefaultSdkLimitOptions, new()); - - var otlpLogRecord = useCustomSerializer - ? ToOtlpLogs(DefaultSdkLimitOptions, new ExperimentalOptions(), logRecord) - : otlpLogRecordTransformer.ToOtlpLog(logRecord); + var otlpLogRecord = ToOtlpLogs(DefaultSdkLimitOptions, new ExperimentalOptions(), logRecord); Assert.NotNull(otlpLogRecord); var allScopeValues = otlpLogRecord.Attributes @@ -1571,10 +1326,8 @@ public void AddOtlpLogExporterLogRecordProcessorOptionsTest(ExportProcessorType } } - [Theory] - [InlineData(true)] - [InlineData(false)] - public void ValidateInstrumentationScope(bool useCustomSerializer) + [Fact] + public void ValidateInstrumentationScope() { var logRecords = new List(); using var loggerFactory = LoggerFactory.Create(builder => @@ -1591,20 +1344,10 @@ public void ValidateInstrumentationScope(bool useCustomSerializer) Assert.Equal(2, logRecords.Count); var batch = new Batch(logRecords.ToArray(), logRecords.Count); - var logRecordTransformer = new OtlpLogRecordTransformer(new(), new()); - var resourceBuilder = ResourceBuilder.CreateEmpty(); - var processResource = resourceBuilder.Build().ToOtlpResource(); + var processResource = CreateResourceSpans(resourceBuilder.Build()); - OtlpCollector.ExportLogsServiceRequest request; - if (useCustomSerializer) - { - request = CreateLogsExportRequest(DefaultSdkLimitOptions, new ExperimentalOptions(), batch, resourceBuilder.Build()); - } - else - { - request = logRecordTransformer.BuildExportRequest(processResource, batch); - } + OtlpCollector.ExportLogsServiceRequest request = CreateLogsExportRequest(DefaultSdkLimitOptions, new ExperimentalOptions(), batch, resourceBuilder.Build()); Assert.Single(request.ResourceLogs); @@ -1624,22 +1367,7 @@ public void ValidateInstrumentationScope(bool useCustomSerializer) Assert.Equal("Hello from green-tomato", logrecord2.Body.StringValue); - // Validate LogListPool - Assert.Empty(OtlpLogRecordTransformer.LogListPool); - logRecordTransformer.Return(request); - Assert.Equal(2, OtlpLogRecordTransformer.LogListPool.Count); - - if (useCustomSerializer) - { - request = CreateLogsExportRequest(DefaultSdkLimitOptions, new ExperimentalOptions(), batch, resourceBuilder.Build()); - } - else - { - request = logRecordTransformer.BuildExportRequest(processResource, batch); - - // ScopeLogs will be reused. - Assert.Empty(OtlpLogRecordTransformer.LogListPool); - } + request = CreateLogsExportRequest(DefaultSdkLimitOptions, new ExperimentalOptions(), batch, resourceBuilder.Build()); Assert.Single(request.ResourceLogs); } @@ -1699,11 +1427,9 @@ public void VerifyEnvironmentVariablesTakenFromIConfigurationWhenUsingLoggingBui } [Theory] - [InlineData("my_instrumentation_scope_name", "my_instrumentation_scope_name", true)] - [InlineData(null, "", true)] - [InlineData("my_instrumentation_scope_name", "my_instrumentation_scope_name", false)] - [InlineData(null, "", false)] - public void LogRecordLoggerNameIsExportedWhenUsingBridgeApi(string? loggerName, string expectedScopeName, bool useCustomSerializer) + [InlineData("my_instrumentation_scope_name", "my_instrumentation_scope_name")] + [InlineData(null, "")] + public void LogRecordLoggerNameIsExportedWhenUsingBridgeApi(string? loggerName, string expectedScopeName) { LogRecordAttributeList attributes = default; attributes.Add("name", "tomato"); @@ -1723,21 +1449,8 @@ public void LogRecordLoggerNameIsExportedWhenUsingBridgeApi(string? loggerName, Assert.Single(logRecords); - var otlpLogRecordTransformer = new OtlpLogRecordTransformer(DefaultSdkLimitOptions, new()); - var batch = new Batch(new[] { logRecords[0] }, 1); - - OtlpCollector.ExportLogsServiceRequest request; - if (useCustomSerializer) - { - request = CreateLogsExportRequest(DefaultSdkLimitOptions, new ExperimentalOptions(), batch, ResourceBuilder.CreateEmpty().Build()); - } - else - { - request = otlpLogRecordTransformer.BuildExportRequest( - new Proto.Resource.V1.Resource(), - batch); - } + OtlpCollector.ExportLogsServiceRequest request = CreateLogsExportRequest(DefaultSdkLimitOptions, new ExperimentalOptions(), batch, ResourceBuilder.CreateEmpty().Build()); Assert.NotNull(request); Assert.Single(request.ResourceLogs); @@ -1902,4 +1615,18 @@ private static OtlpCollector.ExportLogsServiceRequest CreateLogsExportRequest(Sd var scopeLogs = OtlpLogs.ScopeLogs.Parser.ParseFrom(stream); return scopeLogs.LogRecords.FirstOrDefault(); } + + private static ResourceSpans CreateResourceSpans(Resource resource) + { + byte[] buffer = new byte[1024]; + var writePosition = ProtobufOtlpResourceSerializer.WriteResource(buffer, 0, resource); + + ResourceSpans? resourceSpans; + using (var stream = new MemoryStream(buffer, 0, writePosition)) + { + resourceSpans = ResourceSpans.Parser.ParseFrom(stream); + } + + return resourceSpans; + } } diff --git a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpMetricsExporterTests.cs b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpMetricsExporterTests.cs index f34bc572126..d90d3934ad7 100644 --- a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpMetricsExporterTests.cs +++ b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpMetricsExporterTests.cs @@ -7,7 +7,6 @@ using Google.Protobuf; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; -using OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation; using OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.Serializer; using OpenTelemetry.Metrics; using OpenTelemetry.Resources; @@ -159,12 +158,9 @@ public void ServiceProviderHttpClientFactoryInvoked() } [Theory] - [InlineData(true, true)] - [InlineData(true, false)] - - [InlineData(false, true)] - [InlineData(false, false)] - public void ToOtlpResourceMetricsTest(bool useCustomSerializer, bool includeServiceNameInResource) + [InlineData(true)] + [InlineData(false)] + public void ToOtlpResourceMetricsTest(bool includeServiceNameInResource) { var resourceBuilder = ResourceBuilder.CreateEmpty(); if (includeServiceNameInResource) @@ -198,17 +194,7 @@ public void ToOtlpResourceMetricsTest(bool useCustomSerializer, bool includeServ provider.ForceFlush(); var batch = new Batch(metrics.ToArray(), metrics.Count); - - var request = new OtlpCollector.ExportMetricsServiceRequest(); - - if (useCustomSerializer) - { - request = CreateMetricExportRequest(batch, resourceBuilder.Build()); - } - else - { - request.AddMetrics(resourceBuilder.Build().ToOtlpResource(), batch); - } + var request = CreateMetricExportRequest(batch, resourceBuilder.Build()); Assert.Single(request.ResourceMetrics); var resourceMetric = request.ResourceMetrics.First(); @@ -236,18 +222,12 @@ public void ToOtlpResourceMetricsTest(bool useCustomSerializer, bool includeServ } [Theory] - [InlineData(true, "test_gauge", null, null, 123L, null)] - [InlineData(true, "test_gauge", null, null, null, 123.45)] - [InlineData(true, "test_gauge", null, null, 123L, null, true)] - [InlineData(true, "test_gauge", null, null, null, 123.45, true)] - [InlineData(true, "test_gauge", "description", "unit", 123L, null)] - - [InlineData(false, "test_gauge", null, null, 123L, null)] - [InlineData(false, "test_gauge", null, null, null, 123.45)] - [InlineData(false, "test_gauge", null, null, 123L, null, true)] - [InlineData(false, "test_gauge", null, null, null, 123.45, true)] - [InlineData(false, "test_gauge", "description", "unit", 123L, null)] - public void TestGaugeToOtlpMetric(bool useCustomSerializer, string name, string? description, string? unit, long? longValue, double? doubleValue, bool enableExemplars = false) + [InlineData("test_gauge", null, null, 123L, null)] + [InlineData("test_gauge", null, null, null, 123.45)] + [InlineData("test_gauge", null, null, 123L, null, true)] + [InlineData("test_gauge", null, null, null, 123.45, true)] + [InlineData("test_gauge", "description", "unit", 123L, null)] + public void TestGaugeToOtlpMetric(string name, string? description, string? unit, long? longValue, double? doubleValue, bool enableExemplars = false) { var metrics = new List(); @@ -271,16 +251,7 @@ public void TestGaugeToOtlpMetric(bool useCustomSerializer, string name, string? var batch = new Batch(metrics.ToArray(), metrics.Count); - var request = new OtlpCollector.ExportMetricsServiceRequest(); - - if (useCustomSerializer) - { - request = CreateMetricExportRequest(batch, ResourceBuilder.CreateEmpty().Build()); - } - else - { - request.AddMetrics(ResourceBuilder.CreateEmpty().Build().ToOtlpResource(), batch); - } + var request = CreateMetricExportRequest(batch, ResourceBuilder.CreateEmpty().Build()); var resourceMetric = request.ResourceMetrics.Single(); var scopeMetrics = resourceMetric.ScopeMetrics.Single(); @@ -320,28 +291,17 @@ public void TestGaugeToOtlpMetric(bool useCustomSerializer, string name, string? } [Theory] - [InlineData(true, "test_counter", null, null, 123L, null, MetricReaderTemporalityPreference.Cumulative)] - [InlineData(true, "test_counter", null, null, null, 123.45, MetricReaderTemporalityPreference.Cumulative)] - [InlineData(true, "test_counter", null, null, 123L, null, MetricReaderTemporalityPreference.Cumulative, false, true)] - [InlineData(true, "test_counter", null, null, null, 123.45, MetricReaderTemporalityPreference.Cumulative, false, true)] - [InlineData(true, "test_counter", null, null, 123L, null, MetricReaderTemporalityPreference.Delta)] - [InlineData(true, "test_counter", null, null, null, 123.45, MetricReaderTemporalityPreference.Delta)] - [InlineData(true, "test_counter", null, null, 123L, null, MetricReaderTemporalityPreference.Delta, false, true)] - [InlineData(true, "test_counter", null, null, null, 123.45, MetricReaderTemporalityPreference.Delta, false, true)] - [InlineData(true, "test_counter", "description", "unit", 123L, null, MetricReaderTemporalityPreference.Cumulative)] - [InlineData(true, "test_counter", null, null, 123L, null, MetricReaderTemporalityPreference.Delta, true)] - - [InlineData(false, "test_counter", null, null, 123L, null, MetricReaderTemporalityPreference.Cumulative)] - [InlineData(false, "test_counter", null, null, null, 123.45, MetricReaderTemporalityPreference.Cumulative)] - [InlineData(false, "test_counter", null, null, 123L, null, MetricReaderTemporalityPreference.Cumulative, false, true)] - [InlineData(false, "test_counter", null, null, null, 123.45, MetricReaderTemporalityPreference.Cumulative, false, true)] - [InlineData(false, "test_counter", null, null, 123L, null, MetricReaderTemporalityPreference.Delta)] - [InlineData(false, "test_counter", null, null, null, 123.45, MetricReaderTemporalityPreference.Delta)] - [InlineData(false, "test_counter", null, null, 123L, null, MetricReaderTemporalityPreference.Delta, false, true)] - [InlineData(false, "test_counter", null, null, null, 123.45, MetricReaderTemporalityPreference.Delta, false, true)] - [InlineData(false, "test_counter", "description", "unit", 123L, null, MetricReaderTemporalityPreference.Cumulative)] - [InlineData(false, "test_counter", null, null, 123L, null, MetricReaderTemporalityPreference.Delta, true)] - public void TestCounterToOtlpMetric(bool useCustomSerializer, string name, string? description, string? unit, long? longValue, double? doubleValue, MetricReaderTemporalityPreference aggregationTemporality, bool enableKeyValues = false, bool enableExemplars = false) + [InlineData("test_counter", null, null, 123L, null, MetricReaderTemporalityPreference.Cumulative)] + [InlineData("test_counter", null, null, null, 123.45, MetricReaderTemporalityPreference.Cumulative)] + [InlineData("test_counter", null, null, 123L, null, MetricReaderTemporalityPreference.Cumulative, false, true)] + [InlineData("test_counter", null, null, null, 123.45, MetricReaderTemporalityPreference.Cumulative, false, true)] + [InlineData("test_counter", null, null, 123L, null, MetricReaderTemporalityPreference.Delta)] + [InlineData("test_counter", null, null, null, 123.45, MetricReaderTemporalityPreference.Delta)] + [InlineData("test_counter", null, null, 123L, null, MetricReaderTemporalityPreference.Delta, false, true)] + [InlineData("test_counter", null, null, null, 123.45, MetricReaderTemporalityPreference.Delta, false, true)] + [InlineData("test_counter", "description", "unit", 123L, null, MetricReaderTemporalityPreference.Cumulative)] + [InlineData("test_counter", null, null, 123L, null, MetricReaderTemporalityPreference.Delta, true)] + public void TestCounterToOtlpMetric(string name, string? description, string? unit, long? longValue, double? doubleValue, MetricReaderTemporalityPreference aggregationTemporality, bool enableKeyValues = false, bool enableExemplars = false) { var metrics = new List(); @@ -370,17 +330,7 @@ public void TestCounterToOtlpMetric(bool useCustomSerializer, string name, strin provider.ForceFlush(); var batch = new Batch(metrics.ToArray(), metrics.Count); - - var request = new OtlpCollector.ExportMetricsServiceRequest(); - - if (useCustomSerializer) - { - request = CreateMetricExportRequest(batch, ResourceBuilder.CreateEmpty().Build()); - } - else - { - request.AddMetrics(ResourceBuilder.CreateEmpty().Build().ToOtlpResource(), batch); - } + var request = CreateMetricExportRequest(batch, ResourceBuilder.CreateEmpty().Build()); var resourceMetric = request.ResourceMetrics.Single(); var scopeMetrics = resourceMetric.ScopeMetrics.Single(); @@ -434,32 +384,19 @@ public void TestCounterToOtlpMetric(bool useCustomSerializer, string name, strin } [Theory] - [InlineData(true, "test_counter", null, null, 123L, null, MetricReaderTemporalityPreference.Cumulative)] - [InlineData(true, "test_counter", null, null, null, 123.45, MetricReaderTemporalityPreference.Cumulative)] - [InlineData(true, "test_counter", null, null, 123L, null, MetricReaderTemporalityPreference.Cumulative, false, true)] - [InlineData(true, "test_counter", null, null, null, 123.45, MetricReaderTemporalityPreference.Cumulative, false, true)] - [InlineData(true, "test_counter", null, null, -123L, null, MetricReaderTemporalityPreference.Cumulative)] - [InlineData(true, "test_counter", null, null, null, -123.45, MetricReaderTemporalityPreference.Cumulative)] - [InlineData(true, "test_counter", null, null, 123L, null, MetricReaderTemporalityPreference.Delta)] - [InlineData(true, "test_counter", null, null, null, -123.45, MetricReaderTemporalityPreference.Delta)] - [InlineData(true, "test_counter", null, null, 123L, null, MetricReaderTemporalityPreference.Delta, false, true)] - [InlineData(true, "test_counter", null, null, null, -123.45, MetricReaderTemporalityPreference.Delta, false, true)] - [InlineData(true, "test_counter", "description", "unit", 123L, null, MetricReaderTemporalityPreference.Cumulative)] - [InlineData(true, "test_counter", null, null, 123L, null, MetricReaderTemporalityPreference.Delta, true)] - - [InlineData(false, "test_counter", null, null, 123L, null, MetricReaderTemporalityPreference.Cumulative)] - [InlineData(false, "test_counter", null, null, null, 123.45, MetricReaderTemporalityPreference.Cumulative)] - [InlineData(false, "test_counter", null, null, 123L, null, MetricReaderTemporalityPreference.Cumulative, false, true)] - [InlineData(false, "test_counter", null, null, null, 123.45, MetricReaderTemporalityPreference.Cumulative, false, true)] - [InlineData(false, "test_counter", null, null, -123L, null, MetricReaderTemporalityPreference.Cumulative)] - [InlineData(false, "test_counter", null, null, null, -123.45, MetricReaderTemporalityPreference.Cumulative)] - [InlineData(false, "test_counter", null, null, 123L, null, MetricReaderTemporalityPreference.Delta)] - [InlineData(false, "test_counter", null, null, null, -123.45, MetricReaderTemporalityPreference.Delta)] - [InlineData(false, "test_counter", null, null, 123L, null, MetricReaderTemporalityPreference.Delta, false, true)] - [InlineData(false, "test_counter", null, null, null, -123.45, MetricReaderTemporalityPreference.Delta, false, true)] - [InlineData(false, "test_counter", "description", "unit", 123L, null, MetricReaderTemporalityPreference.Cumulative)] - [InlineData(false, "test_counter", null, null, 123L, null, MetricReaderTemporalityPreference.Delta, true)] - public void TestUpDownCounterToOtlpMetric(bool useCustomSerializer, string name, string? description, string? unit, long? longValue, double? doubleValue, MetricReaderTemporalityPreference aggregationTemporality, bool enableKeyValues = false, bool enableExemplars = false) + [InlineData("test_counter", null, null, 123L, null, MetricReaderTemporalityPreference.Cumulative)] + [InlineData("test_counter", null, null, null, 123.45, MetricReaderTemporalityPreference.Cumulative)] + [InlineData("test_counter", null, null, 123L, null, MetricReaderTemporalityPreference.Cumulative, false, true)] + [InlineData("test_counter", null, null, null, 123.45, MetricReaderTemporalityPreference.Cumulative, false, true)] + [InlineData("test_counter", null, null, -123L, null, MetricReaderTemporalityPreference.Cumulative)] + [InlineData("test_counter", null, null, null, -123.45, MetricReaderTemporalityPreference.Cumulative)] + [InlineData("test_counter", null, null, 123L, null, MetricReaderTemporalityPreference.Delta)] + [InlineData("test_counter", null, null, null, -123.45, MetricReaderTemporalityPreference.Delta)] + [InlineData("test_counter", null, null, 123L, null, MetricReaderTemporalityPreference.Delta, false, true)] + [InlineData("test_counter", null, null, null, -123.45, MetricReaderTemporalityPreference.Delta, false, true)] + [InlineData("test_counter", "description", "unit", 123L, null, MetricReaderTemporalityPreference.Cumulative)] + [InlineData("test_counter", null, null, 123L, null, MetricReaderTemporalityPreference.Delta, true)] + public void TestUpDownCounterToOtlpMetric(string name, string? description, string? unit, long? longValue, double? doubleValue, MetricReaderTemporalityPreference aggregationTemporality, bool enableKeyValues = false, bool enableExemplars = false) { var metrics = new List(); @@ -489,15 +426,7 @@ public void TestUpDownCounterToOtlpMetric(bool useCustomSerializer, string name, var batch = new Batch(metrics.ToArray(), metrics.Count); - var request = new OtlpCollector.ExportMetricsServiceRequest(); - if (useCustomSerializer) - { - request = CreateMetricExportRequest(batch, ResourceBuilder.CreateEmpty().Build()); - } - else - { - request.AddMetrics(ResourceBuilder.CreateEmpty().Build().ToOtlpResource(), batch); - } + var request = CreateMetricExportRequest(batch, ResourceBuilder.CreateEmpty().Build()); var resourceMetric = request.ResourceMetrics.Single(); var scopeMetrics = resourceMetric.ScopeMetrics.Single(); @@ -551,32 +480,19 @@ public void TestUpDownCounterToOtlpMetric(bool useCustomSerializer, string name, } [Theory] - [InlineData(true, "test_histogram", null, null, 123L, null, MetricReaderTemporalityPreference.Cumulative)] - [InlineData(true, "test_histogram", null, null, null, 123.45, MetricReaderTemporalityPreference.Cumulative)] - [InlineData(true, "test_histogram", null, null, 123L, null, MetricReaderTemporalityPreference.Cumulative, false, true)] - [InlineData(true, "test_histogram", null, null, null, 123.45, MetricReaderTemporalityPreference.Cumulative, false, true)] - [InlineData(true, "test_histogram", null, null, -123L, null, MetricReaderTemporalityPreference.Cumulative)] - [InlineData(true, "test_histogram", null, null, null, -123.45, MetricReaderTemporalityPreference.Cumulative)] - [InlineData(true, "test_histogram", null, null, 123L, null, MetricReaderTemporalityPreference.Delta)] - [InlineData(true, "test_histogram", null, null, null, 123.45, MetricReaderTemporalityPreference.Delta)] - [InlineData(true, "test_histogram", null, null, 123L, null, MetricReaderTemporalityPreference.Delta, false, true)] - [InlineData(true, "test_histogram", null, null, null, 123.45, MetricReaderTemporalityPreference.Delta, false, true)] - [InlineData(true, "test_histogram", "description", "unit", 123L, null, MetricReaderTemporalityPreference.Cumulative)] - [InlineData(true, "test_histogram", null, null, 123L, null, MetricReaderTemporalityPreference.Delta, true)] - - [InlineData(false, "test_histogram", null, null, 123L, null, MetricReaderTemporalityPreference.Cumulative)] - [InlineData(false, "test_histogram", null, null, null, 123.45, MetricReaderTemporalityPreference.Cumulative)] - [InlineData(false, "test_histogram", null, null, 123L, null, MetricReaderTemporalityPreference.Cumulative, false, true)] - [InlineData(false, "test_histogram", null, null, null, 123.45, MetricReaderTemporalityPreference.Cumulative, false, true)] - [InlineData(false, "test_histogram", null, null, -123L, null, MetricReaderTemporalityPreference.Cumulative)] - [InlineData(false, "test_histogram", null, null, null, -123.45, MetricReaderTemporalityPreference.Cumulative)] - [InlineData(false, "test_histogram", null, null, 123L, null, MetricReaderTemporalityPreference.Delta)] - [InlineData(false, "test_histogram", null, null, null, 123.45, MetricReaderTemporalityPreference.Delta)] - [InlineData(false, "test_histogram", null, null, 123L, null, MetricReaderTemporalityPreference.Delta, false, true)] - [InlineData(false, "test_histogram", null, null, null, 123.45, MetricReaderTemporalityPreference.Delta, false, true)] - [InlineData(false, "test_histogram", "description", "unit", 123L, null, MetricReaderTemporalityPreference.Cumulative)] - [InlineData(false, "test_histogram", null, null, 123L, null, MetricReaderTemporalityPreference.Delta, true)] - public void TestExponentialHistogramToOtlpMetric(bool useCustomSerializer, string name, string? description, string? unit, long? longValue, double? doubleValue, MetricReaderTemporalityPreference aggregationTemporality, bool enableKeyValues = false, bool enableExemplars = false) + [InlineData("test_histogram", null, null, 123L, null, MetricReaderTemporalityPreference.Cumulative)] + [InlineData("test_histogram", null, null, null, 123.45, MetricReaderTemporalityPreference.Cumulative)] + [InlineData("test_histogram", null, null, 123L, null, MetricReaderTemporalityPreference.Cumulative, false, true)] + [InlineData("test_histogram", null, null, null, 123.45, MetricReaderTemporalityPreference.Cumulative, false, true)] + [InlineData("test_histogram", null, null, -123L, null, MetricReaderTemporalityPreference.Cumulative)] + [InlineData("test_histogram", null, null, null, -123.45, MetricReaderTemporalityPreference.Cumulative)] + [InlineData("test_histogram", null, null, 123L, null, MetricReaderTemporalityPreference.Delta)] + [InlineData("test_histogram", null, null, null, 123.45, MetricReaderTemporalityPreference.Delta)] + [InlineData("test_histogram", null, null, 123L, null, MetricReaderTemporalityPreference.Delta, false, true)] + [InlineData("test_histogram", null, null, null, 123.45, MetricReaderTemporalityPreference.Delta, false, true)] + [InlineData("test_histogram", "description", "unit", 123L, null, MetricReaderTemporalityPreference.Cumulative)] + [InlineData("test_histogram", null, null, 123L, null, MetricReaderTemporalityPreference.Delta, true)] + public void TestExponentialHistogramToOtlpMetric(string name, string? description, string? unit, long? longValue, double? doubleValue, MetricReaderTemporalityPreference aggregationTemporality, bool enableKeyValues = false, bool enableExemplars = false) { var metrics = new List(); @@ -611,16 +527,7 @@ public void TestExponentialHistogramToOtlpMetric(bool useCustomSerializer, strin provider.ForceFlush(); var batch = new Batch(metrics.ToArray(), metrics.Count); - - var request = new OtlpCollector.ExportMetricsServiceRequest(); - if (useCustomSerializer) - { - request = CreateMetricExportRequest(batch, ResourceBuilder.CreateEmpty().Build()); - } - else - { - request.AddMetrics(ResourceBuilder.CreateEmpty().Build().ToOtlpResource(), batch); - } + var request = CreateMetricExportRequest(batch, ResourceBuilder.CreateEmpty().Build()); var resourceMetric = request.ResourceMetrics.Single(); var scopeMetrics = resourceMetric.ScopeMetrics.Single(); @@ -711,32 +618,19 @@ public void TestExponentialHistogramToOtlpMetric(bool useCustomSerializer, strin } [Theory] - [InlineData(true, "test_histogram", null, null, 123L, null, MetricReaderTemporalityPreference.Cumulative)] - [InlineData(true, "test_histogram", null, null, null, 123.45, MetricReaderTemporalityPreference.Cumulative)] - [InlineData(true, "test_histogram", null, null, 123L, null, MetricReaderTemporalityPreference.Cumulative, false, true)] - [InlineData(true, "test_histogram", null, null, null, 123.45, MetricReaderTemporalityPreference.Cumulative, false, true)] - [InlineData(true, "test_histogram", null, null, -123L, null, MetricReaderTemporalityPreference.Cumulative)] - [InlineData(true, "test_histogram", null, null, null, -123.45, MetricReaderTemporalityPreference.Cumulative)] - [InlineData(true, "test_histogram", null, null, 123L, null, MetricReaderTemporalityPreference.Delta)] - [InlineData(true, "test_histogram", null, null, null, 123.45, MetricReaderTemporalityPreference.Delta)] - [InlineData(true, "test_histogram", null, null, 123L, null, MetricReaderTemporalityPreference.Delta, false, true)] - [InlineData(true, "test_histogram", null, null, null, 123.45, MetricReaderTemporalityPreference.Delta, false, true)] - [InlineData(true, "test_histogram", "description", "unit", 123L, null, MetricReaderTemporalityPreference.Cumulative)] - [InlineData(true, "test_histogram", null, null, 123L, null, MetricReaderTemporalityPreference.Delta, true)] - - [InlineData(false, "test_histogram", null, null, 123L, null, MetricReaderTemporalityPreference.Cumulative)] - [InlineData(false, "test_histogram", null, null, null, 123.45, MetricReaderTemporalityPreference.Cumulative)] - [InlineData(false, "test_histogram", null, null, 123L, null, MetricReaderTemporalityPreference.Cumulative, false, true)] - [InlineData(false, "test_histogram", null, null, null, 123.45, MetricReaderTemporalityPreference.Cumulative, false, true)] - [InlineData(false, "test_histogram", null, null, -123L, null, MetricReaderTemporalityPreference.Cumulative)] - [InlineData(false, "test_histogram", null, null, null, -123.45, MetricReaderTemporalityPreference.Cumulative)] - [InlineData(false, "test_histogram", null, null, 123L, null, MetricReaderTemporalityPreference.Delta)] - [InlineData(false, "test_histogram", null, null, null, 123.45, MetricReaderTemporalityPreference.Delta)] - [InlineData(false, "test_histogram", null, null, 123L, null, MetricReaderTemporalityPreference.Delta, false, true)] - [InlineData(false, "test_histogram", null, null, null, 123.45, MetricReaderTemporalityPreference.Delta, false, true)] - [InlineData(false, "test_histogram", "description", "unit", 123L, null, MetricReaderTemporalityPreference.Cumulative)] - [InlineData(false, "test_histogram", null, null, 123L, null, MetricReaderTemporalityPreference.Delta, true)] - public void TestHistogramToOtlpMetric(bool useCustomSerializer, string name, string? description, string? unit, long? longValue, double? doubleValue, MetricReaderTemporalityPreference aggregationTemporality, bool enableKeyValues = false, bool enableExemplars = false) + [InlineData("test_histogram", null, null, 123L, null, MetricReaderTemporalityPreference.Cumulative)] + [InlineData("test_histogram", null, null, null, 123.45, MetricReaderTemporalityPreference.Cumulative)] + [InlineData("test_histogram", null, null, 123L, null, MetricReaderTemporalityPreference.Cumulative, false, true)] + [InlineData("test_histogram", null, null, null, 123.45, MetricReaderTemporalityPreference.Cumulative, false, true)] + [InlineData("test_histogram", null, null, -123L, null, MetricReaderTemporalityPreference.Cumulative)] + [InlineData("test_histogram", null, null, null, -123.45, MetricReaderTemporalityPreference.Cumulative)] + [InlineData("test_histogram", null, null, 123L, null, MetricReaderTemporalityPreference.Delta)] + [InlineData("test_histogram", null, null, null, 123.45, MetricReaderTemporalityPreference.Delta)] + [InlineData("test_histogram", null, null, 123L, null, MetricReaderTemporalityPreference.Delta, false, true)] + [InlineData("test_histogram", null, null, null, 123.45, MetricReaderTemporalityPreference.Delta, false, true)] + [InlineData("test_histogram", "description", "unit", 123L, null, MetricReaderTemporalityPreference.Cumulative)] + [InlineData("test_histogram", null, null, 123L, null, MetricReaderTemporalityPreference.Delta, true)] + public void TestHistogramToOtlpMetric(string name, string? description, string? unit, long? longValue, double? doubleValue, MetricReaderTemporalityPreference aggregationTemporality, bool enableKeyValues = false, bool enableExemplars = false) { var metrics = new List(); @@ -765,16 +659,7 @@ public void TestHistogramToOtlpMetric(bool useCustomSerializer, string name, str provider.ForceFlush(); var batch = new Batch(metrics.ToArray(), metrics.Count); - - var request = new OtlpCollector.ExportMetricsServiceRequest(); - if (useCustomSerializer) - { - request = CreateMetricExportRequest(batch, ResourceBuilder.CreateEmpty().Build()); - } - else - { - request.AddMetrics(ResourceBuilder.CreateEmpty().Build().ToOtlpResource(), batch); - } + var request = CreateMetricExportRequest(batch, ResourceBuilder.CreateEmpty().Build()); var resourceMetric = request.ResourceMetrics.Single(); var scopeMetrics = resourceMetric.ScopeMetrics.Single(); @@ -895,16 +780,11 @@ public void TestTemporalityPreferenceUsingEnvVar(string configValue, MetricReade } [Theory] - [InlineData(true, false, false)] - [InlineData(true, true, false)] - [InlineData(true, false, true)] - [InlineData(true, true, true)] - - [InlineData(false, false, false)] - [InlineData(false, true, false)] - [InlineData(false, false, true)] - [InlineData(false, true, true)] - public void ToOtlpExemplarTests(bool useCustomSerializer, bool enableTagFiltering, bool enableTracing) + [InlineData(false, false)] + [InlineData(true, false)] + [InlineData(false, true)] + [InlineData(true, true)] + public void ToOtlpExemplarTests(bool enableTagFiltering, bool enableTracing) { ActivitySource? activitySource = null; Activity? activity = null; @@ -949,15 +829,7 @@ public void ToOtlpExemplarTests(bool useCustomSerializer, bool enableTagFilterin meterProvider.ForceFlush(); var batch = new Batch(exportedItems.ToArray(), exportedItems.Count); - var request = new OtlpCollector.ExportMetricsServiceRequest(); - if (useCustomSerializer) - { - request = CreateMetricExportRequest(batch, ResourceBuilder.CreateEmpty().Build()); - } - else - { - request.AddMetrics(ResourceBuilder.CreateEmpty().Build().ToOtlpResource(), batch); - } + var request = CreateMetricExportRequest(batch, ResourceBuilder.CreateEmpty().Build()); Assert.Single(request.ResourceMetrics); var resourceMetric = request.ResourceMetrics.First(); diff --git a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpResourceTests.cs b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpResourceTests.cs index 57b1c58fdf2..4d309326a78 100644 --- a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpResourceTests.cs +++ b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpResourceTests.cs @@ -1,7 +1,6 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 -using OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation; using OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.Serializer; using OpenTelemetry.Proto.Trace.V1; using OpenTelemetry.Resources; @@ -12,11 +11,9 @@ namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests; public class OtlpResourceTests { [Theory] - [InlineData(true, false)] - [InlineData(false, false)] - [InlineData(true, true)] - [InlineData(false, true)] - public void ToOtlpResourceTest(bool includeServiceNameInResource, bool useCustomSerializer) + [InlineData(true)] + [InlineData(false)] + public void ToOtlpResourceTest(bool includeServiceNameInResource) { // Targeted test to cover OTel Resource to OTLP Resource // conversion, independent of signals. @@ -29,21 +26,14 @@ public void ToOtlpResourceTest(bool includeServiceNameInResource, bool useCustom var resource = resourceBuilder.Build(); Proto.Resource.V1.Resource otlpResource; - if (useCustomSerializer) - { - byte[] buffer = new byte[1024]; - var writePosition = ProtobufOtlpResourceSerializer.WriteResource(buffer, 0, resource); + byte[] buffer = new byte[1024]; + var writePosition = ProtobufOtlpResourceSerializer.WriteResource(buffer, 0, resource); - // Deserialize the ResourceSpans and validate the attributes. - using (var stream = new MemoryStream(buffer, 0, writePosition)) - { - var resourceSpans = ResourceSpans.Parser.ParseFrom(stream); - otlpResource = resourceSpans.Resource; - } - } - else + // Deserialize the ResourceSpans and validate the attributes. + using (var stream = new MemoryStream(buffer, 0, writePosition)) { - otlpResource = resource.ToOtlpResource(); + var resourceSpans = ResourceSpans.Parser.ParseFrom(stream); + otlpResource = resourceSpans.Resource; } if (includeServiceNameInResource) diff --git a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpRetryTests.cs b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpRetryTests.cs index e72faee429c..b380d92d6b0 100644 --- a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpRetryTests.cs +++ b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpRetryTests.cs @@ -8,7 +8,7 @@ #endif using Google.Protobuf; using Google.Protobuf.WellKnownTypes; -using Grpc.Core; +using OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.ExportClient.Grpc; using Xunit; namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.ExportClient.Tests; @@ -29,11 +29,10 @@ public void TryGetGrpcRetryResultTest(GrpcRetryTestCase testCase) foreach (var retryAttempt in testCase.RetryAttempts) { ++attempts; - var rpcException = retryAttempt.Response.Exception as RpcException; - Assert.NotNull(rpcException); - var statusCode = rpcException.StatusCode; + Assert.NotNull(retryAttempt.Response.Status); + var statusCode = retryAttempt.Response.Status.Value.StatusCode; var deadline = retryAttempt.Response.DeadlineUtc; - var trailers = rpcException.Trailers; + var trailers = retryAttempt.Response.GrpcStatusDetailsHeader; var success = OtlpRetry.TryGetGrpcRetryResult(retryAttempt.Response, nextRetryDelayMilliseconds, out var retryResult); Assert.Equal(retryAttempt.ExpectedSuccess, success); @@ -46,7 +45,7 @@ public void TryGetGrpcRetryResultTest(GrpcRetryTestCase testCase) if (retryResult.Throttled) { - Assert.Equal(retryAttempt.ThrottleDelay, retryResult.RetryDelay); + Assert.Equal(GrpcStatusDeserializer.TryGetGrpcRetryDelay(retryAttempt.ThrottleDelay), retryResult.RetryDelay); } else { @@ -131,9 +130,9 @@ public static IEnumerable GetGrpcTestCases() yield return new[] { new GrpcRetryTestCase("Unknown", new GrpcRetryAttempt[] { new(StatusCode.Unknown, expectedSuccess: false) }) }; yield return new[] { new GrpcRetryTestCase("ResourceExhausted w/o RetryInfo", new GrpcRetryAttempt[] { new(StatusCode.ResourceExhausted, expectedSuccess: false) }) }; - yield return new[] { new GrpcRetryTestCase("ResourceExhausted w/ RetryInfo", new GrpcRetryAttempt[] { new(StatusCode.ResourceExhausted, throttleDelay: new Duration { Seconds = 2 }, expectedNextRetryDelayMilliseconds: 3000) }) }; + yield return new[] { new GrpcRetryTestCase("ResourceExhausted w/ RetryInfo", new GrpcRetryAttempt[] { new(StatusCode.ResourceExhausted, throttleDelay: GetThrottleDelayString(new Duration { Seconds = 2 }), expectedNextRetryDelayMilliseconds: 3000) }) }; - yield return new[] { new GrpcRetryTestCase("Unavailable w/ RetryInfo", new GrpcRetryAttempt[] { new(StatusCode.Unavailable, throttleDelay: Duration.FromTimeSpan(TimeSpan.FromMilliseconds(2000)), expectedNextRetryDelayMilliseconds: 3000) }) }; + yield return new[] { new GrpcRetryTestCase("Unavailable w/ RetryInfo", new GrpcRetryAttempt[] { new(StatusCode.Unavailable, throttleDelay: GetThrottleDelayString(Duration.FromTimeSpan(TimeSpan.FromMilliseconds(2000))), expectedNextRetryDelayMilliseconds: 3000) }) }; yield return new[] { new GrpcRetryTestCase("Expired deadline", new GrpcRetryAttempt[] { new(StatusCode.Unavailable, deadlineExceeded: true, expectedSuccess: false) }) }; @@ -176,7 +175,7 @@ public static IEnumerable GetGrpcTestCases() { new(StatusCode.Unavailable, expectedNextRetryDelayMilliseconds: 1500), new(StatusCode.Unavailable, expectedNextRetryDelayMilliseconds: 2250), - new(StatusCode.Unavailable, throttleDelay: Duration.FromTimeSpan(TimeSpan.FromMilliseconds(500)), expectedNextRetryDelayMilliseconds: 750), + new(StatusCode.Unavailable, throttleDelay: GetThrottleDelayString(Duration.FromTimeSpan(TimeSpan.FromMilliseconds(500))), expectedNextRetryDelayMilliseconds: 750), new(StatusCode.Unavailable, expectedNextRetryDelayMilliseconds: 1125), new(StatusCode.Unavailable, expectedNextRetryDelayMilliseconds: 1688), new(StatusCode.Unavailable, expectedNextRetryDelayMilliseconds: 2532), @@ -193,48 +192,46 @@ public override string ToString() return this.testRunnerName; } - private static Metadata GenerateTrailers(Duration throttleDelay) + private static string GetThrottleDelayString(Duration throttleDelay) { - var metadata = new Metadata(); - - var retryInfo = new Google.Rpc.RetryInfo(); - retryInfo.RetryDelay = throttleDelay; - - var status = new Google.Rpc.Status(); - status.Details.Add(Any.Pack(retryInfo)); - - var stream = new MemoryStream(); - status.WriteTo(stream); + var status = new Google.Rpc.Status + { + Code = 4, + Message = "Only nanos", + Details = + { + Any.Pack(new Google.Rpc.RetryInfo + { + RetryDelay = throttleDelay, + }), + }, + }; - metadata.Add(OtlpRetry.GrpcStatusDetailsHeader, stream.ToArray()); - return metadata; + return Convert.ToBase64String(status.ToByteArray()); } public struct GrpcRetryAttempt { - public TimeSpan? ThrottleDelay; + public string? ThrottleDelay; public int? ExpectedNextRetryDelayMilliseconds; public bool ExpectedSuccess; internal ExportClientGrpcResponse Response; - public GrpcRetryAttempt( + internal GrpcRetryAttempt( StatusCode statusCode, bool deadlineExceeded = false, - Duration? throttleDelay = null, + string? throttleDelay = null, int expectedNextRetryDelayMilliseconds = 1500, bool expectedSuccess = true) { var status = new Status(statusCode, "Error"); - var rpcException = throttleDelay != null - ? new RpcException(status, GenerateTrailers(throttleDelay)) - : new RpcException(status); // Using arbitrary +1 hr for deadline for test purposes. var deadlineUtc = deadlineExceeded ? DateTime.UtcNow.AddSeconds(-1) : DateTime.UtcNow.AddHours(1); - this.ThrottleDelay = throttleDelay != null ? throttleDelay.ToTimeSpan() : null; + this.ThrottleDelay = throttleDelay; - this.Response = new ExportClientGrpcResponse(expectedSuccess, deadlineUtc, rpcException, null, null); + this.Response = new ExportClientGrpcResponse(expectedSuccess, deadlineUtc, null, status, this.ThrottleDelay); this.ExpectedNextRetryDelayMilliseconds = expectedNextRetryDelayMilliseconds; diff --git a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpTraceExporterTests.cs b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpTraceExporterTests.cs index 6c876dda3d6..94b97ca1702 100644 --- a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpTraceExporterTests.cs +++ b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpTraceExporterTests.cs @@ -743,10 +743,10 @@ public void UseOpenTelemetryProtocolActivityExporterWithCustomActivityProcessor( [Fact] public void Shutdown_ClientShutdownIsCalled() { - var exportClientMock = new TestProtobufExportClient(); + var exportClientMock = new TestExportClient(); var exporterOptions = new OtlpExporterOptions(); - var transmissionHandler = new ProtobufOtlpExporterTransmissionHandler(exportClientMock, exporterOptions.TimeoutMilliseconds); + var transmissionHandler = new OtlpExporterTransmissionHandler(exportClientMock, exporterOptions.TimeoutMilliseconds); using var exporter = new OtlpTraceExporter(new OtlpExporterOptions(), DefaultSdkLimitOptions, DefaultExperimentalOptions, transmissionHandler); exporter.Shutdown(); diff --git a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/TestExportClient.cs b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/TestExportClient.cs index bbecfa2fa37..8879f540210 100644 --- a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/TestExportClient.cs +++ b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/TestExportClient.cs @@ -5,7 +5,7 @@ namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests; -internal class TestExportClient(bool throwException = false) : IExportClient +internal class TestExportClient(bool throwException = false) : IExportClient { public bool SendExportRequestCalled { get; private set; } @@ -13,7 +13,7 @@ internal class TestExportClient(bool throwException = false) : IExportClient< public bool ThrowException { get; set; } = throwException; - public ExportClientResponse SendExportRequest(T request, DateTime deadlineUtc, CancellationToken cancellationToken = default) + public ExportClientResponse SendExportRequest(byte[] buffer, int contentLength, DateTime deadlineUtc, CancellationToken cancellationToken = default) { if (this.ThrowException) { diff --git a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/TestProtobufExportClient.cs b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/TestProtobufExportClient.cs deleted file mode 100644 index 28fab7ea74c..00000000000 --- a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/TestProtobufExportClient.cs +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -using OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.ExportClient; - -namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests; - -internal class TestProtobufExportClient(bool throwException = false) : IProtobufExportClient -{ - public bool SendExportRequestCalled { get; private set; } - - public bool ShutdownCalled { get; private set; } - - public bool ThrowException { get; set; } = throwException; - - public ExportClientResponse SendExportRequest(byte[] buffer, int contentLength, DateTime deadlineUtc, CancellationToken cancellationToken = default) - { - if (this.ThrowException) - { - throw new Exception("Exception thrown from SendExportRequest"); - } - - this.SendExportRequestCalled = true; - return new TestExportClientResponse(true, deadlineUtc, null); - } - - public bool Shutdown(int timeoutMilliseconds) - { - this.ShutdownCalled = true; - return true; - } - - private class TestExportClientResponse : ExportClientResponse - { - public TestExportClientResponse(bool success, DateTime deadline, Exception? exception) - : base(success, deadline, exception) - { - } - } -} From 44bc90a25b9f6d5323bebc6883ec034ccc36c721 Mon Sep 17 00:00:00 2001 From: Utkarsh Umesan Pillai <66651184+utpilla@users.noreply.github.com> Date: Tue, 3 Dec 2024 10:29:40 -0800 Subject: [PATCH 2/7] [repo] Move Utkarsh to Emeritus (#6008) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Piotr KieÅ‚kowicz --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0b350bdfdc5..b1d03f687f8 100644 --- a/README.md +++ b/README.md @@ -243,7 +243,6 @@ you're more than welcome to participate! * [Cijo Thomas](https://github.com/cijothomas), Microsoft * [Piotr Kiełkowicz](https://github.com/Kielek), Splunk * [Rajkumar Rangaraj](https://github.com/rajkumar-rangaraj), Microsoft -* [Utkarsh Umesan Pillai](https://github.com/utpilla), Microsoft [Triagers](https://github.com/open-telemetry/community/blob/main/guides/contributor/membership.md#triager) ([@open-telemetry/dotnet-triagers](https://github.com/orgs/open-telemetry/teams/dotnet-triagers)): @@ -262,6 +261,7 @@ Maintainer/Approver/Triager](https://github.com/open-telemetry/community/blob/ma * [Reiley Yang](https://github.com/reyang) * [Robert Pająk](https://github.com/pellared) * [Sergey Kanzhelev](https://github.com/SergeyKanzhelev) +* [Utkarsh Umesan Pillai](https://github.com/utpilla) * [Victor Lu](https://github.com/victlu) * [Vishwesh Bankwar](https://github.com/vishweshbankwar) From 0c775e58fa8eb55468f321d5612b527ed80afb9f Mon Sep 17 00:00:00 2001 From: Timothy Mothra Date: Tue, 3 Dec 2024 13:57:57 -0800 Subject: [PATCH 3/7] [otlp] refactor constants (#6010) --- .../Implementation/Serializer/.editorconfig | 4 ++ .../ProtobufOtlpCommonFieldNumberConstants.cs | 32 +++++++++++++ .../ProtobufOtlpLogFieldNumberConstants.cs | 29 ++--------- .../Serializer/ProtobufOtlpLogSerializer.cs | 4 +- .../ProtobufOtlpMetricFieldNumberConstants.cs | 27 ----------- .../ProtobufOtlpMetricSerializer.cs | 8 ++-- .../Serializer/ProtobufOtlpTagWriter.cs | 48 +++++++++---------- .../ProtobufOtlpTraceFieldNumberConstants.cs | 28 ++--------- .../Serializer/ProtobufOtlpTraceSerializer.cs | 10 ++-- 9 files changed, 79 insertions(+), 111 deletions(-) create mode 100644 src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/.editorconfig create mode 100644 src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpCommonFieldNumberConstants.cs diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/.editorconfig b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/.editorconfig new file mode 100644 index 00000000000..822889f9eaa --- /dev/null +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/.editorconfig @@ -0,0 +1,4 @@ +[*Constants.cs] +# SA1310: Field names should not contain underscore +# Justification: These names describe the nested names and properties in the .Proto file. +dotnet_diagnostic.SA1310.severity = none diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpCommonFieldNumberConstants.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpCommonFieldNumberConstants.cs new file mode 100644 index 00000000000..4d220d598e8 --- /dev/null +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpCommonFieldNumberConstants.cs @@ -0,0 +1,32 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.Serializer; + +/// +/// Defines field number constants for fields defined in +/// . +/// +internal static class ProtobufOtlpCommonFieldNumberConstants +{ + // InstrumentationScope + internal const int InstrumentationScope_Name = 1; + internal const int InstrumentationScope_Version = 2; + internal const int InstrumentationScope_Attributes = 3; + internal const int InstrumentationScope_Dropped_Attributes_Count = 4; + + // KeyValue + internal const int KeyValue_Key = 1; + internal const int KeyValue_Value = 2; + + // AnyValue + internal const int AnyValue_String_Value = 1; + internal const int AnyValue_Bool_Value = 2; + internal const int AnyValue_Int_Value = 3; + internal const int AnyValue_Double_Value = 4; + internal const int AnyValue_Array_Value = 5; + internal const int AnyValue_Kvlist_Value = 6; + internal const int AnyValue_Bytes_Value = 7; + + internal const int ArrayValue_Value = 1; +} diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpLogFieldNumberConstants.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpLogFieldNumberConstants.cs index 1ff402d6b1c..f7c97f6e222 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpLogFieldNumberConstants.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpLogFieldNumberConstants.cs @@ -3,10 +3,12 @@ namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.Serializer; +/// +/// Defines field number constants for fields defined in +/// . +/// internal static class ProtobufOtlpLogFieldNumberConstants { -#pragma warning disable SA1310 // Field names should not contain underscore - // Logs data internal const int LogsData_Resource_Logs = 1; @@ -63,30 +65,7 @@ internal static class ProtobufOtlpLogFieldNumberConstants internal const int Severity_Number_Fatal4 = 24; // LogRecordFlags - internal const int LogRecord_Flags_Do_Not_Use = 0; internal const int LogRecord_Flags_Trace_Flags_Mask = 0x000000FF; - - // InstrumentationScope - internal const int InstrumentationScope_Name = 1; - internal const int InstrumentationScope_Version = 2; - internal const int InstrumentationScope_Attributes = 3; - internal const int InstrumentationScope_Dropped_Attributes_Count = 4; - - // KeyValue - internal const int KeyValue_Key = 1; - internal const int KeyValue_Value = 2; - - // AnyValue - internal const int AnyValue_String_Value = 1; - internal const int AnyValue_Bool_Value = 2; - internal const int AnyValue_Int_Value = 3; - internal const int AnyValue_Double_Value = 4; - internal const int AnyValue_Array_Value = 5; - internal const int AnyValue_Kvlist_Value = 6; - internal const int AnyValue_Bytes_Value = 7; - - internal const int ArrayValue_Value = 1; -#pragma warning restore SA1310 // Field names should not contain underscore } diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpLogSerializer.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpLogSerializer.cs index f2e9895a6a9..0f8acfab5a2 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpLogSerializer.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpLogSerializer.cs @@ -113,7 +113,7 @@ internal static int WriteScopeLog(byte[] buffer, int writePosition, SdkLimitOpti // numberOfUtf8CharsInString + tagSize + length field size. writePosition = ProtobufSerializer.WriteTagAndLength(buffer, writePosition, numberOfUtf8CharsInString + 1 + serializedLengthSize, ProtobufOtlpLogFieldNumberConstants.ScopeLogs_Scope, ProtobufWireType.LEN); - writePosition = ProtobufSerializer.WriteStringWithTag(buffer, writePosition, ProtobufOtlpLogFieldNumberConstants.InstrumentationScope_Name, numberOfUtf8CharsInString, value); + writePosition = ProtobufSerializer.WriteStringWithTag(buffer, writePosition, ProtobufOtlpCommonFieldNumberConstants.InstrumentationScope_Name, numberOfUtf8CharsInString, value); for (int i = 0; i < logRecords.Count; i++) { @@ -279,7 +279,7 @@ private static int WriteLogRecordBody(byte[] buffer, int writePosition, ReadOnly // length = numberOfUtf8CharsInString + tagSize + length field size. writePosition = ProtobufSerializer.WriteTagAndLength(buffer, writePosition, numberOfUtf8CharsInString + 1 + serializedLengthSize, ProtobufOtlpLogFieldNumberConstants.LogRecord_Body, ProtobufWireType.LEN); - writePosition = ProtobufSerializer.WriteStringWithTag(buffer, writePosition, ProtobufOtlpTraceFieldNumberConstants.AnyValue_String_Value, numberOfUtf8CharsInString, value); + writePosition = ProtobufSerializer.WriteStringWithTag(buffer, writePosition, ProtobufOtlpCommonFieldNumberConstants.AnyValue_String_Value, numberOfUtf8CharsInString, value); return writePosition; } diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpMetricFieldNumberConstants.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpMetricFieldNumberConstants.cs index 49612492f3f..a416f6a985f 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpMetricFieldNumberConstants.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpMetricFieldNumberConstants.cs @@ -3,13 +3,10 @@ namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.Serializer; -using System.Diagnostics.CodeAnalysis; - /// /// Defines field number constants for fields defined in /// . /// -[SuppressMessage("StyleCop.CSharp.NamingRules", "SA1310:Field names should not contain underscore", Justification = "These names describe the nested names and properties in the .Proto file.")] internal static class ProtobufOtlpMetricFieldNumberConstants { // Metrics Data @@ -126,28 +123,4 @@ internal static class ProtobufOtlpMetricFieldNumberConstants internal const int Exemplar_Value_As_Int = 6; internal const int Exemplar_Span_Id = 4; internal const int Exemplar_Trace_Id = 5; - - // ---------- COMMON FIELDS BELOW THIS LINE ---------- - // TODO: THESE SHOULD BE MOVED TO A SEPARATE FILE. These are currently copied in each Constants file. - - // InstrumentationScope - internal const int InstrumentationScope_Name = 1; - internal const int InstrumentationScope_Version = 2; - internal const int InstrumentationScope_Attributes = 3; - internal const int InstrumentationScope_Dropped_Attributes_Count = 4; - - // KeyValue - internal const int KeyValue_Key = 1; - internal const int KeyValue_Value = 2; - - // AnyValue - internal const int AnyValue_String_Value = 1; - internal const int AnyValue_Bool_Value = 2; - internal const int AnyValue_Int_Value = 3; - internal const int AnyValue_Double_Value = 4; - internal const int AnyValue_Array_Value = 5; - internal const int AnyValue_Kvlist_Value = 6; - internal const int AnyValue_Bytes_Value = 7; - - internal const int ArrayValue_Value = 1; } diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpMetricSerializer.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpMetricSerializer.cs index e729539d802..a16f4bf66c5 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpMetricSerializer.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpMetricSerializer.cs @@ -93,10 +93,10 @@ private static int WriteScopeMetric(byte[] buffer, int writePosition, string met var meterVersion = metrics[0].MeterVersion; var meterTags = metrics[0].MeterTags; - writePosition = ProtobufSerializer.WriteStringWithTag(buffer, writePosition, ProtobufOtlpMetricFieldNumberConstants.InstrumentationScope_Name, meterName); + writePosition = ProtobufSerializer.WriteStringWithTag(buffer, writePosition, ProtobufOtlpCommonFieldNumberConstants.InstrumentationScope_Name, meterName); if (meterVersion != null) { - writePosition = ProtobufSerializer.WriteStringWithTag(buffer, writePosition, ProtobufOtlpMetricFieldNumberConstants.InstrumentationScope_Version, meterVersion); + writePosition = ProtobufSerializer.WriteStringWithTag(buffer, writePosition, ProtobufOtlpCommonFieldNumberConstants.InstrumentationScope_Version, meterVersion); } if (meterTags != null) @@ -105,14 +105,14 @@ private static int WriteScopeMetric(byte[] buffer, int writePosition, string met { for (int i = 0; i < readonlyMeterTags.Count; i++) { - writePosition = WriteTag(buffer, writePosition, readonlyMeterTags[i], ProtobufOtlpMetricFieldNumberConstants.InstrumentationScope_Attributes); + writePosition = WriteTag(buffer, writePosition, readonlyMeterTags[i], ProtobufOtlpCommonFieldNumberConstants.InstrumentationScope_Attributes); } } else { foreach (var tag in meterTags) { - writePosition = WriteTag(buffer, writePosition, tag, ProtobufOtlpMetricFieldNumberConstants.InstrumentationScope_Attributes); + writePosition = WriteTag(buffer, writePosition, tag, ProtobufOtlpCommonFieldNumberConstants.InstrumentationScope_Attributes); } } } diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpTagWriter.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpTagWriter.cs index 79b3213a1cb..14643dc6676 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpTagWriter.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpTagWriter.cs @@ -17,60 +17,60 @@ private ProtobufOtlpTagWriter() protected override void WriteIntegralTag(ref OtlpTagWriterState state, string key, long value) { // Write KeyValue tag - state.WritePosition = ProtobufSerializer.WriteStringWithTag(state.Buffer, state.WritePosition, ProtobufOtlpTraceFieldNumberConstants.KeyValue_Key, key); + state.WritePosition = ProtobufSerializer.WriteStringWithTag(state.Buffer, state.WritePosition, ProtobufOtlpCommonFieldNumberConstants.KeyValue_Key, key); // Write KeyValue.Value tag, length and value. var size = ProtobufSerializer.ComputeVarInt64Size((ulong)value) + 1; // ComputeVarint64Size(ulong) + TagSize - state.WritePosition = ProtobufSerializer.WriteTagAndLength(state.Buffer, state.WritePosition, size, ProtobufOtlpTraceFieldNumberConstants.KeyValue_Value, ProtobufWireType.LEN); - state.WritePosition = ProtobufSerializer.WriteInt64WithTag(state.Buffer, state.WritePosition, ProtobufOtlpTraceFieldNumberConstants.AnyValue_Int_Value, (ulong)value); + state.WritePosition = ProtobufSerializer.WriteTagAndLength(state.Buffer, state.WritePosition, size, ProtobufOtlpCommonFieldNumberConstants.KeyValue_Value, ProtobufWireType.LEN); + state.WritePosition = ProtobufSerializer.WriteInt64WithTag(state.Buffer, state.WritePosition, ProtobufOtlpCommonFieldNumberConstants.AnyValue_Int_Value, (ulong)value); } protected override void WriteFloatingPointTag(ref OtlpTagWriterState state, string key, double value) { // Write KeyValue tag - state.WritePosition = ProtobufSerializer.WriteStringWithTag(state.Buffer, state.WritePosition, ProtobufOtlpTraceFieldNumberConstants.KeyValue_Key, key); + state.WritePosition = ProtobufSerializer.WriteStringWithTag(state.Buffer, state.WritePosition, ProtobufOtlpCommonFieldNumberConstants.KeyValue_Key, key); // Write KeyValue.Value tag, length and value. - state.WritePosition = ProtobufSerializer.WriteTagAndLength(state.Buffer, state.WritePosition, 9, ProtobufOtlpTraceFieldNumberConstants.KeyValue_Value, ProtobufWireType.LEN); // 8 + TagSize - state.WritePosition = ProtobufSerializer.WriteDoubleWithTag(state.Buffer, state.WritePosition, ProtobufOtlpTraceFieldNumberConstants.AnyValue_Double_Value, value); + state.WritePosition = ProtobufSerializer.WriteTagAndLength(state.Buffer, state.WritePosition, 9, ProtobufOtlpCommonFieldNumberConstants.KeyValue_Value, ProtobufWireType.LEN); // 8 + TagSize + state.WritePosition = ProtobufSerializer.WriteDoubleWithTag(state.Buffer, state.WritePosition, ProtobufOtlpCommonFieldNumberConstants.AnyValue_Double_Value, value); } protected override void WriteBooleanTag(ref OtlpTagWriterState state, string key, bool value) { // Write KeyValue tag - state.WritePosition = ProtobufSerializer.WriteStringWithTag(state.Buffer, state.WritePosition, ProtobufOtlpTraceFieldNumberConstants.KeyValue_Key, key); + state.WritePosition = ProtobufSerializer.WriteStringWithTag(state.Buffer, state.WritePosition, ProtobufOtlpCommonFieldNumberConstants.KeyValue_Key, key); // Write KeyValue.Value tag, length and value. - state.WritePosition = ProtobufSerializer.WriteTagAndLength(state.Buffer, state.WritePosition, 2, ProtobufOtlpTraceFieldNumberConstants.KeyValue_Value, ProtobufWireType.LEN); // 1 + TagSize - state.WritePosition = ProtobufSerializer.WriteBoolWithTag(state.Buffer, state.WritePosition, ProtobufOtlpTraceFieldNumberConstants.AnyValue_Bool_Value, value); + state.WritePosition = ProtobufSerializer.WriteTagAndLength(state.Buffer, state.WritePosition, 2, ProtobufOtlpCommonFieldNumberConstants.KeyValue_Value, ProtobufWireType.LEN); // 1 + TagSize + state.WritePosition = ProtobufSerializer.WriteBoolWithTag(state.Buffer, state.WritePosition, ProtobufOtlpCommonFieldNumberConstants.AnyValue_Bool_Value, value); } protected override void WriteStringTag(ref OtlpTagWriterState state, string key, ReadOnlySpan value) { // Write KeyValue tag - state.WritePosition = ProtobufSerializer.WriteStringWithTag(state.Buffer, state.WritePosition, ProtobufOtlpTraceFieldNumberConstants.KeyValue_Key, key); + state.WritePosition = ProtobufSerializer.WriteStringWithTag(state.Buffer, state.WritePosition, ProtobufOtlpCommonFieldNumberConstants.KeyValue_Key, key); // Write KeyValue.Value tag, length and value. var numberOfUtf8CharsInString = ProtobufSerializer.GetNumberOfUtf8CharsInString(value); var serializedLengthSize = ProtobufSerializer.ComputeVarInt64Size((ulong)numberOfUtf8CharsInString); // length = numberOfUtf8CharsInString + tagSize + length field size. - state.WritePosition = ProtobufSerializer.WriteTagAndLength(state.Buffer, state.WritePosition, numberOfUtf8CharsInString + 1 + serializedLengthSize, ProtobufOtlpTraceFieldNumberConstants.KeyValue_Value, ProtobufWireType.LEN); - state.WritePosition = ProtobufSerializer.WriteStringWithTag(state.Buffer, state.WritePosition, ProtobufOtlpTraceFieldNumberConstants.AnyValue_String_Value, numberOfUtf8CharsInString, value); + state.WritePosition = ProtobufSerializer.WriteTagAndLength(state.Buffer, state.WritePosition, numberOfUtf8CharsInString + 1 + serializedLengthSize, ProtobufOtlpCommonFieldNumberConstants.KeyValue_Value, ProtobufWireType.LEN); + state.WritePosition = ProtobufSerializer.WriteStringWithTag(state.Buffer, state.WritePosition, ProtobufOtlpCommonFieldNumberConstants.AnyValue_String_Value, numberOfUtf8CharsInString, value); } protected override void WriteArrayTag(ref OtlpTagWriterState state, string key, ref OtlpTagWriterArrayState value) { // TODO: Expand OtlpTagWriterArrayState.Buffer on IndexOutOfRangeException. // Write KeyValue tag - state.WritePosition = ProtobufSerializer.WriteStringWithTag(state.Buffer, state.WritePosition, ProtobufOtlpTraceFieldNumberConstants.KeyValue_Key, key); + state.WritePosition = ProtobufSerializer.WriteStringWithTag(state.Buffer, state.WritePosition, ProtobufOtlpCommonFieldNumberConstants.KeyValue_Key, key); // Write KeyValue.Value tag and length var serializedLengthSize = ProtobufSerializer.ComputeVarInt64Size((ulong)value.WritePosition); - state.WritePosition = ProtobufSerializer.WriteTagAndLength(state.Buffer, state.WritePosition, value.WritePosition + 1 + serializedLengthSize, ProtobufOtlpTraceFieldNumberConstants.KeyValue_Value, ProtobufWireType.LEN); // Array content length + Array tag size + length field size + state.WritePosition = ProtobufSerializer.WriteTagAndLength(state.Buffer, state.WritePosition, value.WritePosition + 1 + serializedLengthSize, ProtobufOtlpCommonFieldNumberConstants.KeyValue_Value, ProtobufWireType.LEN); // Array content length + Array tag size + length field size // Write Array tag and length - state.WritePosition = ProtobufSerializer.WriteTagAndLength(state.Buffer, state.WritePosition, value.WritePosition, ProtobufOtlpTraceFieldNumberConstants.AnyValue_Array_Value, ProtobufWireType.LEN); + state.WritePosition = ProtobufSerializer.WriteTagAndLength(state.Buffer, state.WritePosition, value.WritePosition, ProtobufOtlpCommonFieldNumberConstants.AnyValue_Array_Value, ProtobufWireType.LEN); Buffer.BlockCopy(value.Buffer, 0, state.Buffer, state.WritePosition, value.WritePosition); state.WritePosition += value.WritePosition; } @@ -113,26 +113,26 @@ public override OtlpTagWriterArrayState BeginWriteArray() public override void WriteNullValue(ref OtlpTagWriterArrayState state) { - state.WritePosition = ProtobufSerializer.WriteTagAndLength(state.Buffer, state.WritePosition, 0, ProtobufOtlpTraceFieldNumberConstants.ArrayValue_Value, ProtobufWireType.LEN); + state.WritePosition = ProtobufSerializer.WriteTagAndLength(state.Buffer, state.WritePosition, 0, ProtobufOtlpCommonFieldNumberConstants.ArrayValue_Value, ProtobufWireType.LEN); } public override void WriteIntegralValue(ref OtlpTagWriterArrayState state, long value) { var size = ProtobufSerializer.ComputeVarInt64Size((ulong)value) + 1; - state.WritePosition = ProtobufSerializer.WriteTagAndLength(state.Buffer, state.WritePosition, size, ProtobufOtlpTraceFieldNumberConstants.ArrayValue_Value, ProtobufWireType.LEN); - state.WritePosition = ProtobufSerializer.WriteInt64WithTag(state.Buffer, state.WritePosition, ProtobufOtlpTraceFieldNumberConstants.AnyValue_Int_Value, (ulong)value); + state.WritePosition = ProtobufSerializer.WriteTagAndLength(state.Buffer, state.WritePosition, size, ProtobufOtlpCommonFieldNumberConstants.ArrayValue_Value, ProtobufWireType.LEN); + state.WritePosition = ProtobufSerializer.WriteInt64WithTag(state.Buffer, state.WritePosition, ProtobufOtlpCommonFieldNumberConstants.AnyValue_Int_Value, (ulong)value); } public override void WriteFloatingPointValue(ref OtlpTagWriterArrayState state, double value) { - state.WritePosition = ProtobufSerializer.WriteTagAndLength(state.Buffer, state.WritePosition, 9, ProtobufOtlpTraceFieldNumberConstants.ArrayValue_Value, ProtobufWireType.LEN); - state.WritePosition = ProtobufSerializer.WriteDoubleWithTag(state.Buffer, state.WritePosition, ProtobufOtlpTraceFieldNumberConstants.AnyValue_Double_Value, value); + state.WritePosition = ProtobufSerializer.WriteTagAndLength(state.Buffer, state.WritePosition, 9, ProtobufOtlpCommonFieldNumberConstants.ArrayValue_Value, ProtobufWireType.LEN); + state.WritePosition = ProtobufSerializer.WriteDoubleWithTag(state.Buffer, state.WritePosition, ProtobufOtlpCommonFieldNumberConstants.AnyValue_Double_Value, value); } public override void WriteBooleanValue(ref OtlpTagWriterArrayState state, bool value) { - state.WritePosition = ProtobufSerializer.WriteTagAndLength(state.Buffer, state.WritePosition, 2, ProtobufOtlpTraceFieldNumberConstants.ArrayValue_Value, ProtobufWireType.LEN); - state.WritePosition = ProtobufSerializer.WriteBoolWithTag(state.Buffer, state.WritePosition, ProtobufOtlpTraceFieldNumberConstants.AnyValue_Bool_Value, value); + state.WritePosition = ProtobufSerializer.WriteTagAndLength(state.Buffer, state.WritePosition, 2, ProtobufOtlpCommonFieldNumberConstants.ArrayValue_Value, ProtobufWireType.LEN); + state.WritePosition = ProtobufSerializer.WriteBoolWithTag(state.Buffer, state.WritePosition, ProtobufOtlpCommonFieldNumberConstants.AnyValue_Bool_Value, value); } public override void WriteStringValue(ref OtlpTagWriterArrayState state, ReadOnlySpan value) @@ -142,8 +142,8 @@ public override void WriteStringValue(ref OtlpTagWriterArrayState state, ReadOnl var serializedLengthSize = ProtobufSerializer.ComputeVarInt64Size((ulong)numberOfUtf8CharsInString); // length = numberOfUtf8CharsInString + tagSize + length field size. - state.WritePosition = ProtobufSerializer.WriteTagAndLength(state.Buffer, state.WritePosition, numberOfUtf8CharsInString + 1 + serializedLengthSize, ProtobufOtlpTraceFieldNumberConstants.ArrayValue_Value, ProtobufWireType.LEN); - state.WritePosition = ProtobufSerializer.WriteStringWithTag(state.Buffer, state.WritePosition, ProtobufOtlpTraceFieldNumberConstants.AnyValue_String_Value, numberOfUtf8CharsInString, value); + state.WritePosition = ProtobufSerializer.WriteTagAndLength(state.Buffer, state.WritePosition, numberOfUtf8CharsInString + 1 + serializedLengthSize, ProtobufOtlpCommonFieldNumberConstants.ArrayValue_Value, ProtobufWireType.LEN); + state.WritePosition = ProtobufSerializer.WriteStringWithTag(state.Buffer, state.WritePosition, ProtobufOtlpCommonFieldNumberConstants.AnyValue_String_Value, numberOfUtf8CharsInString, value); } public override void EndWriteArray(ref OtlpTagWriterArrayState state) diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpTraceFieldNumberConstants.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpTraceFieldNumberConstants.cs index fa0b9737a68..67daf09a48c 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpTraceFieldNumberConstants.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpTraceFieldNumberConstants.cs @@ -3,10 +3,12 @@ namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.Serializer; +/// +/// Defines field number constants for fields defined in +/// . +/// internal static class ProtobufOtlpTraceFieldNumberConstants { -#pragma warning disable SA1310 // Field names should not contain underscore - // Traces data internal const int TracesData_Resource_Spans = 1; @@ -70,27 +72,5 @@ internal static class ProtobufOtlpTraceFieldNumberConstants internal const int StatusCode_Unset = 0; internal const int StatusCode_Ok = 1; internal const int StatusCode_Error = 2; - - // InstrumentationScope - internal const int InstrumentationScope_Name = 1; - internal const int InstrumentationScope_Version = 2; - internal const int InstrumentationScope_Attributes = 3; - internal const int InstrumentationScope_Dropped_Attributes_Count = 4; - - // KeyValue - internal const int KeyValue_Key = 1; - internal const int KeyValue_Value = 2; - - // AnyValue - internal const int AnyValue_String_Value = 1; - internal const int AnyValue_Bool_Value = 2; - internal const int AnyValue_Int_Value = 3; - internal const int AnyValue_Double_Value = 4; - internal const int AnyValue_Array_Value = 5; - internal const int AnyValue_Kvlist_Value = 6; - internal const int AnyValue_Bytes_Value = 7; - - internal const int ArrayValue_Value = 1; -#pragma warning restore SA1310 // Field names should not contain underscore } diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpTraceSerializer.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpTraceSerializer.cs index 7db78213b07..4e77ca8b0fa 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpTraceSerializer.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpTraceSerializer.cs @@ -117,10 +117,10 @@ internal static int WriteScopeSpan(byte[] buffer, int writePosition, SdkLimitOpt int instrumentationScopeLengthPosition = writePosition; writePosition += ReserveSizeForLength; - writePosition = ProtobufSerializer.WriteStringWithTag(buffer, writePosition, ProtobufOtlpTraceFieldNumberConstants.InstrumentationScope_Name, activitySource.Name); + writePosition = ProtobufSerializer.WriteStringWithTag(buffer, writePosition, ProtobufOtlpCommonFieldNumberConstants.InstrumentationScope_Name, activitySource.Name); if (activitySource.Version != null) { - writePosition = ProtobufSerializer.WriteStringWithTag(buffer, writePosition, ProtobufOtlpTraceFieldNumberConstants.InstrumentationScope_Version, activitySource.Version); + writePosition = ProtobufSerializer.WriteStringWithTag(buffer, writePosition, ProtobufOtlpCommonFieldNumberConstants.InstrumentationScope_Version, activitySource.Version); } if (activitySource.Tags != null) @@ -141,7 +141,7 @@ internal static int WriteScopeSpan(byte[] buffer, int writePosition, SdkLimitOpt { if (otlpTagWriterState.TagCount < maxAttributeCount) { - otlpTagWriterState.WritePosition = ProtobufSerializer.WriteTag(otlpTagWriterState.Buffer, otlpTagWriterState.WritePosition, ProtobufOtlpTraceFieldNumberConstants.InstrumentationScope_Attributes, ProtobufWireType.LEN); + otlpTagWriterState.WritePosition = ProtobufSerializer.WriteTag(otlpTagWriterState.Buffer, otlpTagWriterState.WritePosition, ProtobufOtlpCommonFieldNumberConstants.InstrumentationScope_Attributes, ProtobufWireType.LEN); int instrumentationScopeAttributesLengthPosition = otlpTagWriterState.WritePosition; otlpTagWriterState.WritePosition += ReserveSizeForLength; @@ -163,7 +163,7 @@ internal static int WriteScopeSpan(byte[] buffer, int writePosition, SdkLimitOpt { if (otlpTagWriterState.TagCount < maxAttributeCount) { - otlpTagWriterState.WritePosition = ProtobufSerializer.WriteTag(otlpTagWriterState.Buffer, otlpTagWriterState.WritePosition, ProtobufOtlpTraceFieldNumberConstants.InstrumentationScope_Attributes, ProtobufWireType.LEN); + otlpTagWriterState.WritePosition = ProtobufSerializer.WriteTag(otlpTagWriterState.Buffer, otlpTagWriterState.WritePosition, ProtobufOtlpCommonFieldNumberConstants.InstrumentationScope_Attributes, ProtobufWireType.LEN); int instrumentationScopeAttributesLengthPosition = otlpTagWriterState.WritePosition; otlpTagWriterState.WritePosition += ReserveSizeForLength; @@ -182,7 +182,7 @@ internal static int WriteScopeSpan(byte[] buffer, int writePosition, SdkLimitOpt if (otlpTagWriterState.DroppedTagCount > 0) { - otlpTagWriterState.WritePosition = ProtobufSerializer.WriteTag(buffer, otlpTagWriterState.WritePosition, ProtobufOtlpTraceFieldNumberConstants.InstrumentationScope_Dropped_Attributes_Count, ProtobufWireType.VARINT); + otlpTagWriterState.WritePosition = ProtobufSerializer.WriteTag(buffer, otlpTagWriterState.WritePosition, ProtobufOtlpCommonFieldNumberConstants.InstrumentationScope_Dropped_Attributes_Count, ProtobufWireType.VARINT); otlpTagWriterState.WritePosition = ProtobufSerializer.WriteVarInt32(buffer, otlpTagWriterState.WritePosition, (uint)otlpTagWriterState.DroppedTagCount); } From 305597d9d542417c23a547643ef595eabf00b26f Mon Sep 17 00:00:00 2001 From: Rajkumar Rangaraj Date: Thu, 5 Dec 2024 11:04:12 -0800 Subject: [PATCH 4/7] [otlp] Fix TODOs, Refactor Buffer Size Handling, and Cleanup Environment Variables (#6009) Co-authored-by: Mikel Blanchard --- ...nTelemetryBuilderOtlpExporterExtensions.cs | 4 +-- .../Implementation/ExperimentalOptions.cs | 12 ------- .../Grpc/GrpcStatusDeserializer.cs | 4 +-- ...penTelemetryProtocolExporterEventSource.cs | 15 +++++++++ .../Serializer/ProtobufOtlpLogSerializer.cs | 21 +++++++++++- .../ProtobufOtlpMetricSerializer.cs | 21 +++++++++++- .../Serializer/ProtobufOtlpTraceSerializer.cs | 4 --- .../OtlpExporterOptionsExtensions.cs | 6 ++-- .../OtlpLogExporter.cs | 33 +++---------------- .../OtlpMetricExporter.cs | 33 +++---------------- .../OtlpTraceExporter.cs | 9 ++--- .../MockCollectorIntegrationTests.cs | 20 ++--------- .../OtlpExporterOptionsExtensionsTests.cs | 6 ++-- 13 files changed, 82 insertions(+), 106 deletions(-) diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderOtlpExporterExtensions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderOtlpExporterExtensions.cs index 6b1d2afeba0..e0e999b3e72 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderOtlpExporterExtensions.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Builder/OpenTelemetryBuilderOtlpExporterExtensions.cs @@ -88,8 +88,8 @@ internal static IOpenTelemetryBuilder UseOtlpExporter( /// to bind onto . /// Notes: /// - /// See [TODO:Add doc link] for details on the configuration - /// schema. + /// + /// for details on the configuration schema. /// The instance will be /// named "otlp" by default when calling this method. /// diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExperimentalOptions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExperimentalOptions.cs index f86743325db..25b345ac96e 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExperimentalOptions.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExperimentalOptions.cs @@ -17,8 +17,6 @@ internal sealed class ExperimentalOptions public const string OtlpDiskRetryDirectoryPathEnvVar = "OTEL_DOTNET_EXPERIMENTAL_OTLP_DISK_RETRY_DIRECTORY_PATH"; - public const string OtlpUseCustomSerializer = "OTEL_DOTNET_EXPERIMENTAL_USE_CUSTOM_PROTOBUF_SERIALIZER"; - public ExperimentalOptions() : this(new ConfigurationBuilder().AddEnvironmentVariables().Build()) { @@ -31,11 +29,6 @@ public ExperimentalOptions(IConfiguration configuration) this.EmitLogEventAttributes = emitLogEventAttributes; } - if (configuration.TryGetBoolValue(OpenTelemetryProtocolExporterEventSource.Log, OtlpUseCustomSerializer, out var useCustomSerializer)) - { - this.UseCustomProtobufSerializer = useCustomSerializer; - } - if (configuration.TryGetStringValue(OtlpRetryEnvVar, out var retryPolicy) && retryPolicy != null) { if (retryPolicy.Equals("in_memory", StringComparison.OrdinalIgnoreCase)) @@ -85,9 +78,4 @@ public ExperimentalOptions(IConfiguration configuration) /// Gets the path on disk where the telemetry will be stored for retries at a later point. /// public string? DiskRetryDirectoryPath { get; } - - /// - /// Gets a value indicating whether custom serializer should be used for OTLP export. - /// - public bool UseCustomProtobufSerializer { get; } } diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/Grpc/GrpcStatusDeserializer.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/Grpc/GrpcStatusDeserializer.cs index 1df7edd6d58..57efec46b89 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/Grpc/GrpcStatusDeserializer.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/Grpc/GrpcStatusDeserializer.cs @@ -26,9 +26,9 @@ internal static class GrpcStatusDeserializer TimeSpan.FromTicks(retryInfo.Value.RetryDelay.Value.Nanos / 100); // Convert nanos to ticks } } - catch + catch (Exception ex) { - // TODO: Log exception to event source. + OpenTelemetryProtocolExporterEventSource.Log.GrpcRetryDelayParsingFailed(grpcStatusDetailsHeader, ex); return null; } diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/OpenTelemetryProtocolExporterEventSource.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/OpenTelemetryProtocolExporterEventSource.cs index e9544cf981e..ac3db61f61e 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/OpenTelemetryProtocolExporterEventSource.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/OpenTelemetryProtocolExporterEventSource.cs @@ -85,6 +85,15 @@ public void RequestTimedOut(Uri endpoint, Exception ex) } } + [NonEvent] + public void GrpcRetryDelayParsingFailed(string? grpcStatusDetailsHeader, Exception ex) + { + if (Log.IsEnabled(EventLevel.Warning, EventKeywords.All)) + { + this.GrpcRetryDelayParsingFailed(grpcStatusDetailsHeader ?? "null", ex.ToInvariantString()); + } + } + [Event(2, Message = "Exporter failed send data to collector to {0} endpoint. Data will not be sent. Exception: {1}", Level = EventLevel.Error)] public void FailedToReachCollector(string rawCollectorUri, string ex) { @@ -205,6 +214,12 @@ public void ExportFailure(string endpoint, string message) this.WriteEvent(23, endpoint, message); } + [Event(24, Message = "Failed to parse gRPC retry delay from header grpcStatusDetailsHeader: '{0}'. Exception: {1}", Level = EventLevel.Warning)] + public void GrpcRetryDelayParsingFailed(string grpcStatusDetailsHeader, string exception) + { + this.WriteEvent(24, grpcStatusDetailsHeader, exception); + } + void IConfigurationExtensionsLogger.LogInvalidConfigurationValue(string key, string value) { this.InvalidConfigurationValue(key, value); diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpLogSerializer.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpLogSerializer.cs index 0f8acfab5a2..8330d7b2e34 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpLogSerializer.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpLogSerializer.cs @@ -47,13 +47,32 @@ internal static int WriteLogsData(byte[] buffer, int writePosition, SdkLimitOpti logRecords.Add(logRecord); } - writePosition = WriteResourceLogs(buffer, writePosition, sdkLimitOptions, experimentalOptions, resource, ScopeLogsList); + writePosition = TryWriteResourceLogs(buffer, writePosition, sdkLimitOptions, experimentalOptions, resource, ScopeLogsList); ProtobufSerializer.WriteReservedLength(buffer, logsDataLengthPosition, writePosition - (logsDataLengthPosition + ReserveSizeForLength)); ReturnLogRecordListToPool(); return writePosition; } + internal static int TryWriteResourceLogs(byte[] buffer, int writePosition, SdkLimitOptions sdkLimitOptions, ExperimentalOptions experimentalOptions, Resources.Resource? resource, Dictionary> scopeLogs) + { + try + { + writePosition = WriteResourceLogs(buffer, writePosition, sdkLimitOptions, experimentalOptions, resource, scopeLogs); + } + catch (IndexOutOfRangeException) + { + if (!ProtobufSerializer.IncreaseBufferSize(ref buffer, OtlpSignalType.Logs)) + { + throw; + } + + return TryWriteResourceLogs(buffer, writePosition, sdkLimitOptions, experimentalOptions, resource, scopeLogs); + } + + return writePosition; + } + internal static void ReturnLogRecordListToPool() { if (ScopeLogsList.Count != 0) diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpMetricSerializer.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpMetricSerializer.cs index a16f4bf66c5..cb85aefe927 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpMetricSerializer.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpMetricSerializer.cs @@ -35,13 +35,32 @@ internal static int WriteMetricsData(byte[] buffer, int writePosition, Resources metrics.Add(metric); } - writePosition = WriteResourceMetrics(buffer, writePosition, resource, ScopeMetricsList); + writePosition = TryWriteResourceMetrics(buffer, writePosition, resource, ScopeMetricsList); ProtobufSerializer.WriteReservedLength(buffer, mericsDataLengthPosition, writePosition - (mericsDataLengthPosition + ReserveSizeForLength)); ReturnMetricListToPool(); return writePosition; } + internal static int TryWriteResourceMetrics(byte[] buffer, int writePosition, Resources.Resource? resource, Dictionary> scopeMetrics) + { + try + { + writePosition = WriteResourceMetrics(buffer, writePosition, resource, scopeMetrics); + } + catch (IndexOutOfRangeException) + { + if (!ProtobufSerializer.IncreaseBufferSize(ref buffer, OtlpSignalType.Metrics)) + { + throw; + } + + return TryWriteResourceMetrics(buffer, writePosition, resource, scopeMetrics); + } + + return writePosition; + } + private static void ReturnMetricListToPool() { if (ScopeMetricsList.Count != 0) diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpTraceSerializer.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpTraceSerializer.cs index 4e77ca8b0fa..b50acbe7c89 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpTraceSerializer.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpTraceSerializer.cs @@ -63,10 +63,6 @@ internal static int TryWriteResourceSpans(byte[] buffer, int writePosition, SdkL // and avoids stack overflow. return TryWriteResourceSpans(buffer, writePosition, sdkLimitOptions, resource); } - catch - { - throw; - } return writePosition; } diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterOptionsExtensions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterOptionsExtensions.cs index fabb5a23392..9db0404a63a 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterOptionsExtensions.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterOptionsExtensions.cs @@ -58,9 +58,9 @@ public static THeaders GetHeaders(this OtlpExporterOptions options, Ac return headers; } - public static OtlpExporterTransmissionHandler GetProtobufExportTransmissionHandler(this OtlpExporterOptions options, ExperimentalOptions experimentalOptions, OtlpSignalType otlpSignalType) + public static OtlpExporterTransmissionHandler GetExportTransmissionHandler(this OtlpExporterOptions options, ExperimentalOptions experimentalOptions, OtlpSignalType otlpSignalType) { - var exportClient = GetProtobufExportClient(options, otlpSignalType); + var exportClient = GetExportClient(options, otlpSignalType); // `HttpClient.Timeout.TotalMilliseconds` would be populated with the correct timeout value for both the exporter configuration cases: // 1. User provides their own HttpClient. This case is straightforward as the user wants to use their `HttpClient` and thereby the same client's timeout value. @@ -88,7 +88,7 @@ public static OtlpExporterTransmissionHandler GetProtobufExportTransmissionHandl } } - public static IExportClient GetProtobufExportClient(this OtlpExporterOptions options, OtlpSignalType otlpSignalType) + public static IExportClient GetExportClient(this OtlpExporterOptions options, OtlpSignalType otlpSignalType) { var httpClient = options.HttpClientFactory?.Invoke() ?? throw new InvalidOperationException("OtlpExporterOptions was missing HttpClientFactory or it returned null."); diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpLogExporter.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpLogExporter.cs index 97d07468311..d78faaa84ba 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpLogExporter.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpLogExporter.cs @@ -17,6 +17,7 @@ namespace OpenTelemetry.Exporter; /// public sealed class OtlpLogExporter : BaseExporter { + private const int GrpcStartWritePosition = 5; private readonly SdkLimitOptions sdkLimitOptions; private readonly ExperimentalOptions experimentalOptions; private readonly OtlpExporterTransmissionHandler transmissionHandler; @@ -57,8 +58,8 @@ internal OtlpLogExporter( this.experimentalOptions = experimentalOptions!; this.sdkLimitOptions = sdkLimitOptions!; - this.startWritePosition = exporterOptions!.Protocol == OtlpExportProtocol.Grpc ? 5 : 0; - this.transmissionHandler = transmissionHandler ?? exporterOptions!.GetProtobufExportTransmissionHandler(experimentalOptions!, OtlpSignalType.Logs); + this.startWritePosition = exporterOptions!.Protocol == OtlpExportProtocol.Grpc ? GrpcStartWritePosition : 0; + this.transmissionHandler = transmissionHandler ?? exporterOptions!.GetExportTransmissionHandler(experimentalOptions!, OtlpSignalType.Logs); } internal Resource Resource => this.resource ??= this.ParentProvider.GetResource(); @@ -73,14 +74,14 @@ public override ExportResult Export(in Batch logRecordBatch) { int writePosition = ProtobufOtlpLogSerializer.WriteLogsData(this.buffer, this.startWritePosition, this.sdkLimitOptions, this.experimentalOptions, this.Resource, logRecordBatch); - if (this.startWritePosition == 5) + if (this.startWritePosition == GrpcStartWritePosition) { // Grpc payload consists of 3 parts // byte 0 - Specifying if the payload is compressed. // 1-4 byte - Specifies the length of payload in big endian format. // 5 and above - Protobuf serialized data. Span data = new Span(this.buffer, 1, 4); - var dataLength = writePosition - 5; + var dataLength = writePosition - GrpcStartWritePosition; BinaryPrimitives.WriteUInt32BigEndian(data, (uint)dataLength); } @@ -89,13 +90,6 @@ public override ExportResult Export(in Batch logRecordBatch) return ExportResult.Failure; } } - catch (IndexOutOfRangeException) - { - if (!this.IncreaseBufferSize()) - { - throw; - } - } catch (Exception ex) { OpenTelemetryProtocolExporterEventSource.Log.ExportMethodException(ex); @@ -107,21 +101,4 @@ public override ExportResult Export(in Batch logRecordBatch) /// protected override bool OnShutdown(int timeoutMilliseconds) => this.transmissionHandler?.Shutdown(timeoutMilliseconds) ?? true; - - // TODO: Consider moving this to a shared utility class. - private bool IncreaseBufferSize() - { - var newBufferSize = this.buffer.Length * 2; - - if (newBufferSize > 100 * 1024 * 1024) - { - return false; - } - - var newBuffer = new byte[newBufferSize]; - this.buffer.CopyTo(newBuffer, 0); - this.buffer = newBuffer; - - return true; - } } diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpMetricExporter.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpMetricExporter.cs index 1014c1c2edf..26beee90bef 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpMetricExporter.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpMetricExporter.cs @@ -17,6 +17,7 @@ namespace OpenTelemetry.Exporter; /// public class OtlpMetricExporter : BaseExporter { + private const int GrpcStartWritePosition = 5; private readonly OtlpExporterTransmissionHandler transmissionHandler; private readonly int startWritePosition; @@ -50,8 +51,8 @@ internal OtlpMetricExporter( Debug.Assert(exporterOptions != null, "exporterOptions was null"); Debug.Assert(experimentalOptions != null, "experimentalOptions was null"); - this.startWritePosition = exporterOptions!.Protocol == OtlpExportProtocol.Grpc ? 5 : 0; - this.transmissionHandler = transmissionHandler ?? exporterOptions!.GetProtobufExportTransmissionHandler(experimentalOptions!, OtlpSignalType.Metrics); + this.startWritePosition = exporterOptions!.Protocol == OtlpExportProtocol.Grpc ? GrpcStartWritePosition : 0; + this.transmissionHandler = transmissionHandler ?? exporterOptions!.GetExportTransmissionHandler(experimentalOptions!, OtlpSignalType.Metrics); } internal Resource Resource => this.resource ??= this.ParentProvider.GetResource(); @@ -66,14 +67,14 @@ public override ExportResult Export(in Batch metrics) { int writePosition = ProtobufOtlpMetricSerializer.WriteMetricsData(this.buffer, this.startWritePosition, this.Resource, metrics); - if (this.startWritePosition == 5) + if (this.startWritePosition == GrpcStartWritePosition) { // Grpc payload consists of 3 parts // byte 0 - Specifying if the payload is compressed. // 1-4 byte - Specifies the length of payload in big endian format. // 5 and above - Protobuf serialized data. Span data = new Span(this.buffer, 1, 4); - var dataLength = writePosition - 5; + var dataLength = writePosition - GrpcStartWritePosition; BinaryPrimitives.WriteUInt32BigEndian(data, (uint)dataLength); } @@ -82,13 +83,6 @@ public override ExportResult Export(in Batch metrics) return ExportResult.Failure; } } - catch (IndexOutOfRangeException) - { - if (!this.IncreaseBufferSize()) - { - throw; - } - } catch (Exception ex) { OpenTelemetryProtocolExporterEventSource.Log.ExportMethodException(ex); @@ -100,21 +94,4 @@ public override ExportResult Export(in Batch metrics) /// protected override bool OnShutdown(int timeoutMilliseconds) => this.transmissionHandler.Shutdown(timeoutMilliseconds); - - // TODO: Consider moving this to a shared utility class. - private bool IncreaseBufferSize() - { - var newBufferSize = this.buffer.Length * 2; - - if (newBufferSize > 100 * 1024 * 1024) - { - return false; - } - - var newBuffer = new byte[newBufferSize]; - this.buffer.CopyTo(newBuffer, 0); - this.buffer = newBuffer; - - return true; - } } diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpTraceExporter.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpTraceExporter.cs index a9cf0eb0477..2a9aa274241 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpTraceExporter.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpTraceExporter.cs @@ -16,6 +16,7 @@ namespace OpenTelemetry.Exporter; /// public class OtlpTraceExporter : BaseExporter { + private const int GrpcStartWritePosition = 5; private readonly SdkLimitOptions sdkLimitOptions; private readonly OtlpExporterTransmissionHandler transmissionHandler; private readonly int startWritePosition; @@ -53,8 +54,8 @@ internal OtlpTraceExporter( Debug.Assert(sdkLimitOptions != null, "sdkLimitOptions was null"); this.sdkLimitOptions = sdkLimitOptions!; - this.startWritePosition = exporterOptions!.Protocol == OtlpExportProtocol.Grpc ? 5 : 0; - this.transmissionHandler = transmissionHandler ?? exporterOptions!.GetProtobufExportTransmissionHandler(experimentalOptions, OtlpSignalType.Traces); + this.startWritePosition = exporterOptions!.Protocol == OtlpExportProtocol.Grpc ? GrpcStartWritePosition : 0; + this.transmissionHandler = transmissionHandler ?? exporterOptions!.GetExportTransmissionHandler(experimentalOptions, OtlpSignalType.Traces); } internal Resource Resource => this.resource ??= this.ParentProvider.GetResource(); @@ -69,14 +70,14 @@ public override ExportResult Export(in Batch activityBatch) { int writePosition = ProtobufOtlpTraceSerializer.WriteTraceData(this.buffer, this.startWritePosition, this.sdkLimitOptions, this.Resource, activityBatch); - if (this.startWritePosition == 5) + if (this.startWritePosition == GrpcStartWritePosition) { // Grpc payload consists of 3 parts // byte 0 - Specifying if the payload is compressed. // 1-4 byte - Specifies the length of payload in big endian format. // 5 and above - Protobuf serialized data. Span data = new Span(this.buffer, 1, 4); - var dataLength = writePosition - 5; + var dataLength = writePosition - GrpcStartWritePosition; BinaryPrimitives.WriteUInt32BigEndian(data, (uint)dataLength); } diff --git a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/MockCollectorIntegrationTests.cs b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/MockCollectorIntegrationTests.cs index ab382fe728a..56e15422a8f 100644 --- a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/MockCollectorIntegrationTests.cs +++ b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/MockCollectorIntegrationTests.cs @@ -182,7 +182,6 @@ public async Task GrpcRetryTests(bool useRetryTransmissionHandler, ExportResult .AddInMemoryCollection(new Dictionary { [ExperimentalOptions.OtlpRetryEnvVar] = useRetryTransmissionHandler ? "in_memory" : null, - [ExperimentalOptions.OtlpUseCustomSerializer] = "true", }) .Build(); @@ -271,7 +270,6 @@ public async Task HttpRetryTests(bool useRetryTransmissionHandler, ExportResult .AddInMemoryCollection(new Dictionary { [ExperimentalOptions.OtlpRetryEnvVar] = useRetryTransmissionHandler ? "in_memory" : null, - [ExperimentalOptions.OtlpUseCustomSerializer] = "true", }) .Build(); @@ -372,14 +370,7 @@ public async Task HttpPersistentStorageRetryTests(bool usePersistentStorageTrans transmissionHandler = new OtlpExporterTransmissionHandler(exportClient, exporterOptions.TimeoutMilliseconds); } - var configuration = new ConfigurationBuilder() - .AddInMemoryCollection(new Dictionary - { - [ExperimentalOptions.OtlpUseCustomSerializer] = "true", - }) - .Build(); - - using var otlpExporter = new OtlpTraceExporter(exporterOptions, new(), new(configuration), transmissionHandler); + using var otlpExporter = new OtlpTraceExporter(exporterOptions, new(), new(), transmissionHandler); var activitySourceName = "otel.http.persistent.storage.retry.test"; using var source = new ActivitySource(activitySourceName); @@ -512,14 +503,7 @@ public async Task GrpcPersistentStorageRetryTests(bool usePersistentStorageTrans transmissionHandler = new OtlpExporterTransmissionHandler(exportClient, exporterOptions.TimeoutMilliseconds); } - var configuration = new ConfigurationBuilder() - .AddInMemoryCollection(new Dictionary - { - [ExperimentalOptions.OtlpUseCustomSerializer] = "true", - }) - .Build(); - - using var otlpExporter = new OtlpTraceExporter(exporterOptions, new(), new(configuration), transmissionHandler); + using var otlpExporter = new OtlpTraceExporter(exporterOptions, new(), new(), transmissionHandler); var activitySourceName = "otel.grpc.persistent.storage.retry.test"; using var source = new ActivitySource(activitySourceName); diff --git a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpExporterOptionsExtensionsTests.cs b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpExporterOptionsExtensionsTests.cs index 207a20483a4..51fc87891ef 100644 --- a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpExporterOptionsExtensionsTests.cs +++ b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpExporterOptionsExtensionsTests.cs @@ -44,7 +44,7 @@ public void GetTraceExportClient_SupportedProtocol_ReturnsCorrectExportClient(Ot Protocol = protocol, }; - var exportClient = options.GetProtobufExportClient(OtlpSignalType.Traces); + var exportClient = options.GetExportClient(OtlpSignalType.Traces); Assert.Equal(expectedExportClientType, exportClient.GetType()); } @@ -57,7 +57,7 @@ public void GetTraceExportClient_UnsupportedProtocol_Throws() Protocol = (OtlpExportProtocol)123, }; - Assert.Throws(() => options.GetProtobufExportClient(OtlpSignalType.Traces)); + Assert.Throws(() => options.GetExportClient(OtlpSignalType.Traces)); } [Theory] @@ -99,7 +99,7 @@ public void GetTransmissionHandler_InitializesCorrectHandlerExportClientAndTimeo .AddInMemoryCollection(new Dictionary { [ExperimentalOptions.OtlpRetryEnvVar] = retryStrategy }) .Build(); - var transmissionHandler = exporterOptions.GetProtobufExportTransmissionHandler(new ExperimentalOptions(configuration), OtlpSignalType.Traces); + var transmissionHandler = exporterOptions.GetExportTransmissionHandler(new ExperimentalOptions(configuration), OtlpSignalType.Traces); AssertTransmissionHandler(transmissionHandler, exportClientType, expectedTimeoutMilliseconds, retryStrategy); } From 145e7ade6f3d518280d139624951822117b60ebd Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Fri, 6 Dec 2024 09:22:05 -0800 Subject: [PATCH 5/7] [repo] Allow dependabot to bump SDK patch version (#6011) --- .github/dependabot.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 2ac5ee67846..168e352f889 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -6,3 +6,15 @@ updates: interval: "daily" labels: - "infra" + - package-ecosystem: "dotnet-sdk" + directory: "/" + schedule: + interval: "weekly" + day: "wednesday" + labels: + - "infra" + ignore: + - dependency-name: "*" + update-types: + - "version-update:semver-major" + - "version-update:semver-minor" From be8209987b4c81a4aaf62ea8e1e06a9faf342978 Mon Sep 17 00:00:00 2001 From: Rajkumar Rangaraj Date: Tue, 10 Dec 2024 12:42:08 -0800 Subject: [PATCH 6/7] [otlp] Expand array buffer / add tests to existing base buffer (#6013) Co-authored-by: Mikel Blanchard --- ...penTelemetryProtocolExporterEventSource.cs | 6 + .../Serializer/ProtobufOtlpLogSerializer.cs | 10 +- .../ProtobufOtlpMetricSerializer.cs | 10 +- .../Serializer/ProtobufOtlpTagWriter.cs | 35 ++- .../Serializer/ProtobufOtlpTraceSerializer.cs | 10 +- .../Serializer/ProtobufSerializer.cs | 7 + .../OtlpLogExporter.cs | 2 +- .../OtlpMetricExporter.cs | 2 +- .../OtlpTraceExporter.cs | 2 +- src/Shared/TagWriter/ArrayTagWriter.cs | 2 + src/Shared/TagWriter/TagWriter.cs | 56 +++- .../OtlpHttpTraceExportClientTests.cs | 2 +- .../Serializer/OtlpArrayTagWriterTests.cs | 287 ++++++++++++++++++ .../OtlpLogExporterTests.cs | 40 ++- .../OtlpMetricsExporterTests.cs | 54 +++- .../OtlpTraceExporterTests.cs | 42 ++- 16 files changed, 525 insertions(+), 42 deletions(-) create mode 100644 test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/Implementation/Serializer/OtlpArrayTagWriterTests.cs diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/OpenTelemetryProtocolExporterEventSource.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/OpenTelemetryProtocolExporterEventSource.cs index ac3db61f61e..3596b79fa28 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/OpenTelemetryProtocolExporterEventSource.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/OpenTelemetryProtocolExporterEventSource.cs @@ -220,6 +220,12 @@ public void GrpcRetryDelayParsingFailed(string grpcStatusDetailsHeader, string e this.WriteEvent(24, grpcStatusDetailsHeader, exception); } + [Event(25, Message = "The array tag buffer exceeded the maximum allowed size. The array tag value was replaced with 'TRUNCATED'", Level = EventLevel.Warning)] + public void ArrayBufferExceededMaxSize() + { + this.WriteEvent(25); + } + void IConfigurationExtensionsLogger.LogInvalidConfigurationValue(string key, string value) { this.InvalidConfigurationValue(key, value); diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpLogSerializer.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpLogSerializer.cs index 8330d7b2e34..4f29ce28fa4 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpLogSerializer.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpLogSerializer.cs @@ -20,7 +20,7 @@ internal static class ProtobufOtlpLogSerializer [ThreadStatic] private static SerializationState? threadSerializationState; - internal static int WriteLogsData(byte[] buffer, int writePosition, SdkLimitOptions sdkLimitOptions, ExperimentalOptions experimentalOptions, Resources.Resource? resource, in Batch logRecordBatch) + internal static int WriteLogsData(ref byte[] buffer, int writePosition, SdkLimitOptions sdkLimitOptions, ExperimentalOptions experimentalOptions, Resources.Resource? resource, in Batch logRecordBatch) { writePosition = ProtobufSerializer.WriteTag(buffer, writePosition, ProtobufOtlpLogFieldNumberConstants.LogsData_Resource_Logs, ProtobufWireType.LEN); int logsDataLengthPosition = writePosition; @@ -47,27 +47,27 @@ internal static int WriteLogsData(byte[] buffer, int writePosition, SdkLimitOpti logRecords.Add(logRecord); } - writePosition = TryWriteResourceLogs(buffer, writePosition, sdkLimitOptions, experimentalOptions, resource, ScopeLogsList); + writePosition = TryWriteResourceLogs(ref buffer, writePosition, sdkLimitOptions, experimentalOptions, resource, ScopeLogsList); ProtobufSerializer.WriteReservedLength(buffer, logsDataLengthPosition, writePosition - (logsDataLengthPosition + ReserveSizeForLength)); ReturnLogRecordListToPool(); return writePosition; } - internal static int TryWriteResourceLogs(byte[] buffer, int writePosition, SdkLimitOptions sdkLimitOptions, ExperimentalOptions experimentalOptions, Resources.Resource? resource, Dictionary> scopeLogs) + internal static int TryWriteResourceLogs(ref byte[] buffer, int writePosition, SdkLimitOptions sdkLimitOptions, ExperimentalOptions experimentalOptions, Resources.Resource? resource, Dictionary> scopeLogs) { try { writePosition = WriteResourceLogs(buffer, writePosition, sdkLimitOptions, experimentalOptions, resource, scopeLogs); } - catch (IndexOutOfRangeException) + catch (Exception ex) when (ex is IndexOutOfRangeException || ex is ArgumentException) { if (!ProtobufSerializer.IncreaseBufferSize(ref buffer, OtlpSignalType.Logs)) { throw; } - return TryWriteResourceLogs(buffer, writePosition, sdkLimitOptions, experimentalOptions, resource, scopeLogs); + return TryWriteResourceLogs(ref buffer, writePosition, sdkLimitOptions, experimentalOptions, resource, scopeLogs); } return writePosition; diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpMetricSerializer.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpMetricSerializer.cs index cb85aefe927..68d5c5fa115 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpMetricSerializer.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpMetricSerializer.cs @@ -17,7 +17,7 @@ internal static class ProtobufOtlpMetricSerializer private delegate int WriteExemplarFunc(byte[] buffer, int writePosition, in Exemplar exemplar); - internal static int WriteMetricsData(byte[] buffer, int writePosition, Resources.Resource? resource, in Batch batch) + internal static int WriteMetricsData(ref byte[] buffer, int writePosition, Resources.Resource? resource, in Batch batch) { writePosition = ProtobufSerializer.WriteTag(buffer, writePosition, ProtobufOtlpMetricFieldNumberConstants.MetricsData_Resource_Metrics, ProtobufWireType.LEN); int mericsDataLengthPosition = writePosition; @@ -35,27 +35,27 @@ internal static int WriteMetricsData(byte[] buffer, int writePosition, Resources metrics.Add(metric); } - writePosition = TryWriteResourceMetrics(buffer, writePosition, resource, ScopeMetricsList); + writePosition = TryWriteResourceMetrics(ref buffer, writePosition, resource, ScopeMetricsList); ProtobufSerializer.WriteReservedLength(buffer, mericsDataLengthPosition, writePosition - (mericsDataLengthPosition + ReserveSizeForLength)); ReturnMetricListToPool(); return writePosition; } - internal static int TryWriteResourceMetrics(byte[] buffer, int writePosition, Resources.Resource? resource, Dictionary> scopeMetrics) + internal static int TryWriteResourceMetrics(ref byte[] buffer, int writePosition, Resources.Resource? resource, Dictionary> scopeMetrics) { try { writePosition = WriteResourceMetrics(buffer, writePosition, resource, scopeMetrics); } - catch (IndexOutOfRangeException) + catch (Exception ex) when (ex is IndexOutOfRangeException || ex is ArgumentException) { if (!ProtobufSerializer.IncreaseBufferSize(ref buffer, OtlpSignalType.Metrics)) { throw; } - return TryWriteResourceMetrics(buffer, writePosition, resource, scopeMetrics); + return TryWriteResourceMetrics(ref buffer, writePosition, resource, scopeMetrics); } return writePosition; diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpTagWriter.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpTagWriter.cs index 14643dc6676..1002f90e7c7 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpTagWriter.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpTagWriter.cs @@ -1,6 +1,7 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 +using System.Diagnostics; using OpenTelemetry.Internal; namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.Serializer; @@ -61,7 +62,6 @@ protected override void WriteStringTag(ref OtlpTagWriterState state, string key, protected override void WriteArrayTag(ref OtlpTagWriterState state, string key, ref OtlpTagWriterArrayState value) { - // TODO: Expand OtlpTagWriterArrayState.Buffer on IndexOutOfRangeException. // Write KeyValue tag state.WritePosition = ProtobufSerializer.WriteStringWithTag(state.Buffer, state.WritePosition, ProtobufOtlpCommonFieldNumberConstants.KeyValue_Key, key); @@ -95,18 +95,19 @@ internal struct OtlpTagWriterArrayState public int WritePosition; } - private sealed class OtlpArrayTagWriter : ArrayTagWriter + internal sealed class OtlpArrayTagWriter : ArrayTagWriter { [ThreadStatic] - private static byte[]? threadBuffer; + internal static byte[]? ThreadBuffer; + private const int MaxBufferSize = 2 * 1024 * 1024; public override OtlpTagWriterArrayState BeginWriteArray() { - threadBuffer ??= new byte[2048]; + ThreadBuffer ??= new byte[2048]; return new OtlpTagWriterArrayState { - Buffer = threadBuffer, + Buffer = ThreadBuffer, WritePosition = 0, }; } @@ -149,5 +150,29 @@ public override void WriteStringValue(ref OtlpTagWriterArrayState state, ReadOnl public override void EndWriteArray(ref OtlpTagWriterArrayState state) { } + + public override bool TryResize() + { + var buffer = ThreadBuffer; + + Debug.Assert(buffer != null, "buffer was null"); + + if (buffer!.Length >= MaxBufferSize) + { + OpenTelemetryProtocolExporterEventSource.Log.ArrayBufferExceededMaxSize(); + return false; + } + + try + { + ThreadBuffer = new byte[buffer.Length * 2]; + return true; + } + catch (OutOfMemoryException) + { + OpenTelemetryProtocolExporterEventSource.Log.BufferResizeFailedDueToMemory(nameof(OtlpArrayTagWriter)); + return false; + } + } } } diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpTraceSerializer.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpTraceSerializer.cs index b50acbe7c89..91201113897 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpTraceSerializer.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpTraceSerializer.cs @@ -18,7 +18,7 @@ internal static class ProtobufOtlpTraceSerializer private static readonly Stack> ActivityListPool = []; private static readonly Dictionary> ScopeTracesList = []; - internal static int WriteTraceData(byte[] buffer, int writePosition, SdkLimitOptions sdkLimitOptions, Resources.Resource? resource, in Batch batch) + internal static int WriteTraceData(ref byte[] buffer, int writePosition, SdkLimitOptions sdkLimitOptions, Resources.Resource? resource, in Batch batch) { writePosition = ProtobufSerializer.WriteTag(buffer, writePosition, ProtobufOtlpTraceFieldNumberConstants.TracesData_Resource_Spans, ProtobufWireType.LEN); int resourceSpansScopeSpansLengthPosition = writePosition; @@ -36,20 +36,20 @@ internal static int WriteTraceData(byte[] buffer, int writePosition, SdkLimitOpt activities.Add(activity); } - writePosition = TryWriteResourceSpans(buffer, writePosition, sdkLimitOptions, resource); + writePosition = TryWriteResourceSpans(ref buffer, writePosition, sdkLimitOptions, resource); ReturnActivityListToPool(); ProtobufSerializer.WriteReservedLength(buffer, resourceSpansScopeSpansLengthPosition, writePosition - (resourceSpansScopeSpansLengthPosition + ReserveSizeForLength)); return writePosition; } - internal static int TryWriteResourceSpans(byte[] buffer, int writePosition, SdkLimitOptions sdkLimitOptions, Resources.Resource? resource) + internal static int TryWriteResourceSpans(ref byte[] buffer, int writePosition, SdkLimitOptions sdkLimitOptions, Resources.Resource? resource) { try { writePosition = WriteResourceSpans(buffer, writePosition, sdkLimitOptions, resource); } - catch (IndexOutOfRangeException) + catch (Exception ex) when (ex is IndexOutOfRangeException || ex is ArgumentException) { // Attempt to increase the buffer size if (!ProtobufSerializer.IncreaseBufferSize(ref buffer, OtlpSignalType.Traces)) @@ -61,7 +61,7 @@ internal static int TryWriteResourceSpans(byte[] buffer, int writePosition, SdkL // The recursion depth is limited to a maximum of 7 calls, as the buffer size starts at ~732 KB // and doubles until it reaches the maximum size of 100 MB. This ensures the recursion remains safe // and avoids stack overflow. - return TryWriteResourceSpans(buffer, writePosition, sdkLimitOptions, resource); + return TryWriteResourceSpans(ref buffer, writePosition, sdkLimitOptions, resource); } return writePosition; diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufSerializer.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufSerializer.cs index 8597524d52a..b4f3ad7131c 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufSerializer.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufSerializer.cs @@ -321,6 +321,13 @@ internal static int WriteStringWithTag(byte[] buffer, int writePosition, int fie writePosition = WriteLength(buffer, writePosition, numberOfUtf8CharsInString); #if NETFRAMEWORK || NETSTANDARD2_0 + if (buffer.Length - writePosition < numberOfUtf8CharsInString) + { + // Note: Validate there is enough space in the buffer to hold the + // string otherwise throw to trigger a resize of the buffer. + throw new IndexOutOfRangeException(); + } + unsafe { fixed (char* strPtr = &GetNonNullPinnableReference(value)) diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpLogExporter.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpLogExporter.cs index d78faaa84ba..2ac41e5aca1 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpLogExporter.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpLogExporter.cs @@ -72,7 +72,7 @@ public override ExportResult Export(in Batch logRecordBatch) try { - int writePosition = ProtobufOtlpLogSerializer.WriteLogsData(this.buffer, this.startWritePosition, this.sdkLimitOptions, this.experimentalOptions, this.Resource, logRecordBatch); + int writePosition = ProtobufOtlpLogSerializer.WriteLogsData(ref this.buffer, this.startWritePosition, this.sdkLimitOptions, this.experimentalOptions, this.Resource, logRecordBatch); if (this.startWritePosition == GrpcStartWritePosition) { diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpMetricExporter.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpMetricExporter.cs index 26beee90bef..88bafa3007c 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpMetricExporter.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpMetricExporter.cs @@ -65,7 +65,7 @@ public override ExportResult Export(in Batch metrics) try { - int writePosition = ProtobufOtlpMetricSerializer.WriteMetricsData(this.buffer, this.startWritePosition, this.Resource, metrics); + int writePosition = ProtobufOtlpMetricSerializer.WriteMetricsData(ref this.buffer, this.startWritePosition, this.Resource, metrics); if (this.startWritePosition == GrpcStartWritePosition) { diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpTraceExporter.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpTraceExporter.cs index 2a9aa274241..5a1f2f19d24 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpTraceExporter.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpTraceExporter.cs @@ -68,7 +68,7 @@ public override ExportResult Export(in Batch activityBatch) try { - int writePosition = ProtobufOtlpTraceSerializer.WriteTraceData(this.buffer, this.startWritePosition, this.sdkLimitOptions, this.Resource, activityBatch); + int writePosition = ProtobufOtlpTraceSerializer.WriteTraceData(ref this.buffer, this.startWritePosition, this.sdkLimitOptions, this.Resource, activityBatch); if (this.startWritePosition == GrpcStartWritePosition) { diff --git a/src/Shared/TagWriter/ArrayTagWriter.cs b/src/Shared/TagWriter/ArrayTagWriter.cs index c8859acbb2f..92cf3a317e2 100644 --- a/src/Shared/TagWriter/ArrayTagWriter.cs +++ b/src/Shared/TagWriter/ArrayTagWriter.cs @@ -19,4 +19,6 @@ internal abstract class ArrayTagWriter public abstract void WriteStringValue(ref TArrayState state, ReadOnlySpan value); public abstract void EndWriteArray(ref TArrayState state); + + public virtual bool TryResize() => false; } diff --git a/src/Shared/TagWriter/TagWriter.cs b/src/Shared/TagWriter/TagWriter.cs index 941fa6d6f01..fdd83a76bfd 100644 --- a/src/Shared/TagWriter/TagWriter.cs +++ b/src/Shared/TagWriter/TagWriter.cs @@ -71,6 +71,10 @@ public bool TryWriteTag( { this.WriteArrayTagInternal(ref state, key, array, tagValueMaxLength); } + catch (Exception ex) when (ex is IndexOutOfRangeException || ex is ArgumentException) + { + throw; + } catch { // If an exception is thrown when calling ToString @@ -152,27 +156,49 @@ private void WriteArrayTagInternal(ref TTagState state, string key, Array array, { var arrayState = this.arrayWriter.BeginWriteArray(); - // This switch ensures the values of the resultant array-valued tag are of the same type. - switch (array) + try { - case char[] charArray: this.WriteStructToArray(ref arrayState, charArray); break; - case string?[] stringArray: this.WriteStringsToArray(ref arrayState, stringArray, tagValueMaxLength); break; - case bool[] boolArray: this.WriteStructToArray(ref arrayState, boolArray); break; - case byte[] byteArray: this.WriteToArrayCovariant(ref arrayState, byteArray); break; - case short[] shortArray: this.WriteToArrayCovariant(ref arrayState, shortArray); break; + // This switch ensures the values of the resultant array-valued tag are of the same type. + switch (array) + { + case char[] charArray: this.WriteStructToArray(ref arrayState, charArray); break; + case string?[] stringArray: this.WriteStringsToArray(ref arrayState, stringArray, tagValueMaxLength); break; + case bool[] boolArray: this.WriteStructToArray(ref arrayState, boolArray); break; + case byte[] byteArray: this.WriteToArrayCovariant(ref arrayState, byteArray); break; + case short[] shortArray: this.WriteToArrayCovariant(ref arrayState, shortArray); break; #if NETFRAMEWORK - case int[]: this.WriteArrayTagIntNetFramework(ref arrayState, array, tagValueMaxLength); break; - case long[]: this.WriteArrayTagLongNetFramework(ref arrayState, array, tagValueMaxLength); break; + case int[]: this.WriteArrayTagIntNetFramework(ref arrayState, array, tagValueMaxLength); break; + case long[]: this.WriteArrayTagLongNetFramework(ref arrayState, array, tagValueMaxLength); break; #else - case int[] intArray: this.WriteToArrayCovariant(ref arrayState, intArray); break; - case long[] longArray: this.WriteToArrayCovariant(ref arrayState, longArray); break; + case int[] intArray: this.WriteToArrayCovariant(ref arrayState, intArray); break; + case long[] longArray: this.WriteToArrayCovariant(ref arrayState, longArray); break; #endif - case float[] floatArray: this.WriteStructToArray(ref arrayState, floatArray); break; - case double[] doubleArray: this.WriteStructToArray(ref arrayState, doubleArray); break; - default: this.WriteToArrayTypeChecked(ref arrayState, array, tagValueMaxLength); break; + case float[] floatArray: this.WriteStructToArray(ref arrayState, floatArray); break; + case double[] doubleArray: this.WriteStructToArray(ref arrayState, doubleArray); break; + default: this.WriteToArrayTypeChecked(ref arrayState, array, tagValueMaxLength); break; + } + + this.arrayWriter.EndWriteArray(ref arrayState); } + catch (Exception ex) when (ex is IndexOutOfRangeException || ex is ArgumentException) + { + // If the array writer cannot be resized, TryResize should log a message to the event source, return false. + if (this.arrayWriter.TryResize()) + { + this.WriteArrayTagInternal(ref state, key, array, tagValueMaxLength); + return; + } + + // Drop the array value and set "TRUNCATED" as value for easier isolation. + // This is a best effort to avoid dropping the entire tag. + this.WriteStringTag( + ref state, + key, + "TRUNCATED".AsSpan()); - this.arrayWriter.EndWriteArray(ref arrayState); + this.LogUnsupportedTagTypeAndReturnFalse(key, array!.GetType().ToString()); + return; + } this.WriteArrayTag(ref state, key, ref arrayState); } diff --git a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/Implementation/ExportClient/OtlpHttpTraceExportClientTests.cs b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/Implementation/ExportClient/OtlpHttpTraceExportClientTests.cs index e9a4ba80c76..cf5833e376d 100644 --- a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/Implementation/ExportClient/OtlpHttpTraceExportClientTests.cs +++ b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/Implementation/ExportClient/OtlpHttpTraceExportClientTests.cs @@ -181,7 +181,7 @@ void RunTest(Batch batch) private static (byte[] Buffer, int ContentLength) CreateTraceExportRequest(SdkLimitOptions sdkOptions, in Batch batch, Resource resource) { var buffer = new byte[4096]; - var writePosition = ProtobufOtlpTraceSerializer.WriteTraceData(buffer, 0, sdkOptions, resource, batch); + var writePosition = ProtobufOtlpTraceSerializer.WriteTraceData(ref buffer, 0, sdkOptions, resource, batch); return (buffer, writePosition); } } diff --git a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/Implementation/Serializer/OtlpArrayTagWriterTests.cs b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/Implementation/Serializer/OtlpArrayTagWriterTests.cs new file mode 100644 index 00000000000..a4aed5f155c --- /dev/null +++ b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/Implementation/Serializer/OtlpArrayTagWriterTests.cs @@ -0,0 +1,287 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +using System.Diagnostics; +using OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation; +using OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.Serializer; +using OpenTelemetry.Resources; +using Xunit; +using OtlpCollector = OpenTelemetry.Proto.Collector.Trace.V1; +using OtlpTrace = OpenTelemetry.Proto.Trace.V1; + +namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests.Implementation.Serializer; + +public class OtlpArrayTagWriterTests : IDisposable +{ + private readonly ProtobufOtlpTagWriter.OtlpArrayTagWriter arrayTagWriter; + + static OtlpArrayTagWriterTests() + { + Activity.DefaultIdFormat = ActivityIdFormat.W3C; + Activity.ForceDefaultIdFormat = true; + + var listener = new ActivityListener + { + ShouldListenTo = _ => true, + Sample = (ref ActivityCreationOptions options) => ActivitySamplingResult.AllData, + }; + + ActivitySource.AddActivityListener(listener); + } + + public OtlpArrayTagWriterTests() + { + this.arrayTagWriter = new ProtobufOtlpTagWriter.OtlpArrayTagWriter(); + } + + [Fact] + public void BeginWriteArray_InitializesArrayState() + { + // Act + var arrayState = this.arrayTagWriter.BeginWriteArray(); + + // Assert + Assert.NotNull(arrayState.Buffer); + Assert.Equal(0, arrayState.WritePosition); + Assert.True(arrayState.Buffer.Length == 2048); + } + + [Fact] + public void WriteNullValue_AddsNullValueToBuffer() + { + // Arrange + var arrayState = this.arrayTagWriter.BeginWriteArray(); + + // Act + this.arrayTagWriter.WriteNullValue(ref arrayState); + + // Assert + // Check that the buffer contains the correct tag and length for a null value + Assert.True(arrayState.WritePosition > 0); + } + + [Theory] + [InlineData(0L)] + [InlineData(long.MaxValue)] + [InlineData(long.MinValue)] + public void WriteIntegralValue_WritesIntegralValueToBuffer(long value) + { + // Arrange + var arrayState = this.arrayTagWriter.BeginWriteArray(); + + // Act + this.arrayTagWriter.WriteIntegralValue(ref arrayState, value); + + // Assert + Assert.True(arrayState.WritePosition > 0); + } + + [Theory] + [InlineData(0.0)] + [InlineData(double.MaxValue)] + [InlineData(double.MinValue)] + public void WriteFloatingPointValue_WritesFloatingPointValueToBuffer(double value) + { + // Arrange + var arrayState = this.arrayTagWriter.BeginWriteArray(); + + // Act + this.arrayTagWriter.WriteFloatingPointValue(ref arrayState, value); + + // Assert + Assert.True(arrayState.WritePosition > 0); + } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public void WriteBooleanValue_WritesBooleanValueToBuffer(bool value) + { + // Arrange + var arrayState = this.arrayTagWriter.BeginWriteArray(); + + // Act + this.arrayTagWriter.WriteBooleanValue(ref arrayState, value); + + // Assert + Assert.True(arrayState.WritePosition > 0); + } + + [Theory] + [InlineData("")] + [InlineData("test")] + public void WriteStringValue_WritesStringValueToBuffer(string value) + { + // Arrange + var arrayState = this.arrayTagWriter.BeginWriteArray(); + + // Act + this.arrayTagWriter.WriteStringValue(ref arrayState, value.AsSpan()); + + // Assert + Assert.True(arrayState.WritePosition > 0); + } + + [Fact] + public void TryResize_SucceedsInitially() + { + // Act + this.arrayTagWriter.BeginWriteArray(); + bool result = this.arrayTagWriter.TryResize(); + + // Assert + Assert.True(result); + } + + [Fact] + public void TryResize_RepeatedResizingStopsAtMaxBufferSize() + { + // Arrange + var arrayState = this.arrayTagWriter.BeginWriteArray(); + bool resizeResult = true; + + // Act: Repeatedly attempt to resize until reaching maximum buffer size + while (resizeResult) + { + resizeResult = this.arrayTagWriter.TryResize(); + } + + // Assert + Assert.False(resizeResult, "Buffer should not resize beyond the maximum allowed size."); + } + + [Fact] + public void SerializeLargeArrayExceeding2MB_TruncatesInOtlpSpan() + { + // Create a large array exceeding 2 MB + var largeArray = new string[512 * 1024]; + for (int i = 0; i < largeArray.Length; i++) + { + largeArray[i] = "1234"; + } + + var lessthat1MBArray = new string[256 * 4]; + for (int i = 0; i < lessthat1MBArray.Length; i++) + { + lessthat1MBArray[i] = "1234"; + } + + var tags = new ActivityTagsCollection + { + new("lessthat1MBArray", lessthat1MBArray), + new("StringArray", new string?[] { "12345" }), + new("LargeArray", largeArray), + }; + + using var activitySource = new ActivitySource(nameof(this.SerializeLargeArrayExceeding2MB_TruncatesInOtlpSpan)); + using var activity = activitySource.StartActivity("activity", ActivityKind.Server, default(ActivityContext), tags); + + Assert.NotNull(activity); + + var otlpSpan = ToOtlpSpanWithExtendedBuffer(new SdkLimitOptions(), activity); + + Assert.NotNull(otlpSpan); + Assert.True(otlpSpan.Attributes.Count == 3); + var keyValue = otlpSpan.Attributes.FirstOrDefault(kvp => kvp.Key == "StringArray"); + Assert.NotNull(keyValue); + Assert.Equal("12345", keyValue.Value.ArrayValue.Values[0].StringValue); + + // The string is too large, hence not evaluating the content. + keyValue = otlpSpan.Attributes.FirstOrDefault(kvp => kvp.Key == "lessthat1MBArray"); + Assert.NotNull(keyValue); + + keyValue = otlpSpan.Attributes.FirstOrDefault(kvp => kvp.Key == "LargeArray"); + Assert.NotNull(keyValue); + Assert.Equal("TRUNCATED", keyValue.Value.StringValue); + } + + [Fact] + public void LargeArray_WithSmallBaseBuffer_ThrowsExceptionOnWriteSpan() + { + var lessthat1MBArray = new string[256 * 256]; + for (int i = 0; i < lessthat1MBArray.Length; i++) + { + lessthat1MBArray[i] = "1234"; + } + + var tags = new ActivityTagsCollection + { + new("lessthat1MBArray", lessthat1MBArray), + }; + + using var activitySource = new ActivitySource(nameof(this.LargeArray_WithSmallBaseBuffer_ThrowsExceptionOnWriteSpan)); + using var activity = activitySource.StartActivity("root", ActivityKind.Server, default(ActivityContext), tags); + + Assert.NotNull(activity); + Assert.Throws(() => ToOtlpSpan(new SdkLimitOptions(), activity)); + } + + [Fact] + public void LargeArray_WithSmallBaseBuffer_ExpandsOnTraceData() + { + var lessthat1MBArray = new string[256 * 256]; + for (int i = 0; i < lessthat1MBArray.Length; i++) + { + lessthat1MBArray[i] = "1234"; + } + + var tags = new ActivityTagsCollection + { + new("lessthat1MBArray", lessthat1MBArray), + }; + + using var activitySource = new ActivitySource(nameof(this.LargeArray_WithSmallBaseBuffer_ExpandsOnTraceData)); + using var activity = activitySource.StartActivity("root", ActivityKind.Server, default(ActivityContext), tags); + + Assert.NotNull(activity); + var batch = new Batch([activity], 1); + RunTest(new(), batch); + + void RunTest(SdkLimitOptions sdkOptions, Batch batch) + { + var buffer = new byte[4096]; + var writePosition = ProtobufOtlpTraceSerializer.WriteTraceData(ref buffer, 0, sdkOptions, ResourceBuilder.CreateEmpty().Build(), batch); + using var stream = new MemoryStream(buffer, 0, writePosition); + var tracesData = OtlpTrace.TracesData.Parser.ParseFrom(stream); + var request = new OtlpCollector.ExportTraceServiceRequest(); + request.ResourceSpans.Add(tracesData.ResourceSpans); + + // Buffer should be expanded to accommodate the large array. + Assert.True(buffer.Length > 4096); + + Assert.Single(request.ResourceSpans); + var scopeSpans = request.ResourceSpans.First().ScopeSpans; + Assert.Single(scopeSpans); + var otlpSpan = scopeSpans.First().Spans.First(); + Assert.NotNull(otlpSpan); + + // The string is too large, hence not evaluating the content. + var keyValue = otlpSpan.Attributes.FirstOrDefault(kvp => kvp.Key == "lessthat1MBArray"); + Assert.NotNull(keyValue); + } + } + + public void Dispose() + { + // Clean up the thread buffer after each test + ProtobufOtlpTagWriter.OtlpArrayTagWriter.ThreadBuffer = null; + } + + private static OtlpTrace.Span? ToOtlpSpan(SdkLimitOptions sdkOptions, Activity activity) + { + var buffer = new byte[4096]; + var writePosition = ProtobufOtlpTraceSerializer.WriteSpan(buffer, 0, sdkOptions, activity); + using var stream = new MemoryStream(buffer, 0, writePosition); + var scopeSpans = OtlpTrace.ScopeSpans.Parser.ParseFrom(stream); + return scopeSpans.Spans.FirstOrDefault(); + } + + private static OtlpTrace.Span? ToOtlpSpanWithExtendedBuffer(SdkLimitOptions sdkOptions, Activity activity) + { + var buffer = new byte[4194304]; + var writePosition = ProtobufOtlpTraceSerializer.WriteSpan(buffer, 0, sdkOptions, activity); + using var stream = new MemoryStream(buffer, 0, writePosition); + var scopeSpans = OtlpTrace.ScopeSpans.Parser.ParseFrom(stream); + return scopeSpans.Spans.FirstOrDefault(); + } +} diff --git a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpLogExporterTests.cs b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpLogExporterTests.cs index 95acb36da9b..9d14aafefe4 100644 --- a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpLogExporterTests.cs +++ b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpLogExporterTests.cs @@ -1459,6 +1459,44 @@ public void LogRecordLoggerNameIsExportedWhenUsingBridgeApi(string? loggerName, Assert.Equal(expectedScopeName, request.ResourceLogs[0].ScopeLogs[0].Scope?.Name); } + [Fact] + public void LogSerialization_ExpandsBufferForLogsAndSerializes() + { + LogRecordAttributeList attributes = default; + attributes.Add("name", "tomato"); + attributes.Add("price", 2.99); + attributes.Add("{OriginalFormat}", "Hello from {name} {price}."); + + var logRecords = new List(); + + using (var loggerProvider = Sdk.CreateLoggerProviderBuilder() + .AddInMemoryExporter(logRecords) + .Build()) + { + var logger = loggerProvider.GetLogger("MyLogger"); + + logger.EmitLog(new LogRecordData()); + } + + Assert.Single(logRecords); + + var batch = new Batch(new[] { logRecords[0] }, 1); + + var buffer = new byte[50]; + var writePosition = ProtobufOtlpLogSerializer.WriteLogsData(ref buffer, 0, DefaultSdkLimitOptions, new(), ResourceBuilder.CreateEmpty().Build(), batch); + using var stream = new MemoryStream(buffer, 0, writePosition); + var logsData = OtlpLogs.LogsData.Parser.ParseFrom(stream); + var request = new OtlpCollector.ExportLogsServiceRequest(); + request.ResourceLogs.Add(logsData.ResourceLogs); + + Assert.True(buffer.Length > 50); + Assert.NotNull(request); + Assert.Single(request.ResourceLogs); + Assert.Single(request.ResourceLogs[0].ScopeLogs); + + Assert.Equal("MyLogger", request.ResourceLogs[0].ScopeLogs[0].Scope?.Name); + } + private static void RunVerifyEnvironmentVariablesTakenFromIConfigurationTest( string? optionsName, Func, (IDisposable Container, ILoggerFactory LoggerFactory)> createLoggerFactoryFunc) @@ -1599,7 +1637,7 @@ private static void ConfigureOtlpExporter( private static OtlpCollector.ExportLogsServiceRequest CreateLogsExportRequest(SdkLimitOptions sdkOptions, ExperimentalOptions experimentalOptions, in Batch batch, Resource resource) { var buffer = new byte[4096]; - var writePosition = ProtobufOtlpLogSerializer.WriteLogsData(buffer, 0, sdkOptions, experimentalOptions, resource, batch); + var writePosition = ProtobufOtlpLogSerializer.WriteLogsData(ref buffer, 0, sdkOptions, experimentalOptions, resource, batch); using var stream = new MemoryStream(buffer, 0, writePosition); var logsData = OtlpLogs.LogsData.Parser.ParseFrom(stream); var request = new OtlpCollector.ExportLogsServiceRequest(); diff --git a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpMetricsExporterTests.cs b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpMetricsExporterTests.cs index d90d3934ad7..40c075c44a3 100644 --- a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpMetricsExporterTests.cs +++ b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpMetricsExporterTests.cs @@ -906,6 +906,58 @@ void AssertExemplars(T value, OtlpMetrics.Metric metric) } } + [Fact] + public void MetricsSerialization_ExpandsBufferForMetricsAndSerializes() + { + var metrics = new List(); + + var meterTags = new KeyValuePair[] + { + new("key1", "value1"), + new("key2", "value2"), + }; + + using var meter = new Meter(name: $"{Utils.GetCurrentMethodName()}", version: "0.0.1", tags: meterTags); + using var provider = Sdk.CreateMeterProviderBuilder() + .AddMeter(meter.Name) + .AddInMemoryExporter(metrics) + .Build(); + + var counter = meter.CreateCounter("counter"); + counter.Add(100); + + provider.ForceFlush(); + + var batch = new Batch(metrics.ToArray(), metrics.Count); + + var buffer = new byte[50]; + var writePosition = ProtobufOtlpMetricSerializer.WriteMetricsData(ref buffer, 0, ResourceBuilder.CreateEmpty().Build(), in batch); + using var stream = new MemoryStream(buffer, 0, writePosition); + + var metricsData = OtlpMetrics.MetricsData.Parser.ParseFrom(stream); + + var request = new OtlpCollector.ExportMetricsServiceRequest(); + request.ResourceMetrics.Add(metricsData.ResourceMetrics); + + Assert.True(buffer.Length > 50); + + Assert.Single(request.ResourceMetrics); + var resourceMetric = request.ResourceMetrics.First(); + var otlpResource = resourceMetric.Resource; + + Assert.Contains(otlpResource.Attributes, (kvp) => kvp.Key == ResourceSemanticConventions.AttributeServiceName && kvp.Value.ToString().Contains("unknown_service:")); + + Assert.Single(resourceMetric.ScopeMetrics); + var instrumentationLibraryMetrics = resourceMetric.ScopeMetrics.First(); + Assert.Equal(string.Empty, instrumentationLibraryMetrics.SchemaUrl); + Assert.Equal(meter.Name, instrumentationLibraryMetrics.Scope.Name); + Assert.Equal("0.0.1", instrumentationLibraryMetrics.Scope.Version); + + Assert.Equal(2, instrumentationLibraryMetrics.Scope.Attributes.Count); + Assert.Contains(instrumentationLibraryMetrics.Scope.Attributes, (kvp) => kvp.Key == "key1" && kvp.Value.StringValue == "value1"); + Assert.Contains(instrumentationLibraryMetrics.Scope.Attributes, (kvp) => kvp.Key == "key2" && kvp.Value.StringValue == "value2"); + } + public void Dispose() { OtlpSpecConfigDefinitionTests.ClearEnvVars(); @@ -939,7 +991,7 @@ private static void VerifyExemplars(long? longValue, double? doubleValue, boo private static OtlpCollector.ExportMetricsServiceRequest CreateMetricExportRequest(in Batch batch, Resource resource) { var buffer = new byte[4096]; - var writePosition = ProtobufOtlpMetricSerializer.WriteMetricsData(buffer, 0, resource, in batch); + var writePosition = ProtobufOtlpMetricSerializer.WriteMetricsData(ref buffer, 0, resource, in batch); using var stream = new MemoryStream(buffer, 0, writePosition); var metricsData = OtlpMetrics.MetricsData.Parser.ParseFrom(stream); diff --git a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpTraceExporterTests.cs b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpTraceExporterTests.cs index 94b97ca1702..ab3a1c0d3e2 100644 --- a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpTraceExporterTests.cs +++ b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpTraceExporterTests.cs @@ -590,6 +590,46 @@ public void ToOtlpSpanNativeActivityStatusTest(ActivityStatusCode expectedStatus } } + [Fact] + public void TracesSerialization_ExpandsBufferForTracesAndSerializes() + { + var tags = new ActivityTagsCollection + { + new("Tagkey", "Tagvalue"), + }; + + using var activitySource = new ActivitySource(nameof(this.TracesSerialization_ExpandsBufferForTracesAndSerializes)); + using var activity = activitySource.StartActivity("root", ActivityKind.Server, default(ActivityContext), tags); + + Assert.NotNull(activity); + var batch = new Batch([activity], 1); + RunTest(new(), batch); + + void RunTest(SdkLimitOptions sdkOptions, Batch batch) + { + var buffer = new byte[50]; + var writePosition = ProtobufOtlpTraceSerializer.WriteTraceData(ref buffer, 0, sdkOptions, ResourceBuilder.CreateEmpty().Build(), batch); + using var stream = new MemoryStream(buffer, 0, writePosition); + var tracesData = OtlpTrace.TracesData.Parser.ParseFrom(stream); + var request = new OtlpCollector.ExportTraceServiceRequest(); + request.ResourceSpans.Add(tracesData.ResourceSpans); + + // Buffer should be expanded to accommodate the large array. + Assert.True(buffer.Length > 50); + + Assert.Single(request.ResourceSpans); + var scopeSpans = request.ResourceSpans.First().ScopeSpans; + Assert.Single(scopeSpans); + var otlpSpan = scopeSpans.First().Spans.First(); + Assert.NotNull(otlpSpan); + + // The string is too large, hence not evaluating the content. + var keyValue = otlpSpan.Attributes.FirstOrDefault(kvp => kvp.Key == "Tagkey"); + Assert.NotNull(keyValue); + Assert.Equal("Tagvalue", keyValue.Value.StringValue); + } + } + [Theory] [InlineData(StatusCode.Unset, "Unset", "Description will be ignored if status is Unset.")] [InlineData(StatusCode.Ok, "Ok", "Description must only be used with the Error StatusCode.")] @@ -970,7 +1010,7 @@ public void SpanLinkFlagsTest(bool isRecorded, bool isRemote) private static OtlpCollector.ExportTraceServiceRequest CreateTraceExportRequest(SdkLimitOptions sdkOptions, in Batch batch, Resource resource) { var buffer = new byte[4096]; - var writePosition = ProtobufOtlpTraceSerializer.WriteTraceData(buffer, 0, sdkOptions, resource, batch); + var writePosition = ProtobufOtlpTraceSerializer.WriteTraceData(ref buffer, 0, sdkOptions, resource, batch); using var stream = new MemoryStream(buffer, 0, writePosition); var tracesData = OtlpTrace.TracesData.Parser.ParseFrom(stream); var request = new OtlpCollector.ExportTraceServiceRequest(); From 75a683eab7bb71f08d731d8afed420ba1bed9166 Mon Sep 17 00:00:00 2001 From: Rajkumar Rangaraj Date: Tue, 10 Dec 2024 13:23:31 -0800 Subject: [PATCH 7/7] [otlp] Update changelog / Remove OtlpExporter resource modification code (#6015) Co-authored-by: Mikel Blanchard --- .../CHANGELOG.md | 26 +++++++++++++++++++ .../ProtobufOtlpResourceSerializer.cs | 22 +--------------- .../OtlpHttpTraceExportClientTests.cs | 2 +- .../OtlpMetricsExporterTests.cs | 5 +--- .../OtlpResourceTests.cs | 2 +- .../OtlpTraceExporterTests.cs | 2 +- 6 files changed, 31 insertions(+), 28 deletions(-) diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md index d8f52e54309..d2b643ddbea 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md @@ -7,6 +7,32 @@ Notes](../../RELEASENOTES.md). ## Unreleased +* Removed the following package references: + + * `Google.Protobuf` + * `Grpc` + * `Grpc.Net.Client` + + These changes were made to streamline dependencies and reduce the footprint of + the exporter. + ([#6005](https://github.com/open-telemetry/opentelemetry-dotnet/pull/6005)) + +* Switched from using the `Google.Protobuf` library for serialization to a + custom manual implementation of protobuf serialization. + ([#6005](https://github.com/open-telemetry/opentelemetry-dotnet/pull/6005)) + +* Fixed an issue where a `service.name` was added to the resource if it was + missing. The exporter now respects the resource data provided by the SDK + without modifications. + ([#6015](https://github.com/open-telemetry/opentelemetry-dotnet/pull/6015)) + +* Removed the peer service resolver, which was based on earlier experimental + semantic conventions that are not part of the stable specification. This + change ensures that the exporter no longer modifies or assumes the value of + peer service attributes, aligning it more closely with OpenTelemetry protocol + specifications. + ([#6005](https://github.com/open-telemetry/opentelemetry-dotnet/pull/6005)) + ## 1.10.0 Released 2024-Nov-12 diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpResourceSerializer.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpResourceSerializer.cs index e31c4d9dd5f..d12099bec02 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpResourceSerializer.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/Serializer/ProtobufOtlpResourceSerializer.cs @@ -9,9 +9,6 @@ internal static class ProtobufOtlpResourceSerializer { private const int ReserveSizeForLength = 4; - private static readonly string DefaultServiceName = ResourceBuilder.CreateDefault().Build().Attributes.FirstOrDefault( - kvp => kvp.Key == ResourceSemanticConventions.AttributeServiceName).Value as string ?? "unknown_service"; - internal static int WriteResource(byte[] buffer, int writePosition, Resource? resource) { ProtobufOtlpTagWriter.OtlpTagWriterState otlpTagWriterState = new ProtobufOtlpTagWriter.OtlpTagWriterState @@ -24,41 +21,24 @@ internal static int WriteResource(byte[] buffer, int writePosition, Resource? re int resourceLengthPosition = otlpTagWriterState.WritePosition; otlpTagWriterState.WritePosition += ReserveSizeForLength; - bool isServiceNamePresent = false; if (resource != null && resource != Resource.Empty) { if (resource.Attributes is IReadOnlyList> resourceAttributesList) { for (int i = 0; i < resourceAttributesList.Count; i++) { - var attribute = resourceAttributesList[i]; - if (attribute.Key == ResourceSemanticConventions.AttributeServiceName) - { - isServiceNamePresent = true; - } - - ProcessResourceAttribute(ref otlpTagWriterState, attribute); + ProcessResourceAttribute(ref otlpTagWriterState, resourceAttributesList[i]); } } else { foreach (var attribute in resource.Attributes) { - if (attribute.Key == ResourceSemanticConventions.AttributeServiceName) - { - isServiceNamePresent = true; - } - ProcessResourceAttribute(ref otlpTagWriterState, attribute); } } } - if (!isServiceNamePresent) - { - ProcessResourceAttribute(ref otlpTagWriterState, new KeyValuePair(ResourceSemanticConventions.AttributeServiceName, DefaultServiceName)); - } - var resourceLength = otlpTagWriterState.WritePosition - (resourceLengthPosition + ReserveSizeForLength); ProtobufSerializer.WriteReservedLength(otlpTagWriterState.Buffer, resourceLengthPosition, resourceLength); diff --git a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/Implementation/ExportClient/OtlpHttpTraceExportClientTests.cs b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/Implementation/ExportClient/OtlpHttpTraceExportClientTests.cs index cf5833e376d..620c2a0b187 100644 --- a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/Implementation/ExportClient/OtlpHttpTraceExportClientTests.cs +++ b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/Implementation/ExportClient/OtlpHttpTraceExportClientTests.cs @@ -173,7 +173,7 @@ void RunTest(Batch batch) } else { - Assert.Contains(resourceSpan.Resource.Attributes, (kvp) => kvp.Key == ResourceSemanticConventions.AttributeServiceName && kvp.Value.ToString().Contains("unknown_service:")); + Assert.DoesNotContain(resourceSpan.Resource.Attributes, kvp => kvp.Key == ResourceSemanticConventions.AttributeServiceName); } } } diff --git a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpMetricsExporterTests.cs b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpMetricsExporterTests.cs index 40c075c44a3..78c2d033383 100644 --- a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpMetricsExporterTests.cs +++ b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpMetricsExporterTests.cs @@ -207,7 +207,7 @@ public void ToOtlpResourceMetricsTest(bool includeServiceNameInResource) } else { - Assert.Contains(otlpResource.Attributes, (kvp) => kvp.Key == ResourceSemanticConventions.AttributeServiceName && kvp.Value.ToString().Contains("unknown_service:")); + Assert.DoesNotContain(otlpResource.Attributes, kvp => kvp.Key == ResourceSemanticConventions.AttributeServiceName); } Assert.Single(resourceMetric.ScopeMetrics); @@ -943,9 +943,6 @@ public void MetricsSerialization_ExpandsBufferForMetricsAndSerializes() Assert.Single(request.ResourceMetrics); var resourceMetric = request.ResourceMetrics.First(); - var otlpResource = resourceMetric.Resource; - - Assert.Contains(otlpResource.Attributes, (kvp) => kvp.Key == ResourceSemanticConventions.AttributeServiceName && kvp.Value.ToString().Contains("unknown_service:")); Assert.Single(resourceMetric.ScopeMetrics); var instrumentationLibraryMetrics = resourceMetric.ScopeMetrics.First(); diff --git a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpResourceTests.cs b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpResourceTests.cs index 4d309326a78..5bc0c3601ae 100644 --- a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpResourceTests.cs +++ b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpResourceTests.cs @@ -43,7 +43,7 @@ public void ToOtlpResourceTest(bool includeServiceNameInResource) } else { - Assert.Contains(otlpResource.Attributes, (kvp) => kvp.Key == ResourceSemanticConventions.AttributeServiceName && kvp.Value.ToString().Contains("unknown_service:")); + Assert.DoesNotContain(otlpResource.Attributes, kvp => kvp.Key == ResourceSemanticConventions.AttributeServiceName); } } } diff --git a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpTraceExporterTests.cs b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpTraceExporterTests.cs index ab3a1c0d3e2..e96b5d43f44 100644 --- a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpTraceExporterTests.cs +++ b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpTraceExporterTests.cs @@ -184,7 +184,7 @@ void RunTest(SdkLimitOptions sdkOptions, Batch batch) } else { - Assert.Contains(otlpResource.Attributes, (kvp) => kvp.Key == ResourceSemanticConventions.AttributeServiceName && kvp.Value.ToString().Contains("unknown_service:")); + Assert.DoesNotContain(otlpResource.Attributes, kvp => kvp.Key == ResourceSemanticConventions.AttributeServiceName); } var scopeSpans = request.ResourceSpans.First().ScopeSpans;