Consolidate environment variable parsing (#2500)

This commit is contained in:
Robert Pająk 2021-10-26 23:54:02 +02:00 committed by GitHub
parent 105c068928
commit 3aab1244f8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 362 additions and 235 deletions

View File

@ -2,6 +2,10 @@
## Unreleased ## Unreleased
* Changed `JaegerExporterOptions` constructor to throw
`FormatException` if it fails to parse any of the supported environment
variables.
## 1.2.0-beta1 ## 1.2.0-beta1
Released 2021-Oct-08 Released 2021-Oct-08

View File

@ -16,7 +16,6 @@
using System; using System;
using System.Diagnostics.Tracing; using System.Diagnostics.Tracing;
using System.Security;
using OpenTelemetry.Internal; using OpenTelemetry.Internal;
namespace OpenTelemetry.Exporter.Jaeger.Implementation namespace OpenTelemetry.Exporter.Jaeger.Implementation
@ -38,31 +37,10 @@ namespace OpenTelemetry.Exporter.Jaeger.Implementation
} }
} }
[NonEvent]
public void MissingPermissionsToReadEnvironmentVariable(SecurityException ex)
{
if (this.IsEnabled(EventLevel.Warning, EventKeywords.All))
{
this.MissingPermissionsToReadEnvironmentVariable(ex.ToInvariantString());
}
}
[Event(1, Message = "Failed to send spans: '{0}'", Level = EventLevel.Error)] [Event(1, Message = "Failed to send spans: '{0}'", Level = EventLevel.Error)]
public void FailedExport(string exception) public void FailedExport(string exception)
{ {
this.WriteEvent(1, exception); this.WriteEvent(1, exception);
} }
[Event(2, Message = "Failed to parse environment variable: '{0}', value: '{1}'.", Level = EventLevel.Warning)]
public void FailedToParseEnvironmentVariable(string name, string value)
{
this.WriteEvent(2, name, value);
}
[Event(3, Message = "Missing permissions to read environment variable: '{0}'", Level = EventLevel.Warning)]
public void MissingPermissionsToReadEnvironmentVariable(string exception)
{
this.WriteEvent(3, exception);
}
} }
} }

View File

