forked from open-telemetry/opentelemetry-dotnet
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[otlp] Add Trace Exporter to transmit custom serialized data. (open-t…
…elemetry#5969) Co-authored-by: Mikel Blanchard <mblanchard@macrosssoftware.com>
- Loading branch information
1 parent
b201d70
commit 1e7397e
Showing
16 changed files
with
786 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
30 changes: 30 additions & 0 deletions
30
...metry.Exporter.OpenTelemetryProtocol/Implementation/ExportClient/IProtobufExportClient.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
// 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); | ||
} |
102 changes: 102 additions & 0 deletions
102
...xporter.OpenTelemetryProtocol/Implementation/ExportClient/ProtobufOtlpGrpcExportClient.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
// Copyright The OpenTelemetry Authors | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
#if NETFRAMEWORK | ||
using System.Net.Http; | ||
#endif | ||
using System.Net.Http.Headers; | ||
using Grpc.Core; | ||
using OpenTelemetry.Internal; | ||
|
||
namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.ExportClient; | ||
|
||
/// <summary>Base class for sending OTLP export request over gRPC.</summary> | ||
internal sealed class ProtobufOtlpGrpcExportClient : IProtobufExportClient | ||
{ | ||
private static readonly ExportClientHttpResponse SuccessExportResponse = new(success: true, deadlineUtc: default, response: null, exception: null); | ||
private static readonly MediaTypeHeaderValue MediaHeaderValue = new("application/grpc"); | ||
private static readonly Version Http2RequestVersion = new(2, 0); | ||
|
||
public ProtobufOtlpGrpcExportClient(OtlpExporterOptions options, HttpClient httpClient, string signalPath) | ||
{ | ||
Guard.ThrowIfNull(options); | ||
Guard.ThrowIfNull(httpClient); | ||
Guard.ThrowIfNull(signalPath); | ||
Guard.ThrowIfInvalidTimeout(options.TimeoutMilliseconds); | ||
|
||
Uri exporterEndpoint = options.Endpoint.AppendPathIfNotPresent(signalPath); | ||
this.Endpoint = new UriBuilder(exporterEndpoint).Uri; | ||
this.Headers = options.GetHeaders<Dictionary<string, string>>((d, k, v) => d.Add(k, v)); | ||
this.HttpClient = httpClient; | ||
} | ||
|
||
internal HttpClient HttpClient { get; } | ||
|
||
internal Uri Endpoint { get; set; } | ||
|
||
internal IReadOnlyDictionary<string, string> Headers { get; } | ||
|
||
internal int TimeoutMilliseconds { get; } | ||
|
||
/// <inheritdoc/> | ||
public ExportClientResponse SendExportRequest(byte[] buffer, int contentLength, DateTime deadlineUtc, CancellationToken cancellationToken = default) | ||
{ | ||
try | ||
{ | ||
using var httpRequest = this.CreateHttpRequest(buffer, contentLength); | ||
|
||
using var httpResponse = this.SendHttpRequest(httpRequest, cancellationToken); | ||
|
||
try | ||
{ | ||
httpResponse.EnsureSuccessStatusCode(); | ||
} | ||
catch (HttpRequestException ex) | ||
{ | ||
return new ExportClientHttpResponse(success: false, deadlineUtc: deadlineUtc, response: httpResponse, ex); | ||
} | ||
|
||
// TODO: Hande retries & failures. | ||
return SuccessExportResponse; | ||
} | ||
catch (RpcException ex) | ||
{ | ||
OpenTelemetryProtocolExporterEventSource.Log.FailedToReachCollector(this.Endpoint, ex); | ||
return new ExportClientGrpcResponse(success: false, deadlineUtc: deadlineUtc, exception: ex); | ||
} | ||
} | ||
|
||
public HttpRequestMessage CreateHttpRequest(byte[] buffer, int contentLength) | ||
{ | ||
var request = new HttpRequestMessage(HttpMethod.Post, this.Endpoint); | ||
request.Version = Http2RequestVersion; | ||
|
||
#if NET6_0_OR_GREATER | ||
request.VersionPolicy = HttpVersionPolicy.RequestVersionExact; | ||
#endif | ||
|
||
foreach (var header in this.Headers) | ||
{ | ||
request.Headers.Add(header.Key, header.Value); | ||
} | ||
|
||
// TODO: Support compression. | ||
|
||
request.Content = new ByteArrayContent(buffer, 0, contentLength); | ||
request.Content.Headers.ContentType = MediaHeaderValue; | ||
|
||
return request; | ||
} | ||
|
||
public HttpResponseMessage SendHttpRequest(HttpRequestMessage request, CancellationToken cancellationToken) | ||
{ | ||
return this.HttpClient.SendAsync(request, cancellationToken).GetAwaiter().GetResult(); | ||
} | ||
|
||
/// <inheritdoc/> | ||
public bool Shutdown(int timeoutMilliseconds) | ||
{ | ||
this.HttpClient.CancelPendingRequests(); | ||
return true; | ||
} | ||
} |
110 changes: 110 additions & 0 deletions
110
...xporter.OpenTelemetryProtocol/Implementation/ExportClient/ProtobufOtlpHttpExportClient.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
// Copyright The OpenTelemetry Authors | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
#if NETFRAMEWORK | ||
using System.Net.Http; | ||
#endif | ||
using System.Net.Http.Headers; | ||
using OpenTelemetry.Internal; | ||
|
||
namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.ExportClient; | ||
|
||
/// <summary>Class for sending OTLP trace export request over HTTP.</summary> | ||
internal sealed class ProtobufOtlpHttpExportClient : IProtobufExportClient | ||
{ | ||
private static readonly MediaTypeHeaderValue MediaHeaderValue = new("application/x-protobuf"); | ||
private static readonly ExportClientHttpResponse SuccessExportResponse = new(success: true, deadlineUtc: default, response: null, exception: null); | ||
#if NET | ||
private readonly bool synchronousSendSupportedByCurrentPlatform; | ||
#endif | ||
|
||
internal ProtobufOtlpHttpExportClient(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(byte[] buffer, int contentLength, DateTime deadlineUtc, CancellationToken cancellationToken = default) | ||
{ | ||
try | ||
{ | ||
using var httpRequest = this.CreateHttpRequest(buffer, contentLength); | ||
|
||
using var httpResponse = this.SendHttpRequest(httpRequest, cancellationToken); | ||
|
||
try | ||
{ | ||
httpResponse.EnsureSuccessStatusCode(); | ||
} | ||
catch (HttpRequestException ex) | ||
{ | ||
return new ExportClientHttpResponse(success: false, deadlineUtc: deadlineUtc, response: httpResponse, ex); | ||
} | ||
|
||
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; | ||
} | ||
|
||
public HttpRequestMessage CreateHttpRequest(byte[] exportRequest, int contentLength) | ||
{ | ||
var request = new HttpRequestMessage(HttpMethod.Post, this.Endpoint); | ||
|
||
foreach (var header in this.Headers) | ||
{ | ||
request.Headers.Add(header.Key, header.Value); | ||
} | ||
|
||
var content = new ByteArrayContent(exportRequest, 0, contentLength); | ||
content.Headers.ContentType = MediaHeaderValue; | ||
request.Content = content; | ||
|
||
return request; | ||
} | ||
|
||
public 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 | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.