diff --git a/opentelemetry-sdk/CHANGELOG.md b/opentelemetry-sdk/CHANGELOG.md index f494130c44..fd270f6798 100644 --- a/opentelemetry-sdk/CHANGELOG.md +++ b/opentelemetry-sdk/CHANGELOG.md @@ -2,6 +2,12 @@ ## vNext +- `EnvResourceDetector` now percent-decodes attribute values read from + `OTEL_RESOURCE_ATTRIBUTES`, matching the W3C Baggage encoding required by the + Resource SDK specification. For example, + `OTEL_RESOURCE_ATTRIBUTES=key=hello%20world` now yields the value + `hello world`. Keys are baggage tokens and are left unchanged. + ([#857](https://github.com/open-telemetry/opentelemetry-rust/issues/857)) - Bound instruments are now available for `Gauge` and `UpDownCounter` via the new `BoundGauge` and `BoundUpDownCounter` types exposed by the `opentelemetry` crate. Requires the `experimental_metrics_bound_instruments` diff --git a/opentelemetry-sdk/Cargo.toml b/opentelemetry-sdk/Cargo.toml index 1a10758afa..14fb62ca90 100644 --- a/opentelemetry-sdk/Cargo.toml +++ b/opentelemetry-sdk/Cargo.toml @@ -16,7 +16,7 @@ opentelemetry-http = { workspace = true, optional = true } futures-channel = { workspace = true } futures-executor = { workspace = true } futures-util = { workspace = true, features = ["std", "sink", "async-await-macro"] } -percent-encoding = { workspace = true, optional = true } +percent-encoding = { workspace = true } rand = { workspace = true, features = ["std", "std_rng", "small_rng", "os_rng", "thread_rng"], optional = true } serde = { workspace = true, features = ["derive", "rc"], optional = true } serde_json = { workspace = true, optional = true } @@ -44,7 +44,7 @@ pprof = { workspace = true } [features] default = ["trace", "metrics", "logs", "internal-logs"] -trace = ["opentelemetry/trace", "rand", "percent-encoding"] +trace = ["opentelemetry/trace", "rand"] jaeger_remote_sampler = ["trace", "opentelemetry-http", "http", "serde", "serde_json", "url", "experimental_async_runtime"] logs = ["opentelemetry/logs"] metrics = ["opentelemetry/metrics"] diff --git a/opentelemetry-sdk/src/resource/env.rs b/opentelemetry-sdk/src/resource/env.rs index da2600453b..f9b71cb574 100644 --- a/opentelemetry-sdk/src/resource/env.rs +++ b/opentelemetry-sdk/src/resource/env.rs @@ -4,6 +4,7 @@ //! variables. use crate::resource::{Resource, ResourceDetector}; use opentelemetry::{Key, KeyValue, Value}; +use percent_encoding::percent_decode_str; use std::env; const OTEL_RESOURCE_ATTRIBUTES: &str = "OTEL_RESOURCE_ATTRIBUTES"; @@ -50,9 +51,13 @@ fn construct_otel_resources(s: String) -> Resource { None => return None, }; let key = parts.0.trim(); - let value = parts.1.trim(); + // Trim OWS *before* percent-decoding so that any encoded whitespace + // (e.g. `%20`) in the value is preserved. Per the Resource SDK spec + // the value MUST be percent-decoded; keys are baggage tokens and are + // not encoded. + let value = percent_decode_str(parts.1.trim()).decode_utf8_lossy(); - Some(KeyValue::new(key.to_owned(), value.to_owned())) + Some(KeyValue::new(key.to_owned(), value.into_owned())) })) .build() } @@ -124,7 +129,7 @@ mod tests { [ ( "OTEL_RESOURCE_ATTRIBUTES", - Some("key=value, k = v , a= x, a=z,base64=SGVsbG8sIFdvcmxkIQ=="), + Some("key=value, k = v , a= x, a=z,base64=SGVsbG8sIFdvcmxkIQ==,encoded=user%20id%3D42%2C%20name%3D%22foo%22"), ), ("IRRELEVANT", Some("20200810")), ], @@ -140,6 +145,8 @@ mod tests { KeyValue::new("a", "x"), KeyValue::new("a", "z"), KeyValue::new("base64", "SGVsbG8sIFdvcmxkIQ=="), // base64('Hello, World!') + // Values are percent-decoded per the Resource SDK spec. + KeyValue::new("encoded", "user id=42, name=\"foo\""), ]) .build() );