@ -16,12 +16,20 @@
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.Security; using OpenTelemetry.Internal;
using OpenTelemetry.Exporter.Jaeger.Implementation;
using OpenTelemetry.Trace; using OpenTelemetry.Trace;
namespace OpenTelemetry.Exporter namespace OpenTelemetry.Exporter
{ {
/// <summary>
/// Jaeger exporter options.
/// OTEL_EXPORTER_JAEGER_AGENT_HOST, OTEL_EXPORTER_JAEGER_AGENT_PORT
/// environment variables are parsed during object construction.
/// </summary>
/// <remarks>
/// The constructor throws <see cref="FormatException"/> if it fails to parse
/// any of the supported environment variables.
/// </remarks>
public class JaegerExporterOptions public class JaegerExporterOptions
{ {
internal const int DefaultMaxPayloadSizeInBytes = 4096; internal const int DefaultMaxPayloadSizeInBytes = 4096;
@ -31,32 +39,14 @@ namespace OpenTelemetry.Exporter
public JaegerExporterOptions() public JaegerExporterOptions()
{ {
try if (EnvironmentVariableHelper.LoadString(OTelAgentHostEnvVarKey, out string agentHostEnvVar))
{ {
string agentHostEnvVar = Environment.GetEnvironmentVariable(OTelAgentHostEnvVarKey); this.AgentHost = agentHostEnvVar;
if (!string.IsNullOrEmpty(agentHostEnvVar))
{
this.AgentHost = agentHostEnvVar;
}
string agentPortEnvVar = Environment.GetEnvironmentVariable(OTelAgentPortEnvVarKey);
if (!string.IsNullOrEmpty(agentPortEnvVar))
{
if (int.TryParse(agentPortEnvVar, out var agentPortValue))
{
this.AgentPort = agentPortValue;
}
else
{
JaegerExporterEventSource.Log.FailedToParseEnvironmentVariable(OTelAgentPortEnvVarKey, agentPortEnvVar);
}
}
} }
catch (SecurityException ex)
if (EnvironmentVariableHelper.LoadNumeric(OTelAgentPortEnvVarKey, out int agentPortEnvVar))
{ {
// The caller does not have the required permission to this.AgentPort = agentPortEnvVar;
// retrieve the value of an environment variable from the current process.
JaegerExporterEventSource.Log.MissingPermissionsToReadEnvironmentVariable(ex);
} }
} }

View File

@ -26,6 +26,8 @@
<Compile Include="$(RepoRoot)\src\OpenTelemetry.Api\Internal\IActivityEnumerator.cs" Link="Includes\IActivityEnumerator.cs" /> <Compile Include="$(RepoRoot)\src\OpenTelemetry.Api\Internal\IActivityEnumerator.cs" Link="Includes\IActivityEnumerator.cs" />
<Compile Include="$(RepoRoot)\src\OpenTelemetry.Api\Internal\EnumerationHelper.cs" Link="Includes\EnumerationHelper.cs" /> <Compile Include="$(RepoRoot)\src\OpenTelemetry.Api\Internal\EnumerationHelper.cs" Link="Includes\EnumerationHelper.cs" />
<Compile Include="$(RepoRoot)\src\OpenTelemetry.Api\Internal\Guard.cs" Link="Includes\Guard.cs" /> <Compile Include="$(RepoRoot)\src\OpenTelemetry.Api\Internal\Guard.cs" Link="Includes\Guard.cs" />
<Compile Include="$(RepoRoot)\src\OpenTelemetry\Internal\EnvironmentVariableHelper.cs" Link="Includes\EnvironmentVariableHelper.cs" />
<Compile Include="$(RepoRoot)\src\OpenTelemetry\Internal\OpenTelemetrySdkEventSource.cs" Link="Includes\OpenTelemetrySdkEventSource.cs" />
<Compile Include="$(RepoRoot)\src\OpenTelemetry\Internal\PooledList.cs" Link="Includes\PooledList.cs" /> <Compile Include="$(RepoRoot)\src\OpenTelemetry\Internal\PooledList.cs" Link="Includes\PooledList.cs" />
<Compile Include="$(RepoRoot)\src\OpenTelemetry\Internal\PeerServiceResolver.cs" Link="Includes\PeerServiceResolver.cs" /> <Compile Include="$(RepoRoot)\src\OpenTelemetry\Internal\PeerServiceResolver.cs" Link="Includes\PeerServiceResolver.cs" />
<Compile Include="$(RepoRoot)\src\OpenTelemetry\Internal\ResourceSemanticConventions.cs" Link="Includes\ResourceSemanticConventions.cs" /> <Compile Include="$(RepoRoot)\src\OpenTelemetry\Internal\ResourceSemanticConventions.cs" Link="Includes\ResourceSemanticConventions.cs" />

View File

@ -62,6 +62,9 @@ values of the `JaegerExporterOptions`.
| `OTEL_EXPORTER_JAEGER_AGENT_HOST` | `AgentHost` | | `OTEL_EXPORTER_JAEGER_AGENT_HOST` | `AgentHost` |
| `OTEL_EXPORTER_JAEGER_AGENT_PORT` | `AgentPort` | | `OTEL_EXPORTER_JAEGER_AGENT_PORT` | `AgentPort` |
`FormatException` is thrown in case of an invalid value for any of the
supported environment variables.
## References ## References
* [Jaeger](https://www.jaegertracing.io) * [Jaeger](https://www.jaegertracing.io)

View File

@ -2,6 +2,10 @@
## Unreleased ## Unreleased
* Changed `OtlpExporterOptions` constructor to throw
`FormatException` if it fails to parse any of the supported environment
variables.
## 1.2.0-beta1 ## 1.2.0-beta1
Released 2021-Oct-08 Released 2021-Oct-08

View File

@ -16,7 +16,6 @@
using System; using System;
using System.Diagnostics.Tracing; using System.Diagnostics.Tracing;
using System.Security;
using OpenTelemetry.Internal; using OpenTelemetry.Internal;
namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation
@ -26,24 +25,6 @@ namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation
{ {
public static readonly OpenTelemetryProtocolExporterEventSource Log = new OpenTelemetryProtocolExporterEventSource(); 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)
{
if (Log.IsEnabled(EventLevel.Error, EventKeywords.All))
{
this.FailedToConvertToProtoDefinitionError(ex.ToInvariantString());
}
}
[NonEvent] [NonEvent]
public void FailedToReachCollector(Exception ex) public void FailedToReachCollector(Exception ex)
{ {
@ -62,12 +43,6 @@ namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation
} }
} }
[Event(1, Message = "Exporter failed to convert SpanData content into gRPC proto definition. Data will not be sent. Exception: {0}", Level = EventLevel.Error)]
public void FailedToConvertToProtoDefinitionError(string ex)
{
this.WriteEvent(1, ex);
}
[Event(2, Message = "Exporter failed send data to collector. Data will not be sent. Exception: {0}", Level = EventLevel.Error)] [Event(2, Message = "Exporter failed send data to collector. Data will not be sent. Exception: {0}", Level = EventLevel.Error)]
public void FailedToReachCollector(string ex) public void FailedToReachCollector(string ex)
{ {
@ -92,18 +67,6 @@ namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation
this.WriteEvent(5, className, 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);
}
[Event(8, Message = "Unsupported value for protocol '{0}' is configured, default protocol 'grpc' will be used.", Level = EventLevel.Warning)] [Event(8, Message = "Unsupported value for protocol '{0}' is configured, default protocol 'grpc' will be used.", Level = EventLevel.Warning)]
public void UnsupportedProtocol(string protocol) public void UnsupportedProtocol(string protocol)
{ {

View File

@ -43,6 +43,8 @@
<Compile Include="$(RepoRoot)\src\OpenTelemetry.Api\Internal\IActivityEnumerator.cs" Link="Includes\IActivityEnumerator.cs" /> <Compile Include="$(RepoRoot)\src\OpenTelemetry.Api\Internal\IActivityEnumerator.cs" Link="Includes\IActivityEnumerator.cs" />
<Compile Include="$(RepoRoot)\src\OpenTelemetry.Api\Internal\EnumerationHelper.cs" Link="Includes\EnumerationHelper.cs" /> <Compile Include="$(RepoRoot)\src\OpenTelemetry.Api\Internal\EnumerationHelper.cs" Link="Includes\EnumerationHelper.cs" />
<Compile Include="$(RepoRoot)\src\OpenTelemetry.Api\Internal\Guard.cs" Link="Includes\Guard.cs" /> <Compile Include="$(RepoRoot)\src\OpenTelemetry.Api\Internal\Guard.cs" Link="Includes\Guard.cs" />
<Compile Include="$(RepoRoot)\src\OpenTelemetry\Internal\EnvironmentVariableHelper.cs" Link="Includes\EnvironmentVariableHelper.cs" />
<Compile Include="$(RepoRoot)\src\OpenTelemetry\Internal\OpenTelemetrySdkEventSource.cs" Link="Includes\OpenTelemetrySdkEventSource.cs" />
<Compile Include="$(RepoRoot)\src\OpenTelemetry\Internal\PooledList.cs" Link="Includes\PooledList.cs" /> <Compile Include="$(RepoRoot)\src\OpenTelemetry\Internal\PooledList.cs" Link="Includes\PooledList.cs" />
<Compile Include="$(RepoRoot)\src\OpenTelemetry\Internal\PeerServiceResolver.cs" Link="Includes\PeerServiceResolver.cs" /> <Compile Include="$(RepoRoot)\src\OpenTelemetry\Internal\PeerServiceResolver.cs" Link="Includes\PeerServiceResolver.cs" />
<Compile Include="$(RepoRoot)\src\OpenTelemetry\Internal\ResourceSemanticConventions.cs" Link="Includes\ResourceSemanticConventions.cs" /> <Compile Include="$(RepoRoot)\src\OpenTelemetry\Internal\ResourceSemanticConventions.cs" Link="Includes\ResourceSemanticConventions.cs" />

View File

@ -16,16 +16,21 @@
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.Security; using OpenTelemetry.Internal;
using OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation;
using OpenTelemetry.Metrics; using OpenTelemetry.Metrics;
using OpenTelemetry.Trace; using OpenTelemetry.Trace;
namespace OpenTelemetry.Exporter namespace OpenTelemetry.Exporter
{ {
/// <summary> /// <summary>
/// Configuration options for the OpenTelemetry Protocol (OTLP) exporter. /// OpenTelemetry Protocol (OTLP) exporter options.
/// OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_EXPORTER_OTLP_HEADERS, OTEL_EXPORTER_OTLP_TIMEOUT, OTEL_EXPORTER_OTLP_PROTOCOL
/// environment variables are parsed during object construction.
/// </summary> /// </summary>
/// <remarks>
/// The constructor throws <see cref="FormatException"/> if it fails to parse
/// any of the supported environment variables.
/// </remarks>
public class OtlpExporterOptions public class OtlpExporterOptions
{ {
internal const string EndpointEnvVarName = "OTEL_EXPORTER_OTLP_ENDPOINT"; internal const string EndpointEnvVarName = "OTEL_EXPORTER_OTLP_ENDPOINT";
@ -41,59 +46,32 @@ namespace OpenTelemetry.Exporter
/// </summary> /// </summary>
public OtlpExporterOptions() public OtlpExporterOptions()
{ {
try if (EnvironmentVariableHelper.LoadUri(EndpointEnvVarName, out Uri endpoint))
{ {
string endpointEnvVar = Environment.GetEnvironmentVariable(EndpointEnvVarName); this.Endpoint = endpoint;
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);
}
}
string protocolEnvVar = Environment.GetEnvironmentVariable(ProtocolEnvVarName);
if (!string.IsNullOrEmpty(protocolEnvVar))
{
var protocol = protocolEnvVar.ToOtlpExportProtocol();
if (protocol.HasValue)
{
this.Protocol = protocol.Value;
}
else
{
OpenTelemetryProtocolExporterEventSource.Log.UnsupportedProtocol(protocolEnvVar);
}
}
} }
catch (SecurityException ex)
if (EnvironmentVariableHelper.LoadString(HeadersEnvVarName, out string headersEnvVar))
{ {
// The caller does not have the required permission to this.Headers = headersEnvVar;
// retrieve the value of an environment variable from the current process. }
OpenTelemetryProtocolExporterEventSource.Log.MissingPermissionsToReadEnvironmentVariable(ex);
if (EnvironmentVariableHelper.LoadNumeric(TimeoutEnvVarName, out int timeout))
{
this.TimeoutMilliseconds = timeout;
}
if (EnvironmentVariableHelper.LoadString(ProtocolEnvVarName, out string protocolEnvVar))
{
var protocol = protocolEnvVar.ToOtlpExportProtocol();
if (protocol.HasValue)
{
this.Protocol = protocol.Value;
}
else
{
throw new FormatException($"{ProtocolEnvVarName} environment variable has an invalid value: '${protocolEnvVar}'");
}
} }
} }

View File

@ -54,6 +54,9 @@ values of the `OtlpExporterOptions`
| `OTEL_EXPORTER_OTLP_TIMEOUT` | `TimeoutMilliseconds` | | `OTEL_EXPORTER_OTLP_TIMEOUT` | `TimeoutMilliseconds` |
| `OTEL_EXPORTER_OTLP_PROTOCOL` | `Protocol` (grpc or http/protobuf)| | `OTEL_EXPORTER_OTLP_PROTOCOL` | `Protocol` (grpc or http/protobuf)|
`FormatException` is thrown in case of an invalid value for any of the
supported environment variables.
## OTLP Logs ## OTLP Logs
This package currently only supports exporting traces and metrics. Once the This package currently only supports exporting traces and metrics. Once the

View File

@ -2,6 +2,10 @@
## Unreleased ## Unreleased
* Changed `ZipkinExporterOptions` constructor to throw
`FormatException` if it fails to parse any of the supported environment
variables.
## 1.2.0-beta1 ## 1.2.0-beta1
Released 2021-Oct-08 Released 2021-Oct-08

View File

@ -37,25 +37,10 @@ namespace OpenTelemetry.Exporter.Zipkin.Implementation
} }
} }
[NonEvent]
public void FailedEndpointInitialization(Exception ex)
{
if (this.IsEnabled(EventLevel.Error, EventKeywords.All))
{
this.FailedEndpointInitialization(ex.ToInvariantString());
}
}
[Event(1, Message = "Failed to export activities: '{0}'", Level = EventLevel.Error)] [Event(1, Message = "Failed to export activities: '{0}'", Level = EventLevel.Error)]
public void FailedExport(string exception) public void FailedExport(string exception)
{ {
this.WriteEvent(1, exception); this.WriteEvent(1, exception);
} }
[Event(2, Message = "Error initializing Zipkin endpoint, falling back to default value: '{0}'", Level = EventLevel.Error)]
public void FailedEndpointInitialization(string exception)
{
this.WriteEvent(2, exception);
}
} }
} }

View File

@ -17,6 +17,8 @@
<Compile Include="$(RepoRoot)\src\OpenTelemetry.Api\Internal\IActivityEnumerator.cs" Link="Includes\IActivityEnumerator.cs" /> <Compile Include="$(RepoRoot)\src\OpenTelemetry.Api\Internal\IActivityEnumerator.cs" Link="Includes\IActivityEnumerator.cs" />
<Compile Include="$(RepoRoot)\src\OpenTelemetry.Api\Internal\EnumerationHelper.cs" Link="Includes\EnumerationHelper.cs" /> <Compile Include="$(RepoRoot)\src\OpenTelemetry.Api\Internal\EnumerationHelper.cs" Link="Includes\EnumerationHelper.cs" />
<Compile Include="$(RepoRoot)\src\OpenTelemetry.Api\Internal\Guard.cs" Link="Includes\Guard.cs" /> <Compile Include="$(RepoRoot)\src\OpenTelemetry.Api\Internal\Guard.cs" Link="Includes\Guard.cs" />
<Compile Include="$(RepoRoot)\src\OpenTelemetry\Internal\EnvironmentVariableHelper.cs" Link="Includes\EnvironmentVariableHelper.cs" />
<Compile Include="$(RepoRoot)\src\OpenTelemetry\Internal\OpenTelemetrySdkEventSource.cs" Link="Includes\OpenTelemetrySdkEventSource.cs" />
<Compile Include="$(RepoRoot)\src\OpenTelemetry\Internal\PooledList.cs" Link="Includes\PooledList.cs" /> <Compile Include="$(RepoRoot)\src\OpenTelemetry\Internal\PooledList.cs" Link="Includes\PooledList.cs" />
<Compile Include="$(RepoRoot)\src\OpenTelemetry\Internal\PeerServiceResolver.cs" Link="Includes\PeerServiceResolver.cs" /> <Compile Include="$(RepoRoot)\src\OpenTelemetry\Internal\PeerServiceResolver.cs" Link="Includes\PeerServiceResolver.cs" />
<Compile Include="$(RepoRoot)\src\OpenTelemetry\Internal\ResourceSemanticConventions.cs" Link="Includes\ResourceSemanticConventions.cs" /> <Compile Include="$(RepoRoot)\src\OpenTelemetry\Internal\ResourceSemanticConventions.cs" Link="Includes\ResourceSemanticConventions.cs" />

View File

@ -62,6 +62,9 @@ values of the `ZipkinExporterOptions`.
| --------------------------------| -------------------------------- | | --------------------------------| -------------------------------- |
| `OTEL_EXPORTER_ZIPKIN_ENDPOINT` | `Endpoint` | | `OTEL_EXPORTER_ZIPKIN_ENDPOINT` | `Endpoint` |
`FormatException` is thrown in case of an invalid value for any of the
supported environment variables.
## References ## References
* [OpenTelemetry Project](https://opentelemetry.io/) * [OpenTelemetry Project](https://opentelemetry.io/)

View File

@ -16,14 +16,20 @@
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using OpenTelemetry.Exporter.Zipkin.Implementation; using OpenTelemetry.Internal;
using OpenTelemetry.Trace; using OpenTelemetry.Trace;
namespace OpenTelemetry.Exporter namespace OpenTelemetry.Exporter
{ {
/// <summary> /// <summary>
/// Zipkin trace exporter options. /// Zipkin span exporter options.
/// OTEL_EXPORTER_ZIPKIN_ENDPOINT
/// environment variables are parsed during object construction.
/// </summary> /// </summary>
/// <remarks>
/// The constructor throws <see cref="FormatException"/> if it fails to parse
/// any of the supported environment variables.
/// </remarks>
public sealed class ZipkinExporterOptions public sealed class ZipkinExporterOptions
{ {
internal const int DefaultMaxPayloadSizeInBytes = 4096; internal const int DefaultMaxPayloadSizeInBytes = 4096;
@ -36,14 +42,9 @@ namespace OpenTelemetry.Exporter
/// </summary> /// </summary>
public ZipkinExporterOptions() public ZipkinExporterOptions()
{ {
try if (EnvironmentVariableHelper.LoadUri(ZipkinEndpointEnvVar, out Uri endpoint))
{ {
this.Endpoint = new Uri(Environment.GetEnvironmentVariable(ZipkinEndpointEnvVar) ?? DefaultZipkinEndpoint); this.Endpoint = endpoint;
}
catch (Exception ex)
{
this.Endpoint = new Uri(DefaultZipkinEndpoint);
ZipkinExporterEventSource.Log.FailedEndpointInitialization(ex);
} }
} }
@ -51,7 +52,7 @@ namespace OpenTelemetry.Exporter
/// Gets or sets Zipkin endpoint address. See https://zipkin.io/zipkin-api/#/default/post_spans. /// Gets or sets Zipkin endpoint address. See https://zipkin.io/zipkin-api/#/default/post_spans.
/// Typically https://zipkin-server-name:9411/api/v2/spans. /// Typically https://zipkin-server-name:9411/api/v2/spans.
/// </summary> /// </summary>
public Uri Endpoint { get; set; } public Uri Endpoint { get; set; } = new Uri(DefaultZipkinEndpoint);
/// <summary> /// <summary>
/// Gets or sets a value indicating whether short trace id should be used. /// Gets or sets a value indicating whether short trace id should be used.

View File

@ -2,8 +2,9 @@
## Unreleased ## Unreleased
* Changed `BatchExportActivityProcessorOptions` to throw `ArgumentException` * Changed `BatchExportActivityProcessorOptions` constructor to throw
if it fails to parse any of the supported environment variables. `FormatException` if it fails to parse any of the supported environment
variables.
## 1.2.0-beta1 ## 1.2.0-beta1

View File

@ -0,0 +1,115 @@
// <copyright file="EnvironmentVariableHelper.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 System.Globalization;
using System.Security;
namespace OpenTelemetry.Internal
{
/// <summary>
/// EnvironmentVariableHelper facilitates parsing environment variable values as defined by
/// <a href="https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/sdk-environment-variables.md">
/// the specification</a>.
/// </summary>
internal static class EnvironmentVariableHelper
{
/// <summary>
/// Reads an environment variable without any parsing.
/// </summary>
/// <param name="envVarKey">The name of the environment variable.</param>
/// <param name="result">The parsed value of the environment variable.</param>
/// <returns>
/// Returns <c>true</c> when a non-empty value was read; otherwise, <c>false</c>.
/// </returns>
public static bool LoadString(string envVarKey, out string result)
{
result = null;
try
{
result = Environment.GetEnvironmentVariable(envVarKey);
}
catch (SecurityException ex)
{
// The caller does not have the required permission to
// retrieve the value of an environment variable from the current process.
OpenTelemetrySdkEventSource.Log.MissingPermissionsToReadEnvironmentVariable(ex);
return false;
}
return !string.IsNullOrEmpty(result);
}
/// <summary>
/// Reads an environment variable and parses is as a
/// <a href="https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/sdk-environment-variables.md#numeric-value">
/// numeric value</a> - a non-negative decimal integer.
/// </summary>
/// <param name="envVarKey">The name of the environment variable.</param>
/// <param name="result">The parsed value of the environment variable.</param>
/// <returns>
/// Returns <c>true</c> when a non-empty value was read; otherwise, <c>false</c>.
/// </returns>
/// <exception cref="FormatException">
/// Thrown when failed to parse the non-empty value.
/// </exception>
public static bool LoadNumeric(string envVarKey, out int result)
{
result = 0;
if (!LoadString(envVarKey, out string value))
{
return false;
}
if (!int.TryParse(value, NumberStyles.None, CultureInfo.InvariantCulture, out result))
{
throw new FormatException($"{envVarKey} environment variable has an invalid value: '${value}'");
}
return true;
}
/// <summary>
/// Reads an environment variable and parses it as a <see cref="Uri" />.
/// </summary>
/// <param name="envVarKey">The name of the environment variable.</param>
/// <param name="result">The parsed value of the environment variable.</param>
/// <returns>
/// Returns <c>true</c> when a non-empty value was read; otherwise, <c>false</c>.
/// </returns>
/// <exception cref="FormatException">
/// Thrown when failed to parse the non-empty value.
/// </exception>
public static bool LoadUri(string envVarKey, out Uri result)
{
result = null;
if (!LoadString(envVarKey, out string value))
{
return false;
}
if (!Uri.TryCreate(value, UriKind.Absolute, out result))
{
throw new FormatException($"{envVarKey} environment variable has an invalid value: '${value}'");
}
return true;
}
}
}

View File

@ -215,7 +215,7 @@ purposes, the SDK provides the following built-in processors:
| `OTEL_BSP_MAX_EXPORT_BATCH_SIZE` | `MaxExportBatchSizeEnvVarKey` | | `OTEL_BSP_MAX_EXPORT_BATCH_SIZE` | `MaxExportBatchSizeEnvVarKey` |
<!-- markdownlint-enable MD013 --> <!-- markdownlint-enable MD013 -->
`ArgumentException` is thrown in case of an invalid value for any of the `FormatException` is thrown in case of an invalid value for any of the
supported environment variables. supported environment variables.
* [CompositeProcessor&lt;T&gt;](../../src/OpenTelemetry/CompositeProcessor.cs) * [CompositeProcessor&lt;T&gt;](../../src/OpenTelemetry/CompositeProcessor.cs)

View File

@ -14,7 +14,6 @@
// limitations under the License. // limitations under the License.
// </copyright> // </copyright>
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Security; using System.Security;
using OpenTelemetry.Internal; using OpenTelemetry.Internal;
@ -33,8 +32,7 @@ namespace OpenTelemetry.Resources
try try
{ {
string envResourceAttributeValue = Environment.GetEnvironmentVariable(EnvVarKey); if (EnvironmentVariableHelper.LoadString(EnvVarKey, out string envResourceAttributeValue))
if (!string.IsNullOrEmpty(envResourceAttributeValue))
{ {
var attributes = ParseResourceAttributes(envResourceAttributeValue); var attributes = ParseResourceAttributes(envResourceAttributeValue);
resource = new Resource(attributes); resource = new Resource(attributes);

View File

@ -14,7 +14,6 @@
// limitations under the License. // limitations under the License.
// </copyright> // </copyright>
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Security; using System.Security;
using OpenTelemetry.Internal; using OpenTelemetry.Internal;
@ -31,8 +30,7 @@ namespace OpenTelemetry.Resources
try try
{ {
string envResourceAttributeValue = Environment.GetEnvironmentVariable(EnvVarKey); if (EnvironmentVariableHelper.LoadString(EnvVarKey, out string envResourceAttributeValue))
if (!string.IsNullOrEmpty(envResourceAttributeValue))
{ {
resource = new Resource(new Dictionary<string, object> resource = new Resource(new Dictionary<string, object>
{ {

View File

@ -16,7 +16,6 @@
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.Security;
using OpenTelemetry.Internal; using OpenTelemetry.Internal;
namespace OpenTelemetry.Trace namespace OpenTelemetry.Trace
@ -27,7 +26,7 @@ namespace OpenTelemetry.Trace
/// environment variables are parsed during object construction. /// environment variables are parsed during object construction.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// The constructor throws <see cref="ArgumentException"/> if it fails to parse /// The constructor throws <see cref="FormatException"/> if it fails to parse
/// any of the supported environment variables. /// any of the supported environment variables.
/// </remarks> /// </remarks>
public class BatchExportActivityProcessorOptions : BatchExportProcessorOptions<Activity> public class BatchExportActivityProcessorOptions : BatchExportProcessorOptions<Activity>
@ -44,55 +43,25 @@ namespace OpenTelemetry.Trace
{ {
int value; int value;
if (LoadEnvVarInt(ExporterTimeoutEnvVarKey, out value)) if (EnvironmentVariableHelper.LoadNumeric(ExporterTimeoutEnvVarKey, out value))
{ {
this.ExporterTimeoutMilliseconds = value; this.ExporterTimeoutMilliseconds = value;
} }
if (LoadEnvVarInt(MaxExportBatchSizeEnvVarKey, out value)) if (EnvironmentVariableHelper.LoadNumeric(MaxExportBatchSizeEnvVarKey, out value))
{ {
this.MaxExportBatchSize = value; this.MaxExportBatchSize = value;
} }
if (LoadEnvVarInt(MaxQueueSizeEnvVarKey, out value)) if (EnvironmentVariableHelper.LoadNumeric(MaxQueueSizeEnvVarKey, out value))
{ {
this.MaxQueueSize = value; this.MaxQueueSize = value;
} }
if (LoadEnvVarInt(ScheduledDelayEnvVarKey, out value)) if (EnvironmentVariableHelper.LoadNumeric(ScheduledDelayEnvVarKey, out value))
{ {
this.ScheduledDelayMilliseconds = value; this.ScheduledDelayMilliseconds = value;
} }
} }
private static bool LoadEnvVarInt(string envVarKey, out int result)
{
result = 0;
string value;
try
{
value = Environment.GetEnvironmentVariable(envVarKey);
}
catch (SecurityException ex)
{
// The caller does not have the required permission to
// retrieve the value of an environment variable from the current process.
OpenTelemetrySdkEventSource.Log.MissingPermissionsToReadEnvironmentVariable(ex);
return false;
}
if (string.IsNullOrEmpty(value))
{
return false;
}
if (!int.TryParse(value, out result))
{
throw new ArgumentException($"{envVarKey} environment variable has an invalid value: '${value}'");
}
return true;
}
} }
} }

View File

@ -17,10 +17,10 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="$(RepoRoot)\src\OpenTelemetry\OpenTelemetry.csproj" /> <ProjectReference Include="$(RepoRoot)\src\OpenTelemetry\OpenTelemetry.csproj" />
<ProjectReference Include="$(RepoRoot)\src\OpenTelemetry.Exporter.Jaeger\OpenTelemetry.Exporter.Jaeger.csproj" /> <ProjectReference Aliases="Jaeger" Include="$(RepoRoot)\src\OpenTelemetry.Exporter.Jaeger\OpenTelemetry.Exporter.Jaeger.csproj" />
<ProjectReference Include="$(RepoRoot)\src\OpenTelemetry.Exporter.OpenTelemetryProtocol\OpenTelemetry.Exporter.OpenTelemetryProtocol.csproj" /> <ProjectReference Aliases="OpenTelemetryProtocol" Include="$(RepoRoot)\src\OpenTelemetry.Exporter.OpenTelemetryProtocol\OpenTelemetry.Exporter.OpenTelemetryProtocol.csproj" />
<ProjectReference Include="$(RepoRoot)\src\OpenTelemetry.Exporter.Prometheus\OpenTelemetry.Exporter.Prometheus.csproj" /> <ProjectReference Include="$(RepoRoot)\src\OpenTelemetry.Exporter.Prometheus\OpenTelemetry.Exporter.Prometheus.csproj" />
<ProjectReference Include="$(RepoRoot)\src\OpenTelemetry.Exporter.Zipkin\OpenTelemetry.Exporter.Zipkin.csproj" /> <ProjectReference Aliases="Zipkin" Include="$(RepoRoot)\src\OpenTelemetry.Exporter.Zipkin\OpenTelemetry.Exporter.Zipkin.csproj" />
<ProjectReference Include="$(RepoRoot)\src\OpenTelemetry.Instrumentation.AspNetCore\OpenTelemetry.Instrumentation.AspNetCore.csproj" /> <ProjectReference Include="$(RepoRoot)\src\OpenTelemetry.Instrumentation.AspNetCore\OpenTelemetry.Instrumentation.AspNetCore.csproj" />
<ProjectReference Include="$(RepoRoot)\src\OpenTelemetry.Instrumentation.Http\OpenTelemetry.Instrumentation.Http.csproj" /> <ProjectReference Include="$(RepoRoot)\src\OpenTelemetry.Instrumentation.Http\OpenTelemetry.Instrumentation.Http.csproj" />
</ItemGroup> </ItemGroup>

View File

@ -14,13 +14,15 @@
// limitations under the License. // limitations under the License.
// </copyright> // </copyright>
extern alias Jaeger;
using System.Diagnostics; using System.Diagnostics;
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes;
using Benchmarks.Helper; using Benchmarks.Helper;
using Jaeger::OpenTelemetry.Exporter;
using Jaeger::Thrift.Transport;
using OpenTelemetry; using OpenTelemetry;
using OpenTelemetry.Exporter;
using OpenTelemetry.Internal; using OpenTelemetry.Internal;
using Thrift.Transport;
namespace Benchmarks.Exporter namespace Benchmarks.Exporter
{ {
@ -50,7 +52,7 @@ namespace Benchmarks.Exporter
new JaegerExporterOptions(), new JaegerExporterOptions(),
new BlackHoleTransport()) new BlackHoleTransport())
{ {
Process = new OpenTelemetry.Exporter.Jaeger.Implementation.Process("TestService"), Process = new Jaeger::OpenTelemetry.Exporter.Jaeger.Implementation.Process("TestService"),
}; };
for (int i = 0; i < this.NumberOfBatches; i++) for (int i = 0; i < this.NumberOfBatches; i++)

View File

@ -14,6 +14,8 @@
// limitations under the License. // limitations under the License.
// </copyright> // </copyright>
extern alias OpenTelemetryProtocol;
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.Threading; using System.Threading;
@ -21,10 +23,10 @@ using BenchmarkDotNet.Attributes;
using Benchmarks.Helper; using Benchmarks.Helper;
using Grpc.Core; using Grpc.Core;
using OpenTelemetry; using OpenTelemetry;
using OpenTelemetry.Exporter;
using OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.ExportClient;
using OpenTelemetry.Internal; using OpenTelemetry.Internal;
using OtlpCollector = Opentelemetry.Proto.Collector.Trace.V1; using OpenTelemetryProtocol::OpenTelemetry.Exporter;
using OpenTelemetryProtocol::OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.ExportClient;
using OtlpCollector = OpenTelemetryProtocol::Opentelemetry.Proto.Collector.Trace.V1;
namespace Benchmarks.Exporter namespace Benchmarks.Exporter
{ {

View File

@ -14,16 +14,18 @@
// limitations under the License. // limitations under the License.
// </copyright> // </copyright>
extern alias OpenTelemetryProtocol;
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes;
using Benchmarks.Helper; using Benchmarks.Helper;
using OpenTelemetry; using OpenTelemetry;
using OpenTelemetry.Exporter;
using OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.ExportClient;
using OpenTelemetry.Internal; using OpenTelemetry.Internal;
using OpenTelemetry.Tests; using OpenTelemetry.Tests;
using OpenTelemetryProtocol::OpenTelemetry.Exporter;
using OpenTelemetryProtocol::OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.ExportClient;
namespace Benchmarks.Exporter namespace Benchmarks.Exporter
{ {

View File

@ -14,15 +14,17 @@
// limitations under the License. // limitations under the License.
// </copyright> // </copyright>
extern alias Zipkin;
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes;
using Benchmarks.Helper; using Benchmarks.Helper;
using OpenTelemetry; using OpenTelemetry;
using OpenTelemetry.Exporter;
using OpenTelemetry.Internal; using OpenTelemetry.Internal;
using OpenTelemetry.Tests; using OpenTelemetry.Tests;
using Zipkin::OpenTelemetry.Exporter;
namespace Benchmarks.Exporter namespace Benchmarks.Exporter
{ {

View File

@ -59,9 +59,7 @@ namespace OpenTelemetry.Exporter.Jaeger.Tests
{ {
Environment.SetEnvironmentVariable(JaegerExporterOptions.OTelAgentPortEnvVarKey, "invalid"); Environment.SetEnvironmentVariable(JaegerExporterOptions.OTelAgentPortEnvVarKey, "invalid");
var options = new JaegerExporterOptions(); Assert.Throws<FormatException>(() => new JaegerExporterOptions());
Assert.Equal(6831, options.AgentPort); // use default
} }
[Fact] [Fact]

View File

@ -63,9 +63,7 @@ namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests
{ {
Environment.SetEnvironmentVariable(OtlpExporterOptions.EndpointEnvVarName, "invalid"); Environment.SetEnvironmentVariable(OtlpExporterOptions.EndpointEnvVarName, "invalid");
var options = new OtlpExporterOptions(); Assert.Throws<FormatException>(() => new OtlpExporterOptions());
Assert.Equal(new Uri("http://localhost:4317"), options.Endpoint); // use default
} }
[Fact] [Fact]
@ -73,9 +71,7 @@ namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests
{ {
Environment.SetEnvironmentVariable(OtlpExporterOptions.TimeoutEnvVarName, "invalid"); Environment.SetEnvironmentVariable(OtlpExporterOptions.TimeoutEnvVarName, "invalid");
var options = new OtlpExporterOptions(); Assert.Throws<FormatException>(() => new OtlpExporterOptions());
Assert.Equal(10000, options.TimeoutMilliseconds); // use default
} }
[Fact] [Fact]
@ -83,9 +79,7 @@ namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests
{ {
Environment.SetEnvironmentVariable(OtlpExporterOptions.ProtocolEnvVarName, "invalid"); Environment.SetEnvironmentVariable(OtlpExporterOptions.ProtocolEnvVarName, "invalid");
var options = new OtlpExporterOptions(); Assert.Throws<FormatException>(() => new OtlpExporterOptions());
Assert.Equal(OtlpExportProtocol.Grpc, options.Protocol); // use default
} }
[Fact] [Fact]

View File

@ -175,9 +175,7 @@ namespace OpenTelemetry.Exporter.Zipkin.Tests
{ {
Environment.SetEnvironmentVariable(ZipkinExporterOptions.ZipkinEndpointEnvVar, "InvalidUri"); Environment.SetEnvironmentVariable(ZipkinExporterOptions.ZipkinEndpointEnvVar, "InvalidUri");
var exporterOptions = new ZipkinExporterOptions(); Assert.Throws<FormatException>(() => new ZipkinExporterOptions());
Assert.Equal(new Uri(ZipkinExporterOptions.DefaultZipkinEndpoint).AbsoluteUri, exporterOptions.Endpoint.AbsoluteUri);
} }
finally finally
{ {

View File

@ -0,0 +1,126 @@
// <copyright file="EnvironmentVariableHelperTests.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.Internal.Tests
{
public class EnvironmentVariableHelperTests : IDisposable
{
private const string EnvVar = "OTEL_EXAMPLE_VARIABLE";
public EnvironmentVariableHelperTests()
{
Environment.SetEnvironmentVariable(EnvVar, null);
}
public void Dispose()
{
Environment.SetEnvironmentVariable(EnvVar, null);
}
[Fact]
public void LoadString()
{
const string value = "something";
Environment.SetEnvironmentVariable(EnvVar, value);
bool actualBool = EnvironmentVariableHelper.LoadString(EnvVar, out string actualValue);
Assert.True(actualBool);
Assert.Equal(value, actualValue);
}
[Fact]
public void LoadString_NoValue()
{
bool actualBool = EnvironmentVariableHelper.LoadString(EnvVar, out string actualValue);
Assert.False(actualBool);
Assert.Null(actualValue);
}
[Theory]
[InlineData("123", 123)]
[InlineData("0", 0)]
public void LoadNumeric(string value, int expectedValue)
{
Environment.SetEnvironmentVariable(EnvVar, value);
bool actualBool = EnvironmentVariableHelper.LoadNumeric(EnvVar, out int actualValue);
Assert.True(actualBool);
Assert.Equal(expectedValue, actualValue);
}
[Fact]
public void LoadNumeric_NoValue()
{
bool actualBool = EnvironmentVariableHelper.LoadNumeric(EnvVar, out int actualValue);
Assert.False(actualBool);
Assert.Equal(0, actualValue);
}
[Theory]
[InlineData("something")] // NaN
[InlineData("-12")] // negative number not allowed
[InlineData("-0")] // sign not allowed
[InlineData("-1")] // -1 is not allowed
[InlineData(" 123 ")] // whitespaces not allowed
[InlineData("0xFF")] // only decimal number allowed
public void LoadNumeric_Invalid(string value)
{
Environment.SetEnvironmentVariable(EnvVar, value);
Assert.Throws<FormatException>(() => EnvironmentVariableHelper.LoadNumeric(EnvVar, out int _));
}
[Theory]
[InlineData("http://www.example.com", "http://www.example.com/")]
[InlineData("http://www.example.com/space%20here.html", "http://www.example.com/space here.html")] // characters are converted
[InlineData("http://www.example.com/space here.html", "http://www.example.com/space here.html")] // characters are escaped
public void LoadUri(string value, string expectedValue)
{
Environment.SetEnvironmentVariable(EnvVar, value);
bool actualBool = EnvironmentVariableHelper.LoadUri(EnvVar, out Uri actualValue);
Assert.True(actualBool);
Assert.Equal(expectedValue, actualValue.ToString());
}
[Fact]
public void LoadUri_NoValue()
{
bool actualBool = EnvironmentVariableHelper.LoadUri(EnvVar, out Uri actualValue);
Assert.False(actualBool);
Assert.Null(actualValue);
}
[Theory]
[InlineData("invalid")] // invalid format
[InlineData(" ")] // whitespace
public void LoadUri_Invalid(string value)
{
Environment.SetEnvironmentVariable(EnvVar, value);
Assert.Throws<FormatException>(() => EnvironmentVariableHelper.LoadUri(EnvVar, out Uri _));
}
}
}

View File

@ -63,7 +63,7 @@ namespace OpenTelemetry.Trace.Tests
{ {
Environment.SetEnvironmentVariable(BatchExportActivityProcessorOptions.ExporterTimeoutEnvVarKey, "invalid"); Environment.SetEnvironmentVariable(BatchExportActivityProcessorOptions.ExporterTimeoutEnvVarKey, "invalid");
Assert.Throws<ArgumentException>(() => new BatchExportActivityProcessorOptions()); Assert.Throws<FormatException>(() => new BatchExportActivityProcessorOptions());
} }
[Fact] [Fact]