diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md
index d42846d6996..c464714f603 100644
--- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md
+++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md
@@ -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
diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/OpenTelemetryProtocolExporterEventSource.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/OpenTelemetryProtocolExporterEventSource.cs
index 27d4ec3de9e..1824c82fb69 100644
--- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/OpenTelemetryProtocolExporterEventSource.cs
+++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/OpenTelemetryProtocolExporterEventSource.cs
@@ -16,6 +16,7 @@
using System;
using System.Diagnostics.Tracing;
+using System.Security;
using OpenTelemetry.Internal;
namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation
@@ -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)
{
@@ -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);
+ }
}
}
diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterOptions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterOptions.cs
index 7102bab456c..f112e76a773 100644
--- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterOptions.cs
+++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpExporterOptions.cs
@@ -16,6 +16,8 @@
using System;
using System.Diagnostics;
+using System.Security;
+using OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation;
namespace OpenTelemetry.Exporter
{
@@ -24,6 +26,57 @@ namespace OpenTelemetry.Exporter
///
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";
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ 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);
+ }
+ }
+
///
/// 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
diff --git a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpExporterOptionsTests.cs b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpExporterOptionsTests.cs
new file mode 100644
index 00000000000..d7a698a0d6c
--- /dev/null
+++ b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpExporterOptionsTests.cs
@@ -0,0 +1,112 @@
+//
+// 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.
+//
+
+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);
+ }
+ }
+}