[otlp] Remove the Google.Protobuf / Grpc packages, and replace the logs and metrics with the new implementation (#6005)
Co-authored-by: Mikel Blanchard <mblanchard@macrosssoftware.com>
This commit is contained in:
parent
84e6afbeba
commit
b9be07a27d
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
/// <summary>Base class for sending OTLP export request over gRPC.</summary>
|
||||
/// <typeparam name="TRequest">Type of export request.</typeparam>
|
||||
internal abstract class BaseOtlpGrpcExportClient<TRequest> : IExportClient<TRequest>
|
||||
{
|
||||
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; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public abstract ExportClientResponse SendExportRequest(TRequest request, DateTime deadlineUtc, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <inheritdoc/>
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
||||
/// <summary>Base class for sending OTLP export request over HTTP.</summary>
|
||||
/// <typeparam name="TRequest">Type of export request.</typeparam>
|
||||
internal abstract class BaseOtlpHttpExportClient<TRequest> : IExportClient<TRequest>
|
||||
{
|
||||
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<Dictionary<string, string>>((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<string, string> Headers { get; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
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
|
||||
}
|
||||
}
|
||||
|
|
@ -4,17 +4,17 @@
|
|||
namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.ExportClient;
|
||||
|
||||
/// <summary>Export client interface.</summary>
|
||||
/// <typeparam name="TRequest">Type of export request.</typeparam>
|
||||
internal interface IExportClient<in TRequest>
|
||||
internal interface IExportClient
|
||||
{
|
||||
/// <summary>
|
||||
/// Method for sending export request to the server.
|
||||
/// </summary>
|
||||
/// <param name="request">The request to send to the server.</param>
|
||||
/// <param name="buffer">The request body to send to the server.</param>
|
||||
/// <param name="contentLength">length of the content.</param>
|
||||
/// <param name="deadlineUtc">The deadline time in utc for export request to finish.</param>
|
||||
/// <param name="cancellationToken">An optional token for canceling the call.</param>
|
||||
/// <returns><see cref="ExportClientResponse"/>.</returns>
|
||||
ExportClientResponse SendExportRequest(TRequest request, DateTime deadlineUtc, CancellationToken cancellationToken = default);
|
||||
ExportClientResponse SendExportRequest(byte[] buffer, int contentLength, DateTime deadlineUtc, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Method for shutting down the export client.
|
||||
|
|
|
|||
|
|
@ -1,30 +0,0 @@
|
|||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.ExportClient;
|
||||
|
||||
/// <summary>Export client interface.</summary>
|
||||
internal interface IProtobufExportClient
|
||||
{
|
||||
/// <summary>
|
||||
/// Method for sending export request to the server.
|
||||
/// </summary>
|
||||
/// <param name="buffer">The request body to send to the server.</param>
|
||||
/// <param name="contentLength">length of the content.</param>
|
||||
/// <param name="deadlineUtc">The deadline time in utc for export request to finish.</param>
|
||||
/// <param name="cancellationToken">An optional token for canceling the call.</param>
|
||||
/// <returns><see cref="ExportClientResponse"/>.</returns>
|
||||
ExportClientResponse SendExportRequest(byte[] buffer, int contentLength, DateTime deadlineUtc, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Method for shutting down the export client.
|
||||
/// </summary>
|
||||
/// <param name="timeoutMilliseconds">
|
||||
/// The number of milliseconds to wait, or <c>Timeout.Infinite</c> to
|
||||
/// wait indefinitely.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// Returns <c>true</c> if shutdown succeeded; otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
bool Shutdown(int timeoutMilliseconds);
|
||||
}
|
||||
|
|
@ -9,14 +9,14 @@ using OpenTelemetry.Internal;
|
|||
|
||||
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 @@ internal abstract class ProtobufOtlpExportClient : IProtobufExportClient
|
|||
}
|
||||
#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<Dictionary<string, string>>((d, k, v) => d.Add(k, v));
|
||||
this.HttpClient = httpClient;
|
||||
|
|
@ -10,7 +10,7 @@ using OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.ExportClient.G
|
|||
namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.ExportClient;
|
||||
|
||||
/// <summary>Base class for sending OTLP export request over gRPC.</summary>
|
||||
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 @@ internal sealed class ProtobufOtlpGrpcExportClient : ProtobufOtlpExportClient
|
|||
status: null,
|
||||
grpcStatusDetailsHeader: null);
|
||||
|
||||
public ProtobufOtlpGrpcExportClient(OtlpExporterOptions options, HttpClient httpClient, string signalPath)
|
||||
public OtlpGrpcExportClient(OtlpExporterOptions options, HttpClient httpClient, string signalPath)
|
||||
: base(options, httpClient, signalPath)
|
||||
{
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
||||
/// <summary>Class for sending OTLP Logs export request over gRPC.</summary>
|
||||
internal sealed class OtlpGrpcLogExportClient : BaseOtlpGrpcExportClient<OtlpCollector.ExportLogsServiceRequest>
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
||||
/// <summary>Class for sending OTLP metrics export request over gRPC.</summary>
|
||||
internal sealed class OtlpGrpcMetricsExportClient : BaseOtlpGrpcExportClient<OtlpCollector.ExportMetricsServiceRequest>
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -9,12 +9,12 @@ using System.Net.Http.Headers;
|
|||
namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.ExportClient;
|
||||
|
||||
/// <summary>Class for sending OTLP trace export request over HTTP.</summary>
|
||||
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)
|
||||
{
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
||||
/// <summary>Class for sending OTLP log export request over HTTP.</summary>
|
||||
internal sealed class OtlpHttpLogExportClient : BaseOtlpHttpExportClient<OtlpCollector.ExportLogsServiceRequest>
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
||||
/// <summary>Class for sending OTLP metrics export request over HTTP.</summary>
|
||||
internal sealed class OtlpHttpMetricsExportClient : BaseOtlpHttpExportClient<OtlpCollector.ExportMetricsServiceRequest>
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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 @@ internal static class OtlpRetry
|
|||
{
|
||||
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 @@ internal static class OtlpRetry
|
|||
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 @@ internal static class OtlpRetry
|
|||
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<RetryInfo>(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 @@ internal static class OtlpRetry
|
|||
}
|
||||
}
|
||||
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -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<ScopeMetrics> MetricListPool = new();
|
||||
|
||||
internal static void AddMetrics(
|
||||
this OtlpCollector.ExportMetricsServiceRequest request,
|
||||
OtlpResource.Resource processResource,
|
||||
in Batch<Metric> metrics)
|
||||
{
|
||||
var metricsByLibrary = new Dictionary<string, ScopeMetrics>();
|
||||
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<KeyValuePair<string, object?>>? 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>(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<OtlpCommon.KeyValue> attributes)
|
||||
{
|
||||
foreach (var tag in tags)
|
||||
{
|
||||
OtlpTagWriter.Instance.TryWriteTag(ref attributes, tag);
|
||||
}
|
||||
}
|
||||
|
||||
private static void AddScopeAttributes(IEnumerable<KeyValuePair<string, object?>> meterTags, RepeatedField<OtlpCommon.KeyValue> attributes)
|
||||
{
|
||||
foreach (var tag in meterTags)
|
||||
{
|
||||
OtlpTagWriter.Instance.TryWriteTag(ref attributes, tag);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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<OtlpLogs.ScopeLogs> 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<LogRecord> logRecordBatch)
|
||||
{
|
||||
// TODO: https://github.com/open-telemetry/opentelemetry-dotnet/issues/4943
|
||||
Dictionary<string, OtlpLogs.ScopeLogs> logsByCategory = new Dictionary<string, OtlpLogs.ScopeLogs>();
|
||||
|
||||
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<KeyValuePair<string, object?>>
|
||||
// or IEnumerable<KeyValuePair<string, object?>>.
|
||||
// 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<string, object?> 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<string, object?>(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<string, object?>(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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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<RepeatedField<OtlpCommon.KeyValue>, 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<OtlpCommon.KeyValue> tags, string key, long value)
|
||||
{
|
||||
tags.Add(new OtlpCommon.KeyValue { Key = key, Value = ToAnyValue(value) });
|
||||
}
|
||||
|
||||
protected override void WriteFloatingPointTag(ref RepeatedField<OtlpCommon.KeyValue> tags, string key, double value)
|
||||
{
|
||||
tags.Add(new OtlpCommon.KeyValue { Key = key, Value = ToAnyValue(value) });
|
||||
}
|
||||
|
||||
protected override void WriteBooleanTag(ref RepeatedField<OtlpCommon.KeyValue> tags, string key, bool value)
|
||||
{
|
||||
tags.Add(new OtlpCommon.KeyValue { Key = key, Value = ToAnyValue(value) });
|
||||
}
|
||||
|
||||
protected override void WriteStringTag(ref RepeatedField<OtlpCommon.KeyValue> tags, string key, ReadOnlySpan<char> value)
|
||||
{
|
||||
tags.Add(new OtlpCommon.KeyValue { Key = key, Value = ToAnyValue(value.ToString()) });
|
||||
}
|
||||
|
||||
protected override void WriteArrayTag(ref RepeatedField<OtlpCommon.KeyValue> 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<OtlpCommon.ArrayValue>
|
||||
{
|
||||
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<char> value)
|
||||
{
|
||||
array.Values.Add(ToAnyValue(value.ToString()));
|
||||
}
|
||||
|
||||
public override void EndWriteArray(ref OtlpCommon.ArrayValue array)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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.
|
||||
|
|
@ -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<string, object> 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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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<TRequest> : OtlpExporterTransmissionHandler<TRequest>, 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<TRequest>
|
|||
private readonly AutoResetEvent exportEvent = new(false);
|
||||
private readonly Thread thread;
|
||||
private readonly PersistentBlobProvider persistentBlobProvider;
|
||||
private readonly Func<byte[], TRequest> requestFactory;
|
||||
private bool disposed;
|
||||
|
||||
public OtlpExporterPersistentStorageTransmissionHandler(IExportClient<TRequest> exportClient, double timeoutMilliseconds, Func<byte[], TRequest> 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<TRequest> exportClient, double timeoutMilliseconds, Func<byte[], TRequest> 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 sealed class OtlpExporterPersistentStorageTransmissionHandler<TRequest>
|
|||
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 @@ internal sealed class OtlpExporterPersistentStorageTransmissionHandler<TRequest>
|
|||
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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,14 +5,14 @@ using OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.ExportClient;
|
|||
|
||||
namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.Transmission;
|
||||
|
||||
internal sealed class OtlpExporterRetryTransmissionHandler<TRequest> : OtlpExporterTransmissionHandler<TRequest>
|
||||
internal sealed class OtlpExporterRetryTransmissionHandler : OtlpExporterTransmissionHandler
|
||||
{
|
||||
internal OtlpExporterRetryTransmissionHandler(IExportClient<TRequest> 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 @@ internal sealed class OtlpExporterRetryTransmissionHandler<TRequest> : OtlpExpor
|
|||
// 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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,9 +7,9 @@ using OpenTelemetry.Internal;
|
|||
|
||||
namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.Transmission;
|
||||
|
||||
internal class OtlpExporterTransmissionHandler<TRequest> : IDisposable
|
||||
internal class OtlpExporterTransmissionHandler : IDisposable
|
||||
{
|
||||
public OtlpExporterTransmissionHandler(IExportClient<TRequest> exportClient, double timeoutMilliseconds)
|
||||
public OtlpExporterTransmissionHandler(IExportClient exportClient, double timeoutMilliseconds)
|
||||
{
|
||||
Guard.ThrowIfNull(exportClient);
|
||||
|
||||
|
|
@ -17,7 +17,7 @@ internal class OtlpExporterTransmissionHandler<TRequest> : IDisposable
|
|||
this.TimeoutMilliseconds = timeoutMilliseconds;
|
||||
}
|
||||
|
||||
internal IExportClient<TRequest> ExportClient { get; }
|
||||
internal IExportClient ExportClient { get; }
|
||||
|
||||
internal double TimeoutMilliseconds { get; }
|
||||
|
||||
|
|
@ -25,21 +25,22 @@ internal class OtlpExporterTransmissionHandler<TRequest> : IDisposable
|
|||
/// Attempts to send an export request to the server.
|
||||
/// </summary>
|
||||
/// <param name="request">The request to send to the server.</param>
|
||||
/// <param name="contentLength">length of content.</param>
|
||||
/// <returns> <see langword="true" /> if the request is sent successfully; otherwise, <see
|
||||
/// langword="false" />.
|
||||
/// </returns>
|
||||
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 @@ internal class OtlpExporterTransmissionHandler<TRequest> : IDisposable
|
|||
/// Fired when a request could not be submitted.
|
||||
/// </summary>
|
||||
/// <param name="request">The request that was attempted to send to the server.</param>
|
||||
/// <param name="contentLength">Length of content.</param>
|
||||
/// <param name="response"><see cref="ExportClientResponse" />.</param>
|
||||
/// <returns><see langword="true" /> If the request is resubmitted and succeeds; otherwise, <see
|
||||
/// langword="false" />.</returns>
|
||||
protected virtual bool OnSubmitRequestFailure(TRequest request, ExportClientResponse response)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
protected virtual bool OnSubmitRequestFailure(byte[] request, int contentLength, ExportClientResponse response) => false;
|
||||
|
||||
/// <summary>
|
||||
/// Fired when resending a request to the server.
|
||||
/// </summary>
|
||||
/// <param name="request">The request to be resent to the server.</param>
|
||||
/// <param name="contentLength">Length of content.</param>
|
||||
/// <param name="deadlineUtc">The deadline time in utc for export request to finish.</param>
|
||||
/// <param name="response"><see cref="ExportClientResponse" />.</param>
|
||||
/// <returns><see langword="true" /> If the retry succeeds; otherwise, <see
|
||||
/// langword="false" />.</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to send an export request to the server.
|
||||
/// </summary>
|
||||
/// <param name="request">The request to send to the server.</param>
|
||||
/// <param name="contentLength">length of content.</param>
|
||||
/// <returns> <see langword="true" /> if the request is sent successfully; otherwise, <see
|
||||
/// langword="false" />.
|
||||
/// </returns>
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to shutdown the transmission handler, blocks the current thread
|
||||
/// until shutdown completed or timed out.
|
||||
/// </summary>
|
||||
/// <param name="timeoutMilliseconds">
|
||||
/// The number (non-negative) of milliseconds to wait, or
|
||||
/// <c>Timeout.Infinite</c> to wait indefinitely.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// Returns <see langword="true" /> if shutdown succeeded; otherwise, <see
|
||||
/// langword="false" />.
|
||||
/// </returns>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Dispose()
|
||||
{
|
||||
this.Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fired when the transmission handler is shutdown.
|
||||
/// </summary>
|
||||
/// <param name="timeoutMilliseconds">
|
||||
/// The number (non-negative) of milliseconds to wait, or
|
||||
/// <c>Timeout.Infinite</c> to wait indefinitely.
|
||||
/// </param>
|
||||
protected virtual void OnShutdown(int timeoutMilliseconds)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fired when a request could not be submitted.
|
||||
/// </summary>
|
||||
/// <param name="request">The request that was attempted to send to the server.</param>
|
||||
/// <param name="contentLength">Length of content.</param>
|
||||
/// <param name="response"><see cref="ExportClientResponse" />.</param>
|
||||
/// <returns><see langword="true" /> If the request is resubmitted and succeeds; otherwise, <see
|
||||
/// langword="false" />.</returns>
|
||||
protected virtual bool OnSubmitRequestFailure(byte[] request, int contentLength, ExportClientResponse response) => false;
|
||||
|
||||
/// <summary>
|
||||
/// Fired when resending a request to the server.
|
||||
/// </summary>
|
||||
/// <param name="request">The request to be resent to the server.</param>
|
||||
/// <param name="contentLength">Length of content.</param>
|
||||
/// <param name="deadlineUtc">The deadline time in utc for export request to finish.</param>
|
||||
/// <param name="response"><see cref="ExportClientResponse" />.</param>
|
||||
/// <returns><see langword="true" /> If the retry succeeds; otherwise, <see
|
||||
/// langword="false" />.</returns>
|
||||
protected bool TryRetryRequest(byte[] request, int contentLength, DateTime deadlineUtc, out ExportClientResponse response)
|
||||
{
|
||||
response = this.ExportClient.SendExportRequest(request, contentLength, deadlineUtc);
|
||||
return response.Success;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases the unmanaged resources used by this class and optionally
|
||||
/// releases the managed resources.
|
||||
/// </summary>
|
||||
/// <param name="disposing">
|
||||
/// <see langword="true"/> to release both managed and unmanaged resources;
|
||||
/// <see langword="false"/> to release only unmanaged resources.
|
||||
/// </param>
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -23,13 +23,6 @@
|
|||
<ProjectReference Include="$(RepoRoot)\src\OpenTelemetry\OpenTelemetry.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Grpc.Net.Client" Condition="'$(TargetFramework)' != 'netstandard2.0' AND '$(TargetFramework)' != '$(NetFrameworkMinimumSupportedVersion)'" />
|
||||
<PackageReference Include="Grpc" Condition="'$(TargetFramework)' == 'netstandard2.0' OR '$(TargetFramework)' == '$(NetFrameworkMinimumSupportedVersion)'" />
|
||||
<PackageReference Include="Google.Protobuf" />
|
||||
<PackageReference Include="Grpc.Tools" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="System.Net.Http" Condition="'$(TargetFramework)' == '$(NetFrameworkMinimumSupportedVersion)'" />
|
||||
</ItemGroup>
|
||||
|
|
@ -41,10 +34,4 @@
|
|||
<Compile Include="$(RepoRoot)\src\Shared\TagWriter\TagWriter.cs" Link="Includes\TagWriter\TagWriter.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Protobuf Include="**/*.proto" Access="internal">
|
||||
<ProtoRoot>Implementation</ProtoRoot>
|
||||
</Protobuf>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
|||
|
|
@ -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<Metadata>((m, k, v) => m.Add(k, v));
|
||||
}
|
||||
|
||||
public static THeaders GetHeaders<THeaders>(this OtlpExporterOptions options, Action<THeaders, string, string> addHeader)
|
||||
where THeaders : new()
|
||||
{
|
||||
|
|
@ -98,37 +58,37 @@ internal static class OtlpExporterOptionsExtensions
|
|||
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 @@ internal static class OtlpExporterOptionsExtensions
|
|||
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<MetricsOtlpCollector.ExportMetricsServiceRequest> 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<MetricsOtlpCollector.ExportMetricsServiceRequest>(exportClient, timeoutMilliseconds);
|
||||
}
|
||||
else if (experimentalOptions.EnableDiskRetry)
|
||||
{
|
||||
Debug.Assert(!string.IsNullOrEmpty(experimentalOptions.DiskRetryDirectoryPath), $"{nameof(experimentalOptions.DiskRetryDirectoryPath)} is null or empty");
|
||||
|
||||
return new OtlpExporterPersistentStorageTransmissionHandler<MetricsOtlpCollector.ExportMetricsServiceRequest>(
|
||||
exportClient,
|
||||
timeoutMilliseconds,
|
||||
(byte[] data) =>
|
||||
{
|
||||
var request = new MetricsOtlpCollector.ExportMetricsServiceRequest();
|
||||
request.MergeFrom(data);
|
||||
return request;
|
||||
},
|
||||
Path.Combine(experimentalOptions.DiskRetryDirectoryPath, "metrics"));
|
||||
}
|
||||
else
|
||||
{
|
||||
return new OtlpExporterTransmissionHandler<MetricsOtlpCollector.ExportMetricsServiceRequest>(exportClient, timeoutMilliseconds);
|
||||
}
|
||||
}
|
||||
|
||||
public static OtlpExporterTransmissionHandler<LogOtlpCollector.ExportLogsServiceRequest> 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<LogOtlpCollector.ExportLogsServiceRequest>(exportClient, timeoutMilliseconds);
|
||||
}
|
||||
else if (experimentalOptions.EnableDiskRetry)
|
||||
{
|
||||
Debug.Assert(!string.IsNullOrEmpty(experimentalOptions.DiskRetryDirectoryPath), $"{nameof(experimentalOptions.DiskRetryDirectoryPath)} is null or empty");
|
||||
|
||||
return new OtlpExporterPersistentStorageTransmissionHandler<LogOtlpCollector.ExportLogsServiceRequest>(
|
||||
exportClient,
|
||||
timeoutMilliseconds,
|
||||
(byte[] data) =>
|
||||
{
|
||||
var request = new LogOtlpCollector.ExportLogsServiceRequest();
|
||||
request.MergeFrom(data);
|
||||
return request;
|
||||
},
|
||||
Path.Combine(experimentalOptions.DiskRetryDirectoryPath, "logs"));
|
||||
}
|
||||
else
|
||||
{
|
||||
return new OtlpExporterTransmissionHandler<LogOtlpCollector.ExportLogsServiceRequest>(exportClient, timeoutMilliseconds);
|
||||
}
|
||||
}
|
||||
|
||||
public static IExportClient<MetricsOtlpCollector.ExportMetricsServiceRequest> 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<LogOtlpCollector.ExportLogsServiceRequest> 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
|
||||
|
|
|
|||
|
|
@ -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;
|
|||
/// </summary>
|
||||
public sealed class OtlpLogExporter : BaseExporter<LogRecord>
|
||||
{
|
||||
private readonly OtlpExporterTransmissionHandler<OtlpCollector.ExportLogsServiceRequest> 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];
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="OtlpLogExporter"/> class.
|
||||
|
|
@ -36,24 +44,24 @@ public sealed class OtlpLogExporter : BaseExporter<LogRecord>
|
|||
/// <param name="exporterOptions"><see cref="OtlpExporterOptions"/>.</param>
|
||||
/// <param name="sdkLimitOptions"><see cref="SdkLimitOptions"/>.</param>
|
||||
/// <param name="experimentalOptions"><see cref="ExperimentalOptions"/>.</param>
|
||||
/// <param name="transmissionHandler"><see cref="OtlpExporterTransmissionHandler{T}"/>.</param>
|
||||
/// <param name="transmissionHandler"><see cref="OtlpExporterTransmissionHandler"/>.</param>
|
||||
internal OtlpLogExporter(
|
||||
OtlpExporterOptions exporterOptions,
|
||||
SdkLimitOptions sdkLimitOptions,
|
||||
ExperimentalOptions experimentalOptions,
|
||||
OtlpExporterTransmissionHandler<OtlpCollector.ExportLogsServiceRequest>? 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();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override ExportResult Export(in Batch<LogRecord> logRecordBatch)
|
||||
|
|
@ -61,36 +69,59 @@ public sealed class OtlpLogExporter : BaseExporter<LogRecord>
|
|||
// 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.transmissionHandler.TrySubmitRequest(request))
|
||||
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<byte> data = new Span<byte>(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;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (request != null)
|
||||
{
|
||||
this.otlpLogRecordTransformer.Return(request);
|
||||
}
|
||||
}
|
||||
|
||||
return ExportResult.Success;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -305,16 +305,10 @@ public static class OtlpLogExporterHelperExtensions
|
|||
* "OtlpLogExporter");
|
||||
*/
|
||||
|
||||
BaseExporter<LogRecord> otlpExporter;
|
||||
|
||||
if (experimentalOptions != null && experimentalOptions.UseCustomProtobufSerializer)
|
||||
{
|
||||
otlpExporter = new ProtobufOtlpLogExporter(exporterOptions!, sdkLimitOptions!, experimentalOptions!);
|
||||
}
|
||||
else
|
||||
{
|
||||
otlpExporter = new OtlpLogExporter(exporterOptions!, sdkLimitOptions!, experimentalOptions!);
|
||||
}
|
||||
BaseExporter<LogRecord> otlpExporter = new OtlpLogExporter(
|
||||
exporterOptions!,
|
||||
sdkLimitOptions!,
|
||||
experimentalOptions!);
|
||||
|
||||
if (configureExporterInstance != null)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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;
|
|||
/// </summary>
|
||||
public class OtlpMetricExporter : BaseExporter<Metric>
|
||||
{
|
||||
private readonly OtlpExporterTransmissionHandler<OtlpCollector.ExportMetricsServiceRequest> 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];
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="OtlpMetricExporter"/> class.
|
||||
|
|
@ -34,19 +41,20 @@ public class OtlpMetricExporter : BaseExporter<Metric>
|
|||
/// </summary>
|
||||
/// <param name="exporterOptions"><see cref="OtlpExporterOptions"/>.</param>
|
||||
/// <param name="experimentalOptions"><see cref="ExperimentalOptions"/>.</param>
|
||||
/// <param name="transmissionHandler"><see cref="OtlpExporterTransmissionHandler{T}"/>.</param>
|
||||
/// <param name="transmissionHandler"><see cref="OtlpExporterTransmissionHandler"/>.</param>
|
||||
internal OtlpMetricExporter(
|
||||
OtlpExporterOptions exporterOptions,
|
||||
ExperimentalOptions experimentalOptions,
|
||||
OtlpExporterTransmissionHandler<OtlpCollector.ExportMetricsServiceRequest>? 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();
|
||||
|
||||
/// <inheritdoc />
|
||||
public override ExportResult Export(in Batch<Metric> metrics)
|
||||
|
|
@ -54,33 +62,59 @@ public class OtlpMetricExporter : BaseExporter<Metric>
|
|||
// 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.transmissionHandler.TrySubmitRequest(request))
|
||||
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<byte> data = new Span<byte>(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;
|
||||
}
|
||||
finally
|
||||
{
|
||||
request.Return();
|
||||
}
|
||||
|
||||
return ExportResult.Success;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -175,16 +175,7 @@ public static class OtlpMetricExporterExtensions
|
|||
|
||||
exporterOptions!.TryEnableIHttpClientFactoryIntegration(serviceProvider!, "OtlpMetricExporter");
|
||||
|
||||
BaseExporter<Metric> metricExporter;
|
||||
|
||||
if (experimentalOptions != null && experimentalOptions.UseCustomProtobufSerializer)
|
||||
{
|
||||
metricExporter = new ProtobufOtlpMetricExporter(exporterOptions!, experimentalOptions!);
|
||||
}
|
||||
else
|
||||
{
|
||||
metricExporter = new OtlpMetricExporter(exporterOptions!, experimentalOptions!);
|
||||
}
|
||||
BaseExporter<Metric> metricExporter = new OtlpMetricExporter(exporterOptions!, experimentalOptions!);
|
||||
|
||||
if (configureExporterInstance != null)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ namespace OpenTelemetry.Exporter;
|
|||
public class OtlpTraceExporter : BaseExporter<Activity>
|
||||
{
|
||||
private readonly SdkLimitOptions sdkLimitOptions;
|
||||
private readonly ProtobufOtlpExporterTransmissionHandler transmissionHandler;
|
||||
private readonly OtlpExporterTransmissionHandler transmissionHandler;
|
||||
private readonly int startWritePosition;
|
||||
|
||||
private Resource? resource;
|
||||
|
|
@ -42,12 +42,12 @@ public class OtlpTraceExporter : BaseExporter<Activity>
|
|||
/// <param name="exporterOptions"><see cref="OtlpExporterOptions"/>.</param>
|
||||
/// <param name="sdkLimitOptions"><see cref="SdkLimitOptions"/>.</param>
|
||||
/// <param name="experimentalOptions"><see cref="ExperimentalOptions"/>.</param>
|
||||
/// <param name="transmissionHandler"><see cref="OtlpExporterTransmissionHandler{T}"/>.</param>
|
||||
/// <param name="transmissionHandler"><see cref="OtlpExporterTransmissionHandler"/>.</param>
|
||||
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");
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// Exporter consuming <see cref="LogRecord"/> and exporting the data using
|
||||
/// the OpenTelemetry protocol (OTLP).
|
||||
/// </summary>
|
||||
internal sealed class ProtobufOtlpLogExporter : BaseExporter<LogRecord>
|
||||
{
|
||||
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];
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ProtobufOtlpLogExporter"/> class.
|
||||
/// </summary>
|
||||
/// <param name="options">Configuration options for the exporter.</param>
|
||||
public ProtobufOtlpLogExporter(OtlpExporterOptions options)
|
||||
: this(options, sdkLimitOptions: new(), experimentalOptions: new(), transmissionHandler: null)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ProtobufOtlpLogExporter"/> class.
|
||||
/// </summary>
|
||||
/// <param name="exporterOptions"><see cref="OtlpExporterOptions"/>.</param>
|
||||
/// <param name="sdkLimitOptions"><see cref="SdkLimitOptions"/>.</param>
|
||||
/// <param name="experimentalOptions"><see cref="ExperimentalOptions"/>.</param>
|
||||
/// <param name="transmissionHandler"><see cref="OtlpExporterTransmissionHandler{T}"/>.</param>
|
||||
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();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override ExportResult Export(in Batch<LogRecord> 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<byte> data = new Span<byte>(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;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// Exporter consuming <see cref="Metric"/> and exporting the data using
|
||||
/// the OpenTelemetry protocol (OTLP).
|
||||
/// </summary>
|
||||
internal sealed class ProtobufOtlpMetricExporter : BaseExporter<Metric>
|
||||
{
|
||||
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];
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ProtobufOtlpMetricExporter"/> class.
|
||||
/// </summary>
|
||||
/// <param name="options">Configuration options for the exporter.</param>
|
||||
public ProtobufOtlpMetricExporter(OtlpExporterOptions options)
|
||||
: this(options, experimentalOptions: new(), transmissionHandler: null)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ProtobufOtlpMetricExporter"/> class.
|
||||
/// </summary>
|
||||
/// <param name="exporterOptions"><see cref="OtlpExporterOptions"/>.</param>
|
||||
/// <param name="experimentalOptions"><see cref="ExperimentalOptions"/>.</param>
|
||||
/// <param name="transmissionHandler"><see cref="OtlpExporterTransmissionHandler{T}"/>.</param>
|
||||
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();
|
||||
|
||||
/// <inheritdoc />
|
||||
public override ExportResult Export(in Batch<Metric> 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<byte> data = new Span<byte>(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;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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.
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
|
|
@ -27,4 +27,17 @@
|
|||
<ProjectReference Include="$(RepoRoot)\src\OpenTelemetry.Exporter.Prometheus.HttpListener\OpenTelemetry.Exporter.Prometheus.HttpListener.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Grpc.Net.Client" />
|
||||
<PackageReference Include="Grpc" />
|
||||
<PackageReference Include="Google.Protobuf" />
|
||||
<PackageReference Include="Grpc.Tools" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Protobuf Include="$(RepoRoot)\src\Shared\Proto\**\*.proto" Link="Proto\%(RecursiveDir)%(Filename)%(Extension)" Access="internal">
|
||||
<ProtoRoot>$(RepoRoot)\src\Shared\Proto</ProtoRoot>
|
||||
</Protobuf>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ public class OtlpGrpcExporterBenchmarks
|
|||
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<Activity>(this.NumberOfSpans);
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ public class OtlpHttpExporterBenchmarks
|
|||
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<Activity>(this.NumberOfSpans);
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ using OpenTelemetry.Internal;
|
|||
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)
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ using OpenTelemetry;
|
|||
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)
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -44,7 +44,7 @@ public class OtlpHttpTraceExportClientTests
|
|||
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 class OtlpHttpTraceExportClientTests
|
|||
|
||||
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 @@ public class OtlpHttpTraceExportClientTests
|
|||
// TODO: Revisit once the HttpClient part is overridden.
|
||||
// Assert.IsType<ProtobufOtlpHttpExportClient.ExportRequestContent>(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);
|
||||
|
|
|
|||
|
|
@ -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, object>
|
||||
{
|
||||
{ "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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -354,22 +354,22 @@ public sealed class MockCollectorIntegrationTests
|
|||
|
||||
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 sealed class MockCollectorIntegrationTests
|
|||
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 sealed class MockCollectorIntegrationTests
|
|||
|
||||
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 sealed class MockCollectorIntegrationTests
|
|||
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 _));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,6 +31,10 @@
|
|||
<PackageReference Include="Microsoft.NET.Test.Sdk" />
|
||||
<PackageReference Include="xunit" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" PrivateAssets="All" />
|
||||
<PackageReference Include="Grpc.Net.Client" />
|
||||
<PackageReference Include="Grpc" />
|
||||
<PackageReference Include="Google.Protobuf" />
|
||||
<PackageReference Include="Grpc.Tools" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
@ -49,4 +53,10 @@
|
|||
<Compile Include="$(RepoRoot)\test\OpenTelemetry.Tests\Shared\Utils.cs" Link="Includes\Utils.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Protobuf Include="$(RepoRoot)\src\Shared\Proto\**\*.proto" Link="Proto\%(RecursiveDir)%(Filename)%(Extension)" Access="internal">
|
||||
<ProtoRoot>$(RepoRoot)\src\Shared\Proto</ProtoRoot>
|
||||
</Protobuf>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
|||
|
|
@ -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 class OtlpAttributeTests
|
|||
|
||||
private static bool TryTransformTag(KeyValuePair<string, object?> tag, [NotNullWhen(true)] out OtlpCommon.KeyValue? attribute)
|
||||
{
|
||||
var destination = new RepeatedField<OtlpCommon.KeyValue>();
|
||||
|
||||
if (OtlpTagWriter.Instance.TryWriteTag(ref destination, tag))
|
||||
ProtobufOtlpTagWriter.OtlpTagWriterState otlpTagWriterState = new ProtobufOtlpTagWriter.OtlpTagWriterState
|
||||
{
|
||||
Assert.NotEmpty(destination);
|
||||
attribute = destination[0];
|
||||
Buffer = new byte[1024],
|
||||
WritePosition = 0,
|
||||
};
|
||||
|
||||
if (ProtobufOtlpTagWriter.Instance.TryWriteTag(ref otlpTagWriterState, tag))
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,67 +9,11 @@ using OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation;
|
|||
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<ArgumentException>(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 class OtlpExporterOptionsExtensionsTests
|
|||
}
|
||||
|
||||
[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 class OtlpExporterOptionsExtensionsTests
|
|||
}
|
||||
|
||||
[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 class OtlpExporterOptionsExtensionsTests
|
|||
.AddInMemoryCollection(new Dictionary<string, string?> { [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);
|
||||
}
|
||||
var transmissionHandler = exporterOptions.GetProtobufExportTransmissionHandler(new ExperimentalOptions(configuration), OtlpSignalType.Traces);
|
||||
AssertTransmissionHandler(transmissionHandler, exportClientType, expectedTimeoutMilliseconds, retryStrategy);
|
||||
}
|
||||
|
||||
private static void AssertTransmissionHandler<T>(OtlpExporterTransmissionHandler<T> 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 OtlpExporterRetryTransmissionHandler<T>);
|
||||
Assert.True(transmissionHandler is OtlpExporterRetryTransmissionHandler);
|
||||
}
|
||||
else if (retryStrategy == "disk")
|
||||
{
|
||||
Assert.True(transmissionHandler is OtlpExporterPersistentStorageTransmissionHandler<T>);
|
||||
Assert.True(transmissionHandler is OtlpExporterPersistentStorageTransmissionHandler);
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.True(transmissionHandler is OtlpExporterTransmissionHandler<T>);
|
||||
}
|
||||
|
||||
Assert.Equal(exportClientType, transmissionHandler.ExportClient.GetType());
|
||||
|
||||
Assert.Equal(expectedTimeoutMilliseconds, transmissionHandler.TimeoutMilliseconds);
|
||||
}
|
||||
|
||||
private static void AssertTransmissionHandler(ProtobufOtlpExporterTransmissionHandler transmissionHandler, Type exportClientType, int expectedTimeoutMilliseconds, string? retryStrategy)
|
||||
{
|
||||
if (retryStrategy == "in_memory")
|
||||
{
|
||||
Assert.True(transmissionHandler is ProtobufOtlpExporterRetryTransmissionHandler);
|
||||
}
|
||||
else if (retryStrategy == "disk")
|
||||
{
|
||||
Assert.True(transmissionHandler is ProtobufOtlpExporterPersistentStorageTransmissionHandler);
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.True(transmissionHandler is ProtobufOtlpExporterTransmissionHandler);
|
||||
Assert.True(transmissionHandler is OtlpExporterTransmissionHandler);
|
||||
}
|
||||
|
||||
Assert.Equal(exportClientType, transmissionHandler.ExportClient.GetType());
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ using Xunit;
|
|||
|
||||
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 class BaseOtlpHttpExportClientTests
|
|||
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 class BaseOtlpHttpExportClientTests
|
|||
Environment.SetEnvironmentVariable(OtlpSpecConfigDefinitions.DefaultEndpointEnvVarName, null);
|
||||
}
|
||||
}
|
||||
|
||||
internal class TestOtlpHttpExportClient : BaseOtlpHttpExportClient<string>
|
||||
{
|
||||
public TestOtlpHttpExportClient(OtlpExporterOptions options, HttpClient httpClient)
|
||||
: base(options, httpClient, "signal/path")
|
||||
{
|
||||
}
|
||||
|
||||
protected override HttpContent CreateHttpContent(string exportRequest)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -12,6 +12,7 @@ using OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.Serializer;
|
|||
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 class OtlpLogExporterTests
|
|||
#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<LogRecord>();
|
||||
using var loggerFactory = LoggerFactory.Create(builder =>
|
||||
|
|
@ -266,19 +265,8 @@ public class OtlpLogExporterTests
|
|||
|
||||
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 class OtlpLogExporterTests
|
|||
}
|
||||
|
||||
[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<LogRecord>();
|
||||
using var loggerFactory = LoggerFactory.Create(builder =>
|
||||
|
|
@ -327,20 +312,8 @@ public class OtlpLogExporterTests
|
|||
.AddInMemoryCollection(new Dictionary<string, string?> { [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 class OtlpLogExporterTests
|
|||
|
||||
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 class OtlpLogExporterTests
|
|||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(true)]
|
||||
[InlineData(false)]
|
||||
public void CheckToOtlpLogRecordTimestamps(bool useCustomSerializer)
|
||||
[Fact]
|
||||
public void CheckToOtlpLogRecordTimestamps()
|
||||
{
|
||||
var logRecords = new List<LogRecord>();
|
||||
using var loggerFactory = LoggerFactory.Create(builder =>
|
||||
|
|
@ -405,29 +369,17 @@ public class OtlpLogExporterTests
|
|||
|
||||
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<LogRecord>();
|
||||
using var loggerFactory = LoggerFactory.Create(builder =>
|
||||
|
|
@ -438,19 +390,8 @@ public class OtlpLogExporterTests
|
|||
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 class OtlpLogExporterTests
|
|||
Assert.Equal(0u, otlpLogRecord.Flags);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(true)]
|
||||
[InlineData(false)]
|
||||
public void CheckToOtlpLogRecordSpanIdTraceIdAndFlag(bool useCustomSerializer)
|
||||
[Fact]
|
||||
public void CheckToOtlpLogRecordSpanIdTraceIdAndFlag()
|
||||
{
|
||||
var logRecords = new List<LogRecord>();
|
||||
using var loggerFactory = LoggerFactory.Create(builder =>
|
||||
|
|
@ -482,20 +421,9 @@ public class OtlpLogExporterTests
|
|||
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 class OtlpLogExporterTests
|
|||
}
|
||||
|
||||
[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<LogRecord>();
|
||||
using var loggerFactory = LoggerFactory.Create(builder =>
|
||||
|
|
@ -532,19 +454,8 @@ public class OtlpLogExporterTests
|
|||
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 class OtlpLogExporterTests
|
|||
}
|
||||
|
||||
[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<LogRecord>();
|
||||
using var loggerFactory = LoggerFactory.Create(builder =>
|
||||
|
|
@ -600,19 +509,8 @@ public class OtlpLogExporterTests
|
|||
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 class OtlpLogExporterTests
|
|||
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 class OtlpLogExporterTests
|
|||
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 class OtlpLogExporterTests
|
|||
|
||||
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 class OtlpLogExporterTests
|
|||
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 class OtlpLogExporterTests
|
|||
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<LogRecord>();
|
||||
using var loggerFactory = LoggerFactory.Create(builder =>
|
||||
|
|
@ -747,18 +625,7 @@ public class OtlpLogExporterTests
|
|||
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 class OtlpLogExporterTests
|
|||
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 class OtlpLogExporterTests
|
|||
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 class OtlpLogExporterTests
|
|||
public void Export_WhenExportClientIsProvidedInCtor_UsesProvidedExportClient()
|
||||
{
|
||||
// Arrange.
|
||||
var testExportClient = new TestExportClient<OtlpCollector.ExportLogsServiceRequest>();
|
||||
var testExportClient = new TestExportClient();
|
||||
var exporterOptions = new OtlpExporterOptions();
|
||||
var transmissionHandler = new OtlpExporterTransmissionHandler<OtlpCollector.ExportLogsServiceRequest>(testExportClient, exporterOptions.TimeoutMilliseconds);
|
||||
var transmissionHandler = new OtlpExporterTransmissionHandler(testExportClient, exporterOptions.TimeoutMilliseconds);
|
||||
var emptyLogRecords = Array.Empty<LogRecord>();
|
||||
var emptyBatch = new Batch<LogRecord>(emptyLogRecords, emptyLogRecords.Length);
|
||||
var sut = new OtlpLogExporter(
|
||||
|
|
@ -854,9 +708,9 @@ public class OtlpLogExporterTests
|
|||
public void Export_WhenExportClientThrowsException_ReturnsExportResultFailure()
|
||||
{
|
||||
// Arrange.
|
||||
var testExportClient = new TestExportClient<OtlpCollector.ExportLogsServiceRequest>(throwException: true);
|
||||
var testExportClient = new TestExportClient(throwException: true);
|
||||
var exporterOptions = new OtlpExporterOptions();
|
||||
var transmissionHandler = new OtlpExporterTransmissionHandler<OtlpCollector.ExportLogsServiceRequest>(testExportClient, exporterOptions.TimeoutMilliseconds);
|
||||
var transmissionHandler = new OtlpExporterTransmissionHandler(testExportClient, exporterOptions.TimeoutMilliseconds);
|
||||
var emptyLogRecords = Array.Empty<LogRecord>();
|
||||
var emptyBatch = new Batch<LogRecord>(emptyLogRecords, emptyLogRecords.Length);
|
||||
var sut = new OtlpLogExporter(
|
||||
|
|
@ -876,9 +730,9 @@ public class OtlpLogExporterTests
|
|||
public void Export_WhenExportIsSuccessful_ReturnsExportResultSuccess()
|
||||
{
|
||||
// Arrange.
|
||||
var testExportClient = new TestExportClient<OtlpCollector.ExportLogsServiceRequest>();
|
||||
var testExportClient = new TestExportClient();
|
||||
var exporterOptions = new OtlpExporterOptions();
|
||||
var transmissionHandler = new OtlpExporterTransmissionHandler<OtlpCollector.ExportLogsServiceRequest>(testExportClient, exporterOptions.TimeoutMilliseconds);
|
||||
var transmissionHandler = new OtlpExporterTransmissionHandler(testExportClient, exporterOptions.TimeoutMilliseconds);
|
||||
var emptyLogRecords = Array.Empty<LogRecord>();
|
||||
var emptyBatch = new Batch<LogRecord>(emptyLogRecords, emptyLogRecords.Length);
|
||||
var sut = new OtlpLogExporter(
|
||||
|
|
@ -894,10 +748,8 @@ public class OtlpLogExporterTests
|
|||
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<LogRecord>(1);
|
||||
|
|
@ -923,17 +775,7 @@ public class OtlpLogExporterTests
|
|||
|
||||
// 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 class OtlpLogExporterTests
|
|||
}
|
||||
|
||||
[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<LogRecord>(1);
|
||||
|
|
@ -970,11 +810,7 @@ public class OtlpLogExporterTests
|
|||
|
||||
// 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 class OtlpLogExporterTests
|
|||
}
|
||||
|
||||
[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<LogRecord>(1);
|
||||
|
|
@ -1015,11 +849,7 @@ public class OtlpLogExporterTests
|
|||
|
||||
// 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 class OtlpLogExporterTests
|
|||
}
|
||||
|
||||
[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<LogRecord>(1);
|
||||
|
|
@ -1084,11 +900,7 @@ public class OtlpLogExporterTests
|
|||
|
||||
// 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 class OtlpLogExporterTests
|
|||
}
|
||||
|
||||
[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<LogRecord>(1);
|
||||
|
|
@ -1129,11 +939,8 @@ public class OtlpLogExporterTests
|
|||
|
||||
// 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 class OtlpLogExporterTests
|
|||
}
|
||||
|
||||
[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<LogRecord>(1);
|
||||
|
|
@ -1174,11 +979,7 @@ public class OtlpLogExporterTests
|
|||
|
||||
// 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 class OtlpLogExporterTests
|
|||
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<LogRecord>(1);
|
||||
|
|
@ -1213,28 +1012,19 @@ public class OtlpLogExporterTests
|
|||
|
||||
// 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<LogRecord>(1);
|
||||
|
|
@ -1257,20 +1047,14 @@ public class OtlpLogExporterTests
|
|||
|
||||
// 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<LogRecord>(1);
|
||||
|
|
@ -1294,11 +1078,7 @@ public class OtlpLogExporterTests
|
|||
|
||||
// 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 class OtlpLogExporterTests
|
|||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(typeof(List<KeyValuePair<string, object>>), false)]
|
||||
[InlineData(typeof(ReadOnlyCollection<KeyValuePair<string, object>>), false)]
|
||||
[InlineData(typeof(HashSet<KeyValuePair<string, object>>), false)]
|
||||
[InlineData(typeof(List<KeyValuePair<string, object>>), true)]
|
||||
[InlineData(typeof(ReadOnlyCollection<KeyValuePair<string, object>>), true)]
|
||||
[InlineData(typeof(HashSet<KeyValuePair<string, object>>), true)]
|
||||
public void ToOtlpLog_WhenOptionsIncludeScopesIsTrue_AndScopeStateIsOfEnumerableType_ScopeIsProcessed(Type typeOfScopeState, bool useCustomSerializer)
|
||||
[InlineData(typeof(List<KeyValuePair<string, object>>))]
|
||||
[InlineData(typeof(ReadOnlyCollection<KeyValuePair<string, object>>))]
|
||||
[InlineData(typeof(HashSet<KeyValuePair<string, object>>))]
|
||||
public void ToOtlpLog_WhenOptionsIncludeScopesIsTrue_AndScopeStateIsOfEnumerableType_ScopeIsProcessed(Type typeOfScopeState)
|
||||
{
|
||||
// Arrange.
|
||||
var logRecords = new List<LogRecord>(1);
|
||||
|
|
@ -1341,11 +1118,7 @@ public class OtlpLogExporterTests
|
|||
|
||||
// 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 class OtlpLogExporterTests
|
|||
}
|
||||
|
||||
[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<LogRecord>(1);
|
||||
|
|
@ -1387,11 +1158,7 @@ public class OtlpLogExporterTests
|
|||
|
||||
// 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 class OtlpLogExporterTests
|
|||
}
|
||||
|
||||
[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<LogRecord>(1);
|
||||
|
|
@ -1434,11 +1199,7 @@ public class OtlpLogExporterTests
|
|||
|
||||
// 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 class OtlpLogExporterTests
|
|||
}
|
||||
|
||||
[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<LogRecord>(1);
|
||||
|
|
@ -1486,11 +1245,7 @@ public class OtlpLogExporterTests
|
|||
|
||||
// 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 class OtlpLogExporterTests
|
|||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(true)]
|
||||
[InlineData(false)]
|
||||
public void ValidateInstrumentationScope(bool useCustomSerializer)
|
||||
[Fact]
|
||||
public void ValidateInstrumentationScope()
|
||||
{
|
||||
var logRecords = new List<LogRecord>();
|
||||
using var loggerFactory = LoggerFactory.Create(builder =>
|
||||
|
|
@ -1591,20 +1344,10 @@ public class OtlpLogExporterTests
|
|||
Assert.Equal(2, logRecords.Count);
|
||||
|
||||
var batch = new Batch<LogRecord>(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 class OtlpLogExporterTests
|
|||
|
||||
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 class OtlpLogExporterTests
|
|||
}
|
||||
|
||||
[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 class OtlpLogExporterTests
|
|||
|
||||
Assert.Single(logRecords);
|
||||
|
||||
var otlpLogRecordTransformer = new OtlpLogRecordTransformer(DefaultSdkLimitOptions, new());
|
||||
|
||||
var batch = new Batch<LogRecord>(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 @@ public class OtlpLogExporterTests
|
|||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ using System.Reflection;
|
|||
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 class OtlpMetricsExporterTests : IDisposable
|
|||
}
|
||||
|
||||
[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 class OtlpMetricsExporterTests : IDisposable
|
|||
provider.ForceFlush();
|
||||
|
||||
var batch = new Batch<Metric>(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 class OtlpMetricsExporterTests : IDisposable
|
|||
}
|
||||
|
||||
[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<Metric>();
|
||||
|
||||
|
|
@ -271,16 +251,7 @@ public class OtlpMetricsExporterTests : IDisposable
|
|||
|
||||
var batch = new Batch<Metric>(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 class OtlpMetricsExporterTests : IDisposable
|
|||
}
|
||||
|
||||
[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<Metric>();
|
||||
|
||||
|
|
@ -370,17 +330,7 @@ public class OtlpMetricsExporterTests : IDisposable
|
|||
provider.ForceFlush();
|
||||
|
||||
var batch = new Batch<Metric>(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 class OtlpMetricsExporterTests : IDisposable
|
|||
}
|
||||
|
||||
[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<Metric>();
|
||||
|
||||
|
|
@ -489,15 +426,7 @@ public class OtlpMetricsExporterTests : IDisposable
|
|||
|
||||
var batch = new Batch<Metric>(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 class OtlpMetricsExporterTests : IDisposable
|
|||
}
|
||||
|
||||
[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<Metric>();
|
||||
|
||||
|
|
@ -611,16 +527,7 @@ public class OtlpMetricsExporterTests : IDisposable
|
|||
provider.ForceFlush();
|
||||
|
||||
var batch = new Batch<Metric>(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 class OtlpMetricsExporterTests : IDisposable
|
|||
}
|
||||
|
||||
[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<Metric>();
|
||||
|
||||
|
|
@ -765,16 +659,7 @@ public class OtlpMetricsExporterTests : IDisposable
|
|||
provider.ForceFlush();
|
||||
|
||||
var batch = new Batch<Metric>(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 class OtlpMetricsExporterTests : IDisposable
|
|||
}
|
||||
|
||||
[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 class OtlpMetricsExporterTests : IDisposable
|
|||
meterProvider.ForceFlush();
|
||||
|
||||
var batch = new Batch<Metric>(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();
|
||||
|
|
|
|||
|
|
@ -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 class OtlpResourceTests
|
|||
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)
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ using System.Net.Http;
|
|||
#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 class OtlpRetryTests
|
|||
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 class OtlpRetryTests
|
|||
|
||||
if (retryResult.Throttled)
|
||||
{
|
||||
Assert.Equal(retryAttempt.ThrottleDelay, retryResult.RetryDelay);
|
||||
Assert.Equal(GrpcStatusDeserializer.TryGetGrpcRetryDelay(retryAttempt.ThrottleDelay), retryResult.RetryDelay);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -131,9 +130,9 @@ public class OtlpRetryTests
|
|||
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 class OtlpRetryTests
|
|||
{
|
||||
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 class OtlpRetryTests
|
|||
return this.testRunnerName;
|
||||
}
|
||||
|
||||
private static Metadata GenerateTrailers(Duration throttleDelay)
|
||||
private static string GetThrottleDelayString(Duration throttleDelay)
|
||||
{
|
||||
var metadata = new Metadata();
|
||||
var status = new Google.Rpc.Status
|
||||
{
|
||||
Code = 4,
|
||||
Message = "Only nanos",
|
||||
Details =
|
||||
{
|
||||
Any.Pack(new Google.Rpc.RetryInfo
|
||||
{
|
||||
RetryDelay = throttleDelay,
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
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;
|
||||
|
||||
|
|
|
|||
|
|
@ -743,10 +743,10 @@ public class OtlpTraceExporterTests
|
|||
[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();
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ using OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.ExportClient;
|
|||
|
||||
namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests;
|
||||
|
||||
internal class TestExportClient<T>(bool throwException = false) : IExportClient<T>
|
||||
internal class TestExportClient(bool throwException = false) : IExportClient
|
||||
{
|
||||
public bool SendExportRequestCalled { get; private set; }
|
||||
|
||||
|
|
@ -13,7 +13,7 @@ internal class TestExportClient<T>(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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue