Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
- removed usage of signal specific endpoints for Traces and Metrics (OTEL_EXPORTER_OTLP_TRACES_ENDPOINT, OTEL_EXPORTER_OTLP_METRICS_ENDPOINT) - it should be considered along with other signal specific env. variables in a scope of dedicated PR
- implemented according to Protocol specification rule for  appending of traces path (if not already present) in the OtlpHttpTraceExportClient class
- unit tests
  • Loading branch information
rypdal committed Sep 24, 2021
1 parent 63da651 commit ea93e71
Show file tree
Hide file tree
Showing 5 changed files with 48 additions and 149 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,12 @@ namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.ExportClie
internal sealed class OtlpHttpTraceExportClient : BaseOtlpHttpExportClient<OtlpCollector.ExportTraceServiceRequest>
{
internal const string MediaContentType = "application/x-protobuf";
private readonly Uri exportTracesUri;

public OtlpHttpTraceExportClient(OtlpExporterOptions options, IHttpHandler httpHandler = null)
: base(options, httpHandler)
{
this.exportTracesUri = this.Options.Endpoint.AppendPathIfNotPresent(OtlpExporterOptions.TracesExportPath);
}

/// <inheritdoc/>
Expand All @@ -57,7 +59,7 @@ public override bool SendExportRequest(OtlpCollector.ExportTraceServiceRequest r

private HttpRequestMessage CreateHttpRequest(OtlpCollector.ExportTraceServiceRequest exportRequest)
{
var request = new HttpRequestMessage(HttpMethod.Post, this.Options.TracesEndpoint);
var request = new HttpRequestMessage(HttpMethod.Post, this.exportTracesUri);
foreach (var header in this.Headers)
{
request.Headers.Add(header.Key, header.Value);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,11 @@ namespace OpenTelemetry.Exporter
public class OtlpExporterOptions
{
internal const string EndpointEnvVarName = "OTEL_EXPORTER_OTLP_ENDPOINT";
internal const string TracesEndpointEnvVarName = "OTEL_EXPORTER_OTLP_TRACES_ENDPOINT";
internal const string MetricsEndpointEnvVarName = "OTEL_EXPORTER_OTLP_METRICS_ENDPOINT";
internal const string HeadersEnvVarName = "OTEL_EXPORTER_OTLP_HEADERS";
internal const string TimeoutEnvVarName = "OTEL_EXPORTER_OTLP_TIMEOUT";
internal const string ProtocolEnvVarName = "OTEL_EXPORTER_OTLP_PROTOCOL";

internal const string TraceExportPath = "v1/traces";
internal const string TracesExportPath = "v1/traces";
internal const string MetricsExportPath = "v1/metrics";

/// <summary>
Expand All @@ -45,23 +43,19 @@ public OtlpExporterOptions()
{
try
{
// Protocol initialization should come before endpoints as it's initialization logic depends on the protocol.
var protocolEnvVar = Environment.GetEnvironmentVariable(ProtocolEnvVarName);
if (!string.IsNullOrEmpty(protocolEnvVar))
string endpointEnvVar = Environment.GetEnvironmentVariable(EndpointEnvVarName);
if (!string.IsNullOrEmpty(endpointEnvVar))
{
var protocol = protocolEnvVar.ToExportProtocol();
if (protocol.HasValue)
if (Uri.TryCreate(endpointEnvVar, UriKind.Absolute, out var endpoint))
{
this.Protocol = protocol.Value;
this.Endpoint = endpoint;
}
else
{
OpenTelemetryProtocolExporterEventSource.Log.UnsupportedProtocol(protocolEnvVar);
OpenTelemetryProtocolExporterEventSource.Log.FailedToParseEnvironmentVariable(EndpointEnvVarName, endpointEnvVar);
}
}

this.InitializeEndpoints(this.Protocol);

string headersEnvVar = Environment.GetEnvironmentVariable(HeadersEnvVarName);
if (!string.IsNullOrEmpty(headersEnvVar))
{
Expand All @@ -80,6 +74,20 @@ public OtlpExporterOptions()
OpenTelemetryProtocolExporterEventSource.Log.FailedToParseEnvironmentVariable(TimeoutEnvVarName, timeoutEnvVar);
}
}

string protocolEnvVar = Environment.GetEnvironmentVariable(ProtocolEnvVarName);
if (!string.IsNullOrEmpty(protocolEnvVar))
{
var protocol = protocolEnvVar.ToExportProtocol();
if (protocol.HasValue)
{
this.Protocol = protocol.Value;
}
else
{
OpenTelemetryProtocolExporterEventSource.Log.UnsupportedProtocol(protocolEnvVar);
}
}
}
catch (SecurityException ex)
{
Expand All @@ -89,29 +97,13 @@ public OtlpExporterOptions()
}
}

/// <summary>
/// Gets or sets the target to which the exporter is going to send traces or metrics.
/// Must be a valid Uri with scheme (http) and host, and
/// may contain a port and path. Secure connection(https) is not
/// supported.
/// </summary>
public Uri Endpoint { get; set; } = new Uri("http://localhost:4317");

/// <summary>
/// Gets or sets the target to which the exporter is going to send traces.
/// Must be a valid Uri with scheme (http) and host, and
/// may contain a port and path. Secure connection(https) is not
/// supported.
/// </summary>
public Uri TracesEndpoint { get; set; } = new Uri("http://localhost:4317");

/// <summary>
/// Gets or sets the target to which the exporter is going to send metrics.
/// Must be a valid Uri with scheme (http) and host, and
/// may contain a port and path. Secure connection(https) is not
/// supported.
/// </summary>
public Uri MetricsEndpoint { get; set; } = new Uri("http://localhost:4317");
public Uri Endpoint { get; set; } = new Uri("http://localhost:4317");

/// <summary>
/// Gets or sets optional headers for the connection. Refer to the <a href="https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/protocol/exporter.md#specifying-headers-via-environment-variables">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,79 +104,31 @@ public static THeaders GetHeaders<THeaders>(this OtlpExporterOptions options, Ac
_ => null
};

public static void InitializeEndpoints(this OtlpExporterOptions options, ExportProtocol protocol)
public static Uri AppendPathIfNotPresent(this Uri uri, string path)
{
switch (protocol)
{
case ExportProtocol.Grpc:
InitializeGrpcEndpoints(options);
break;
case ExportProtocol.HttpProtobuf:
InitializeHttpProtobufEndpoints(options);
break;
default:
throw new NotSupportedException($"Export protocol {protocol} is not supported.");
}
}

private static void InitializeGrpcEndpoints(OtlpExporterOptions options)
{
if (TryCreateUriFromEndpointEnvVar(OtlpExporterOptions.EndpointEnvVarName, out var endpoint))
{
options.Endpoint = endpoint;
}
}
var absoluteUri = uri.AbsoluteUri;
var separator = string.Empty;

private static void InitializeHttpProtobufEndpoints(OtlpExporterOptions options)
{
if (TryCreateUriFromEndpointEnvVar(OtlpExporterOptions.EndpointEnvVarName, out var endpoint))
if (absoluteUri.EndsWith("/"))
{
options.Endpoint = endpoint;

if (Uri.TryCreate(endpoint.AbsoluteUri.AppendPath(OtlpExporterOptions.TraceExportPath), UriKind.Absolute, out var traceEndpointUri))
// Endpoint already ends with 'path/'
if (absoluteUri.EndsWith(string.Concat(path, "/"), StringComparison.OrdinalIgnoreCase))
{
options.TracesEndpoint = traceEndpointUri;
return uri;
}

if (Uri.TryCreate(endpoint.AbsoluteUri.AppendPath(OtlpExporterOptions.MetricsExportPath), UriKind.Absolute, out var metricsEndpointUri))
{
options.MetricsEndpoint = metricsEndpointUri;
}
}

if (TryCreateUriFromEndpointEnvVar(OtlpExporterOptions.TracesEndpointEnvVarName, out var tracesEndpoint))
{
options.TracesEndpoint = tracesEndpoint;
}

if (TryCreateUriFromEndpointEnvVar(OtlpExporterOptions.MetricsEndpointEnvVarName, out var metricsEndpoint))
{
options.MetricsEndpoint = metricsEndpoint;
}
}

private static string AppendPath(this string endpoint, string path)
{
var separator = endpoint.EndsWith("/") ? string.Empty : "/";
return string.Concat(endpoint, separator, path);
}

private static bool TryCreateUriFromEndpointEnvVar(string endpointEnvVarName, out Uri uri)
{
uri = null;
var uriCreated = false;
var endpointEnvVar = Environment.GetEnvironmentVariable(endpointEnvVarName);

if (!string.IsNullOrEmpty(endpointEnvVar))
else
{
uriCreated = Uri.TryCreate(endpointEnvVar, UriKind.Absolute, out uri);
if (!uriCreated)
// Endpoint already ends with 'path'
if (absoluteUri.EndsWith(path, StringComparison.OrdinalIgnoreCase))
{
OpenTelemetryProtocolExporterEventSource.Log.FailedToParseEnvironmentVariable(OtlpExporterOptions.EndpointEnvVarName, endpointEnvVar);
return uri;
}

separator = "/";
}

return uriCreated;
return new Uri(string.Concat(uri.AbsoluteUri, separator, path));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ public void SendExportRequest_ExportTraceServiceRequest_SendsCorrectHttpRequest(
Assert.True(result);
Assert.NotNull(httpRequest);
Assert.Equal(HttpMethod.Post, httpRequest.Method);
Assert.Equal(options.Endpoint.AbsoluteUri, httpRequest.RequestUri.AbsoluteUri);
Assert.Equal("http://localhost:4317/v1/traces", httpRequest.RequestUri.AbsoluteUri);
Assert.Equal(2, httpRequest.Headers.Count());
Assert.Contains(httpRequest.Headers, h => h.Key == header1.Name && h.Value.First() == header1.Value);
Assert.Contains(httpRequest.Headers, h => h.Key == header2.Name && h.Value.First() == header2.Value);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,65 +118,18 @@ public void ToExportProtocol_Protocol_MapsToCorrectExportProtocol(string protoco
Assert.Equal(expectedExportProtocol, exportProtocol);
}

[Fact]
public void InitializeEndpoints_Grpc_OptionsHasCorrectEndpoints()
{
Environment.SetEnvironmentVariable(OtlpExporterOptions.EndpointEnvVarName, "http://test:8888");

var options = new OtlpExporterOptions
{
Endpoint = new Uri("https://test.com"),
};

options.InitializeEndpoints(ExportProtocol.Grpc);

Assert.Equal(new Uri("http://test:8888"), options.Endpoint);
Assert.Equal(new Uri("http://localhost:4317"), options.TracesEndpoint);
Assert.Equal(new Uri("http://localhost:4317"), options.MetricsEndpoint);

Environment.SetEnvironmentVariable(OtlpExporterOptions.EndpointEnvVarName, null);
}

[Fact]
public void InitializeEndpoints_HttpProtobuf_SignalSpecificEndpointEnvVarsNotDefined_OptionsHasCorrectEndpoints()
{
Environment.SetEnvironmentVariable(OtlpExporterOptions.EndpointEnvVarName, "http://test:8888");

var options = new OtlpExporterOptions
{
Endpoint = new Uri("https://test.com"),
};

options.InitializeEndpoints(ExportProtocol.HttpProtobuf);

Assert.Equal(new Uri("http://test:8888"), options.Endpoint);
Assert.Equal(new Uri($"http://test:8888/{OtlpExporterOptions.TraceExportPath}"), options.TracesEndpoint);
Assert.Equal(new Uri($"http://test:8888/{OtlpExporterOptions.MetricsExportPath}"), options.MetricsEndpoint);

Environment.SetEnvironmentVariable(OtlpExporterOptions.EndpointEnvVarName, null);
}

[Fact]
public void InitializeEndpoints_HttpProtobuf_SignalSpecificEndpointEnvVarsDefined_OptionsHasCorrectEndpoints()
[Theory]
[InlineData("http://test:8888", "http://test:8888/v1/traces")]
[InlineData("http://test:8888/", "http://test:8888/v1/traces")]
[InlineData("http://test:8888/v1/traces", "http://test:8888/v1/traces")]
[InlineData("http://test:8888/v1/traces/", "http://test:8888/v1/traces/")]
public void AppendPathIfNotPresent_TracesPath_AppendsCorrectly(string inputUri, string expectedUri)
{
Environment.SetEnvironmentVariable(OtlpExporterOptions.EndpointEnvVarName, "http://test:8888");
Environment.SetEnvironmentVariable(OtlpExporterOptions.TracesEndpointEnvVarName, "http://test/mytraces");
Environment.SetEnvironmentVariable(OtlpExporterOptions.MetricsEndpointEnvVarName, "http://test/somemetrics");

var options = new OtlpExporterOptions
{
Endpoint = new Uri("https://test.com"),
};

options.InitializeEndpoints(ExportProtocol.HttpProtobuf);
var uri = new Uri(inputUri, UriKind.Absolute);

Assert.Equal(new Uri("http://test:8888"), options.Endpoint);
Assert.Equal(new Uri("http://test/mytraces"), options.TracesEndpoint);
Assert.Equal(new Uri("http://test/somemetrics"), options.MetricsEndpoint);
var resultUri = uri.AppendPathIfNotPresent(OtlpExporterOptions.TracesExportPath);

Environment.SetEnvironmentVariable(OtlpExporterOptions.EndpointEnvVarName, null);
Environment.SetEnvironmentVariable(OtlpExporterOptions.TracesEndpointEnvVarName, null);
Environment.SetEnvironmentVariable(OtlpExporterOptions.MetricsEndpointEnvVarName, null);
Assert.Equal(expectedUri, resultUri.AbsoluteUri);
}
}
}

0 comments on commit ea93e71

Please sign in to comment.