Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_EXPORTER_OTLP_HEADERS and OTEL_EXPORTER_OTLP_TIMEOUT env vars #2188

Merged
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@

## Unreleased

* The `OtlpExporterOptions` defaults can be overridden using
`OTEL_EXPORTER_OTLP_ENDPOINT`, `OTEL_EXPORTER_OTLP_HEADERS` and `OTEL_EXPORTER_OTLP_TIMEOUT`
envionmental variables as defined in the
[specification](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/protocol/exporter.md).
([#2188](https://github.com/open-telemetry/opentelemetry-dotnet/pull/2188))

## 1.2.0-alpha1

Released 2021-Jul-23
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

using System;
using System.Diagnostics.Tracing;
using System.Security;
using OpenTelemetry.Internal;

namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation
Expand All @@ -25,6 +26,15 @@ internal class OpenTelemetryProtocolExporterEventSource : EventSource
{
public static readonly OpenTelemetryProtocolExporterEventSource Log = new OpenTelemetryProtocolExporterEventSource();

[NonEvent]
public void MissingPermissionsToReadEnvironmentVariable(SecurityException ex)
{
if (this.IsEnabled(EventLevel.Warning, EventKeywords.All))
{
this.MissingPermissionsToReadEnvironmentVariable(ex.ToInvariantString());
}
}

[NonEvent]
public void FailedToConvertToProtoDefinitionError(Exception ex)
{
Expand Down Expand Up @@ -81,5 +91,17 @@ public void CouldNotTranslateMetric(string className, string methodName)
{
this.WriteEvent(5, className, methodName);
}

[Event(6, Message = "Failed to parse environment variable: '{0}', value: '{1}'.", Level = EventLevel.Warning)]
public void FailedToParseEnvironmentVariable(string name, string value)
{
this.WriteEvent(6, name, value);
}

[Event(7, Message = "Missing permissions to read environment variable: '{0}'", Level = EventLevel.Warning)]
public void MissingPermissionsToReadEnvironmentVariable(string exception)
{
this.WriteEvent(7, exception);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

using System;
using System.Diagnostics;
using System.Security;
using OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation;

namespace OpenTelemetry.Exporter
{
Expand All @@ -24,6 +26,57 @@ namespace OpenTelemetry.Exporter
/// </summary>
public class OtlpExporterOptions
{
internal const string EndpointEnvVarName = "OTEL_EXPORTER_OTLP_ENDPOINT";
internal const string HeadersEnvVarName = "OTEL_EXPORTER_OTLP_HEADERS";
internal const string TimeoutEnvVarName = "OTEL_EXPORTER_OTLP_TIMEOUT";

/// <summary>
/// Initializes a new instance of the <see cref="OtlpExporterOptions"/> class.
/// </summary>
public OtlpExporterOptions()
{
try
{
string endpointEnvVar = Environment.GetEnvironmentVariable(EndpointEnvVarName);
if (!string.IsNullOrEmpty(endpointEnvVar))
{
if (Uri.TryCreate(endpointEnvVar, UriKind.Absolute, out var endpoint))
{
this.Endpoint = endpoint;
}
else
{
OpenTelemetryProtocolExporterEventSource.Log.FailedToParseEnvironmentVariable(EndpointEnvVarName, endpointEnvVar);
}
}

string headersEnvVar = Environment.GetEnvironmentVariable(HeadersEnvVarName);
if (!string.IsNullOrEmpty(headersEnvVar))
{
this.Headers = headersEnvVar;
}

string timeoutEnvVar = Environment.GetEnvironmentVariable(TimeoutEnvVarName);
if (!string.IsNullOrEmpty(timeoutEnvVar))
{
if (int.TryParse(timeoutEnvVar, out var timeout))
{
this.TimeoutMilliseconds = timeout;
}
else
{
OpenTelemetryProtocolExporterEventSource.Log.FailedToParseEnvironmentVariable(TimeoutEnvVarName, timeoutEnvVar);
}
}
}
catch (SecurityException ex)
{
// The caller does not have the required permission to
// retrieve the value of an environment variable from the current process.
OpenTelemetryProtocolExporterEventSource.Log.MissingPermissionsToReadEnvironmentVariable(ex);
}
}

/// <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
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
// <copyright file="OtlpExporterOptionsTests.cs" company="OpenTelemetry Authors">
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// </copyright>

using System;
using Xunit;

namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests
{
public class OtlpExporterOptionsTests : IDisposable
{
public OtlpExporterOptionsTests()
{
ClearEnvVars();
}

public void Dispose()
{
ClearEnvVars();
}

[Fact]
public void OtlpExporterOptions_Defaults()
{
var options = new OtlpExporterOptions();

Assert.Equal(new Uri("http://localhost:4317"), options.Endpoint);
Assert.Null(options.Headers);
Assert.Equal(10000, options.TimeoutMilliseconds);
}

[Fact]
public void OtlpExporterOptions_EnvironmentVariableOverride()
{
Environment.SetEnvironmentVariable(OtlpExporterOptions.EndpointEnvVarName, "http://test:8888");
Environment.SetEnvironmentVariable(OtlpExporterOptions.HeadersEnvVarName, "A=2,B=3");
Environment.SetEnvironmentVariable(OtlpExporterOptions.TimeoutEnvVarName, "2000");

var options = new OtlpExporterOptions();

Assert.Equal(new Uri("http://test:8888"), options.Endpoint);
Assert.Equal("A=2,B=3", options.Headers);
Assert.Equal(2000, options.TimeoutMilliseconds);
}

[Fact]
public void OtlpExporterOptions_InvalidEndpointVariableOverride()
{
Environment.SetEnvironmentVariable(OtlpExporterOptions.EndpointEnvVarName, "invalid");

var options = new OtlpExporterOptions();

Assert.Equal(new Uri("http://localhost:4317"), options.Endpoint); // use default
}

[Fact]
public void OtlpExporterOptions_InvalidTimeoutVariableOverride()
{
Environment.SetEnvironmentVariable(OtlpExporterOptions.TimeoutEnvVarName, "invalid");

var options = new OtlpExporterOptions();

Assert.Equal(10000, options.TimeoutMilliseconds); // use default
}

[Fact]
public void OtlpExporterOptions_SetterOverridesEnvironmentVariable()
{
Environment.SetEnvironmentVariable(OtlpExporterOptions.EndpointEnvVarName, "http://test:8888");
Environment.SetEnvironmentVariable(OtlpExporterOptions.HeadersEnvVarName, "A=2,B=3");
Environment.SetEnvironmentVariable(OtlpExporterOptions.TimeoutEnvVarName, "2000");

var options = new OtlpExporterOptions
{
Endpoint = new Uri("http://localhost:200"),
Headers = "C=3",
TimeoutMilliseconds = 40000,
};

Assert.Equal(new Uri("http://localhost:200"), options.Endpoint);
Assert.Equal("C=3", options.Headers);
Assert.Equal(40000, options.TimeoutMilliseconds);
}

[Fact]
public void OtlpExporterOptions_EnvironmentVariableNames()
{
Assert.Equal("OTEL_EXPORTER_OTLP_ENDPOINT", OtlpExporterOptions.EndpointEnvVarName);
Assert.Equal("OTEL_EXPORTER_OTLP_HEADERS", OtlpExporterOptions.HeadersEnvVarName);
Assert.Equal("OTEL_EXPORTER_OTLP_TIMEOUT", OtlpExporterOptions.TimeoutEnvVarName);
}

private static void ClearEnvVars()
{
Environment.SetEnvironmentVariable(OtlpExporterOptions.EndpointEnvVarName, null);
Environment.SetEnvironmentVariable(OtlpExporterOptions.HeadersEnvVarName, null);
Environment.SetEnvironmentVariable(OtlpExporterOptions.TimeoutEnvVarName, null);
}
}
}