diff --git a/src/OpenTelemetry/CHANGELOG.md b/src/OpenTelemetry/CHANGELOG.md index 89595afcbd8..1749e7c1a0f 100644 --- a/src/OpenTelemetry/CHANGELOG.md +++ b/src/OpenTelemetry/CHANGELOG.md @@ -2,6 +2,9 @@ ## Unreleased +* `ResourceBuilder.AddEnvironmentVariableDetector` handles `OTEL_SERVICE_NAME` + environmental variable. ([#2209](https://github.com/open-telemetry/opentelemetry-dotnet/pull/2209)) + * Removes upper constraint for Microsoft.Extensions.Logging dependencies. ([#2179](https://github.com/open-telemetry/opentelemetry-dotnet/pull/2179)) diff --git a/src/OpenTelemetry/README.md b/src/OpenTelemetry/README.md index 94744f5cadc..33a840986a0 100644 --- a/src/OpenTelemetry/README.md +++ b/src/OpenTelemetry/README.md @@ -5,10 +5,10 @@ * [Installation](#installation) * [Introduction](#introduction) -* [Getting started with Logs](#getting-started-with-logging) -* [Getting started with Traces](#getting-started-with-tracing) -* [Tracing Configuration](#tracing-configuration) - * [ActivitySource](#activity-source) +* [Getting started with Logging](#getting-started-with-logging) +* [Getting started with Tracing](#getting-started-with-tracing) +* [Tracing configuration](#tracing-configuration) + * [Activity Source](#activity-source) * [Instrumentation](#instrumentation) * [Processor](#processor) * [Resource](#resource) @@ -16,6 +16,8 @@ * [Advanced topics](#advanced-topics) * [Propagators](#propagators) * [Troubleshooting](#troubleshooting) + * [Configuration Parameters](#configuration-parameters) + * [Remarks](#remarks) * [References](#references) ## Installation @@ -243,6 +245,16 @@ using var tracerProvider = Sdk.CreateTracerProviderBuilder() .Build(); ``` +It is also possible to configure the `Resource` by using `AddEnvironmentVariableDetector` +together with following environmental variables: + + +| Environment variable | Description | +| -------------------------- | -------------------------------------------------- | +| `OTEL_RESOURCE_ATTRIBUTES` | Key-value pairs to be used as resource attributes. See the [Resource SDK specification](https://github.com/open-telemetry/opentelemetry-specification/blob/v1.5.0/specification/resource/sdk.md#specifying-resource-information-via-an-environment-variable) for more details. | +| `OTEL_SERVICE_NAME` | Sets the value of the `service.name` resource attribute. If `service.name` is also provided in `OTEL_RESOURCE_ATTRIBUTES`, then `OTEL_SERVICE_NAME` takes precedence. | + + ### Sampler [Samplers](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/sdk.md#sampler) diff --git a/src/OpenTelemetry/Resources/OtelEnvResourceDetector.cs b/src/OpenTelemetry/Resources/OtelEnvResourceDetector.cs index 236762ad098..cd424e2d034 100644 --- a/src/OpenTelemetry/Resources/OtelEnvResourceDetector.cs +++ b/src/OpenTelemetry/Resources/OtelEnvResourceDetector.cs @@ -16,13 +16,14 @@ using System; using System.Collections.Generic; +using System.Security; using OpenTelemetry.Internal; namespace OpenTelemetry.Resources { internal class OtelEnvResourceDetector : IResourceDetector { - private const string OTelResourceEnvVarKey = "OTEL_RESOURCE_ATTRIBUTES"; + public const string EnvVarKey = "OTEL_RESOURCE_ATTRIBUTES"; private const char AttributeListSplitter = ','; private const char AttributeKeyValueSplitter = '='; @@ -32,16 +33,16 @@ public Resource Detect() try { - string envResourceAttributeValue = Environment.GetEnvironmentVariable(OTelResourceEnvVarKey); + string envResourceAttributeValue = Environment.GetEnvironmentVariable(EnvVarKey); if (!string.IsNullOrEmpty(envResourceAttributeValue)) { var attributes = ParseResourceAttributes(envResourceAttributeValue); resource = new Resource(attributes); } } - catch (Exception ex) + catch (SecurityException ex) { - OpenTelemetrySdkEventSource.Log.ResourceDetectorFailed("OtelEnvResourceDetector", ex.Message); + OpenTelemetrySdkEventSource.Log.ResourceDetectorFailed(nameof(OtelEnvResourceDetector), ex.Message); } return resource; diff --git a/src/OpenTelemetry/Resources/OtelServiceNameEnvVarDetector.cs b/src/OpenTelemetry/Resources/OtelServiceNameEnvVarDetector.cs new file mode 100644 index 00000000000..806ced7c793 --- /dev/null +++ b/src/OpenTelemetry/Resources/OtelServiceNameEnvVarDetector.cs @@ -0,0 +1,51 @@ +// +// 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 System.Collections.Generic; +using System.Security; +using OpenTelemetry.Internal; + +namespace OpenTelemetry.Resources +{ + internal class OtelServiceNameEnvVarDetector : IResourceDetector + { + public const string EnvVarKey = "OTEL_SERVICE_NAME"; + + public Resource Detect() + { + var resource = Resource.Empty; + + try + { + string envResourceAttributeValue = Environment.GetEnvironmentVariable(EnvVarKey); + if (!string.IsNullOrEmpty(envResourceAttributeValue)) + { + resource = new Resource(new Dictionary + { + [ResourceSemanticConventions.AttributeServiceName] = envResourceAttributeValue, + }); + } + } + catch (SecurityException ex) + { + OpenTelemetrySdkEventSource.Log.ResourceDetectorFailed(nameof(OtelServiceNameEnvVarDetector), ex.Message); + } + + return resource; + } + } +} diff --git a/src/OpenTelemetry/Resources/ResourceBuilderExtensions.cs b/src/OpenTelemetry/Resources/ResourceBuilderExtensions.cs index 8eb500597da..9dd30fc1236 100644 --- a/src/OpenTelemetry/Resources/ResourceBuilderExtensions.cs +++ b/src/OpenTelemetry/Resources/ResourceBuilderExtensions.cs @@ -110,8 +110,8 @@ public static ResourceBuilder AddAttributes(this ResourceBuilder resourceBuilder } /// - /// Adds resource attributes parsed from an environment variable to a - /// following the following the Resource /// SDK. /// @@ -119,7 +119,7 @@ public static ResourceBuilder AddAttributes(this ResourceBuilder resourceBuilder /// Returns for chaining. public static ResourceBuilder AddEnvironmentVariableDetector(this ResourceBuilder resourceBuilder) { - return resourceBuilder.AddDetector(new OtelEnvResourceDetector()); + return resourceBuilder.AddDetector(new OtelEnvResourceDetector()).AddDetector(new OtelServiceNameEnvVarDetector()); } private static string GetFileVersion() diff --git a/test/OpenTelemetry.Tests/Resources/OtelEnvResourceDetectorTest.cs b/test/OpenTelemetry.Tests/Resources/OtelEnvResourceDetectorTest.cs index 8ca21cdb0b9..dc1a8a376e2 100644 --- a/test/OpenTelemetry.Tests/Resources/OtelEnvResourceDetectorTest.cs +++ b/test/OpenTelemetry.Tests/Resources/OtelEnvResourceDetectorTest.cs @@ -22,11 +22,24 @@ namespace OpenTelemetry.Resources.Tests { public class OtelEnvResourceDetectorTest : IDisposable { - private const string OtelEnvVarKey = "OTEL_RESOURCE_ATTRIBUTES"; - public OtelEnvResourceDetectorTest() { - Environment.SetEnvironmentVariable(OtelEnvVarKey, null); + Environment.SetEnvironmentVariable(OtelEnvResourceDetector.EnvVarKey, null); + } + + public void Dispose() + { + Environment.SetEnvironmentVariable(OtelEnvResourceDetector.EnvVarKey, null); + } + + [Fact] + public void OtelEnvResource_EnvVarKey() + { + // Act + var resource = new OtelServiceNameEnvVarDetector().Detect(); + + // Assert + Assert.Equal("OTEL_RESOURCE_ATTRIBUTES", OtelEnvResourceDetector.EnvVarKey); } [Fact] @@ -44,7 +57,7 @@ public void OtelEnvResource_WithEnvVar_1() { // Arrange var envVarValue = "Key1=Val1,Key2=Val2"; - Environment.SetEnvironmentVariable(OtelEnvVarKey, envVarValue); + Environment.SetEnvironmentVariable(OtelEnvResourceDetector.EnvVarKey, envVarValue); var resource = new OtelEnvResourceDetector().Detect(); // Assert @@ -57,7 +70,7 @@ public void OtelEnvResource_WithEnvVar_2() { // Arrange var envVarValue = "Key1,Key2=Val2"; - Environment.SetEnvironmentVariable(OtelEnvVarKey, envVarValue); + Environment.SetEnvironmentVariable(OtelEnvResourceDetector.EnvVarKey, envVarValue); var resource = new OtelEnvResourceDetector().Detect(); // Assert @@ -65,10 +78,5 @@ public void OtelEnvResource_WithEnvVar_2() Assert.Single(resource.Attributes); Assert.Contains(new KeyValuePair("Key2", "Val2"), resource.Attributes); } - - public void Dispose() - { - Environment.SetEnvironmentVariable(OtelEnvVarKey, null); - } } } diff --git a/test/OpenTelemetry.Tests/Resources/OtelServiceNameEnvVarDetectorTests.cs b/test/OpenTelemetry.Tests/Resources/OtelServiceNameEnvVarDetectorTests.cs new file mode 100644 index 00000000000..27ef814c3af --- /dev/null +++ b/test/OpenTelemetry.Tests/Resources/OtelServiceNameEnvVarDetectorTests.cs @@ -0,0 +1,70 @@ +// +// 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 System.Collections.Generic; +using Xunit; + +namespace OpenTelemetry.Resources.Tests +{ + public class OtelServiceNameEnvVarDetectorTests : IDisposable + { + public OtelServiceNameEnvVarDetectorTests() + { + Environment.SetEnvironmentVariable(OtelServiceNameEnvVarDetector.EnvVarKey, null); + } + + public void Dispose() + { + Environment.SetEnvironmentVariable(OtelServiceNameEnvVarDetector.EnvVarKey, null); + } + + [Fact] + public void OtelServiceNameEnvVar_EnvVarKey() + { + // Act + var resource = new OtelServiceNameEnvVarDetector().Detect(); + + // Assert + Assert.Equal("OTEL_SERVICE_NAME", OtelServiceNameEnvVarDetector.EnvVarKey); + } + + [Fact] + public void OtelServiceNameEnvVar_Null() + { + // Act + var resource = new OtelServiceNameEnvVarDetector().Detect(); + + // Assert + Assert.Equal(Resource.Empty, resource); + } + + [Fact] + public void OtelServiceNameEnvVar_WithValue() + { + // Arrange + var envVarValue = "my-service"; + Environment.SetEnvironmentVariable(OtelServiceNameEnvVarDetector.EnvVarKey, envVarValue); + + // Act + var resource = new OtelServiceNameEnvVarDetector().Detect(); + + // Assert + Assert.NotEqual(Resource.Empty, resource); + Assert.Contains(new KeyValuePair(ResourceSemanticConventions.AttributeServiceName, envVarValue), resource.Attributes); + } + } +} diff --git a/test/OpenTelemetry.Tests/Resources/ResourceTest.cs b/test/OpenTelemetry.Tests/Resources/ResourceTest.cs index 624d0c3f404..12288630a65 100644 --- a/test/OpenTelemetry.Tests/Resources/ResourceTest.cs +++ b/test/OpenTelemetry.Tests/Resources/ResourceTest.cs @@ -25,15 +25,19 @@ public class ResourceTest : IDisposable { private const string KeyName = "key"; private const string ValueName = "value"; - private const string OtelEnvVarKey = "OTEL_RESOURCE_ATTRIBUTES"; public ResourceTest() { - Environment.SetEnvironmentVariable(OtelEnvVarKey, null); + ClearEnvVars(); + } + + public void Dispose() + { + ClearEnvVars(); } [Fact] - public static void CreateResource_NullAttributeCollection() + public void CreateResource_NullAttributeCollection() { // Act and Assert var resource = new Resource(null); @@ -421,10 +425,10 @@ public void GetResourceWithDefaultAttributes_ResourceWithAttrs() } [Fact] - public void GetResourceWithDefaultAttributes_WithEnvVar() + public void GetResourceWithDefaultAttributes_WithResourceEnvVar() { // Arrange - Environment.SetEnvironmentVariable(OtelEnvVarKey, "EVKey1=EVVal1,EVKey2=EVVal2"); + Environment.SetEnvironmentVariable(OtelEnvResourceDetector.EnvVarKey, "EVKey1=EVVal1,EVKey2=EVVal2"); var resource = ResourceBuilder.CreateDefault().AddEnvironmentVariableDetector().AddAttributes(this.CreateAttributes(2)).Build(); // Assert @@ -436,9 +440,54 @@ public void GetResourceWithDefaultAttributes_WithEnvVar() Assert.Contains(new KeyValuePair("EVKey2", "EVVal2"), attributes); } - public void Dispose() + [Fact] + public void GetResource_WithServiceEnvVar() + { + // Arrange + Environment.SetEnvironmentVariable(OtelServiceNameEnvVarDetector.EnvVarKey, "some-service"); + var resource = ResourceBuilder.CreateDefault().AddEnvironmentVariableDetector().AddAttributes(this.CreateAttributes(2)).Build(); + + // Assert + var attributes = resource.Attributes; + Assert.Equal(3, attributes.Count()); + ValidateAttributes(attributes, 0, 1); + Assert.Contains(new KeyValuePair("service.name", "some-service"), attributes); + } + + [Fact] + public void GetResource_WithServiceNameSetWithTwoEnvVars() + { + // Arrange + Environment.SetEnvironmentVariable(OtelEnvResourceDetector.EnvVarKey, "service.name=from-resource-attr"); + Environment.SetEnvironmentVariable(OtelServiceNameEnvVarDetector.EnvVarKey, "from-service-name"); + var resource = ResourceBuilder.CreateDefault().AddEnvironmentVariableDetector().AddAttributes(this.CreateAttributes(2)).Build(); + + // Assert + var attributes = resource.Attributes; + Assert.Equal(3, attributes.Count()); + ValidateAttributes(attributes, 0, 1); + Assert.Contains(new KeyValuePair("service.name", "from-service-name"), attributes); + } + + [Fact] + public void GetResource_WithServiceNameSetWithTwoEnvVarsAndCode() + { + // Arrange + Environment.SetEnvironmentVariable(OtelEnvResourceDetector.EnvVarKey, "service.name=from-resource-attr"); + Environment.SetEnvironmentVariable(OtelServiceNameEnvVarDetector.EnvVarKey, "from-service-name"); + var resource = ResourceBuilder.CreateDefault().AddEnvironmentVariableDetector().AddService("from-code").AddAttributes(this.CreateAttributes(2)).Build(); + + // Assert + var attributes = resource.Attributes; + Assert.Equal(4, attributes.Count()); + ValidateAttributes(attributes, 0, 1); + Assert.Contains(new KeyValuePair("service.name", "from-code"), attributes); + } + + private static void ClearEnvVars() { - Environment.SetEnvironmentVariable(OtelEnvVarKey, null); + Environment.SetEnvironmentVariable(OtelEnvResourceDetector.EnvVarKey, null); + Environment.SetEnvironmentVariable(OtelServiceNameEnvVarDetector.EnvVarKey, null); } private static void AddAttributes(Dictionary attributes, int attributeCount, int startIndex = 0)