Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/OpenTelemetry/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ Notes](../../RELEASENOTES.md).
* Added support for `Meter.TelemetrySchemaUrl` property.
([#6714](https://github.com/open-telemetry/opentelemetry-dotnet/pull/6714))

* Decode `value` in OTEL_RESOURCE_ATTRIBUTES environment variable.
([#6737](https://github.com/open-telemetry/opentelemetry-dotnet/pull/6737))

## 1.14.0

Released 2025-Nov-12
Expand Down
8 changes: 5 additions & 3 deletions src/OpenTelemetry/Resources/OtelEnvResourceDetector.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

using System.Net;
using Microsoft.Extensions.Configuration;

namespace OpenTelemetry.Resources;
Expand All @@ -9,7 +10,7 @@ internal sealed class OtelEnvResourceDetector : IResourceDetector
{
public const string EnvVarKey = "OTEL_RESOURCE_ATTRIBUTES";
private const char AttributeListSplitter = ',';
private const char AttributeKeyValueSplitter = '=';
private static readonly char[] AttributeKeyValueSplitter = ['='];

private readonly IConfiguration configuration;

Expand Down Expand Up @@ -38,13 +39,14 @@ private static List<KeyValuePair<string, object>> ParseResourceAttributes(string
string[] rawAttributes = resourceAttributes.Split(AttributeListSplitter);
foreach (string rawKeyValuePair in rawAttributes)
{
string[] keyValuePair = rawKeyValuePair.Split(AttributeKeyValueSplitter);
string[] keyValuePair = rawKeyValuePair.Split(AttributeKeyValueSplitter, 2);
if (keyValuePair.Length != 2)
{
continue;
}

attributes.Add(new KeyValuePair<string, object>(keyValuePair[0].Trim(), keyValuePair[1].Trim()));
var value = WebUtility.UrlDecode(keyValuePair[1].Trim());
attributes.Add(new KeyValuePair<string, object>(keyValuePair[0].Trim(), value));
}

return attributes;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,27 @@ public void OtelEnvResource_WithEnvVar_2()
Assert.Contains(new KeyValuePair<string, object>("Key2", "Val2"), resource.Attributes);
}

[Theory]
[InlineData("Key1=Val1%20With%20Spaces", "Key1", "Val1 With Spaces")]
[InlineData(" query= select%20*%20from%20foo ", "query", "select * from foo")]
[InlineData("raw=100%25%", "raw", "100%%")]
[InlineData("bad=%G1value=value", "bad", "%G1value=value")]
[InlineData("a=%2C%3B%3D", "a", ",;=")]

public void OtelEnvResource_WithEnvVar_Decoding(string envVarValue, string key, string value)
{
// Arrange
Environment.SetEnvironmentVariable(OtelEnvResourceDetector.EnvVarKey, envVarValue);
var resource = new OtelEnvResourceDetector(
new ConfigurationBuilder().AddEnvironmentVariables().Build())
.Detect();

// Assert
Assert.NotEqual(Resource.Empty, resource);
Assert.Single(resource.Attributes);
Assert.Contains(new KeyValuePair<string, object>(key, value), resource.Attributes);
}

[Fact]
public void OtelEnvResource_UsingIConfiguration()
{
Expand Down
Loading