Skip to content

Commit de0d58c

Browse files
djvcomlalitb
andauthored
fix(proto): preserve InstrumentationScope version and attributes (#3332)
Co-authored-by: Lalit Kumar Bhasin <lalit_fin@yahoo.com>
1 parent 329dc2d commit de0d58c

3 files changed

Lines changed: 99 additions & 29 deletions

File tree

opentelemetry-proto/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
- Update proto definitions to v1.10.0.
66
- Updated `schemars` dependency to version 1.0.0.
7+
- **Bug fix**: `InstrumentationScope` version and attributes are now preserved when logs have a target set. Previously, setting a log target would discard the scope's version and attributes. ([#3276](https://github.com/open-telemetry/opentelemetry-rust/issues/3276))
78

89
## 0.31.0
910

opentelemetry-proto/src/transform/common.rs

Lines changed: 59 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -55,20 +55,11 @@ pub mod tonic {
5555
),
5656
) -> Self {
5757
let (library, target) = data;
58-
if let Some(t) = target {
59-
InstrumentationScope {
60-
name: t.to_string(),
61-
version: String::new(),
62-
attributes: vec![],
63-
..Default::default()
64-
}
65-
} else {
66-
InstrumentationScope {
67-
name: library.name().to_owned(),
68-
version: library.version().map(ToOwned::to_owned).unwrap_or_default(),
69-
attributes: Attributes::from(library.attributes().cloned()).0,
70-
..Default::default()
71-
}
58+
InstrumentationScope {
59+
name: target.map_or_else(|| library.name().to_owned(), Cow::into_owned),
60+
version: library.version().unwrap_or_default().to_owned(),
61+
attributes: Attributes::from(library.attributes().cloned()).0,
62+
..Default::default()
7263
}
7364
}
7465
}
@@ -86,20 +77,11 @@ pub mod tonic {
8677
),
8778
) -> Self {
8879
let (library, target) = data;
89-
if let Some(t) = target {
90-
InstrumentationScope {
91-
name: t.to_string(),
92-
version: String::new(),
93-
attributes: vec![],
94-
..Default::default()
95-
}
96-
} else {
97-
InstrumentationScope {
98-
name: library.name().to_owned(),
99-
version: library.version().map(ToOwned::to_owned).unwrap_or_default(),
100-
attributes: Attributes::from(library.attributes().cloned()).0,
101-
..Default::default()
102-
}
80+
InstrumentationScope {
81+
name: target.map_or_else(|| library.name().to_owned(), Cow::into_owned),
82+
version: library.version().unwrap_or_default().to_owned(),
83+
attributes: Attributes::from(library.attributes().cloned()).0,
84+
..Default::default()
10385
}
10486
}
10587
}
@@ -178,4 +160,53 @@ pub mod tonic {
178160
.collect::<Vec<_>>()
179161
.into()
180162
}
163+
164+
#[cfg(test)]
165+
mod tests {
166+
use super::*;
167+
use opentelemetry::KeyValue;
168+
use std::borrow::Cow;
169+
170+
fn assert_scope_fields(
171+
proto_scope: &InstrumentationScope,
172+
expected_name: &str,
173+
expected_version: &str,
174+
expected_attr_key: &str,
175+
) {
176+
assert_eq!(proto_scope.name, expected_name);
177+
assert_eq!(proto_scope.version, expected_version);
178+
assert_eq!(proto_scope.attributes.len(), 1);
179+
assert_eq!(proto_scope.attributes[0].key, expected_attr_key);
180+
}
181+
182+
#[test]
183+
fn instrumentation_scope_with_target_overrides_name_but_preserves_version_and_attributes() {
184+
let scope = opentelemetry::InstrumentationScope::builder("my-lib")
185+
.with_version("1.0.0")
186+
.with_attributes([KeyValue::new("feature", "metrics")])
187+
.build();
188+
let target: Option<Cow<'static, str>> = Some(Cow::Borrowed("my_app::handlers"));
189+
190+
let from_owned = InstrumentationScope::from((scope.clone(), target.clone()));
191+
let from_ref = InstrumentationScope::from((&scope, target));
192+
193+
assert_scope_fields(&from_owned, "my_app::handlers", "1.0.0", "feature");
194+
assert_scope_fields(&from_ref, "my_app::handlers", "1.0.0", "feature");
195+
}
196+
197+
#[test]
198+
fn instrumentation_scope_without_target_preserves_all_fields() {
199+
let scope = opentelemetry::InstrumentationScope::builder("my-lib")
200+
.with_version("1.0.0")
201+
.with_attributes([KeyValue::new("feature", "metrics")])
202+
.build();
203+
let target: Option<Cow<'static, str>> = None;
204+
205+
let from_owned = InstrumentationScope::from((scope.clone(), target.clone()));
206+
let from_ref = InstrumentationScope::from((&scope, target));
207+
208+
assert_scope_fields(&from_owned, "my-lib", "1.0.0", "feature");
209+
assert_scope_fields(&from_ref, "my-lib", "1.0.0", "feature");
210+
}
211+
}
181212
}

opentelemetry-proto/src/transform/logs.rs

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -228,11 +228,12 @@ mod tests {
228228
use opentelemetry::logs::Logger;
229229
use opentelemetry::logs::LoggerProvider;
230230
use opentelemetry::time::now;
231-
use opentelemetry::InstrumentationScope;
231+
use opentelemetry::{InstrumentationScope, KeyValue};
232232
use opentelemetry_sdk::error::OTelSdkResult;
233233
use opentelemetry_sdk::logs::LogProcessor;
234234
use opentelemetry_sdk::logs::SdkLoggerProvider;
235235
use opentelemetry_sdk::{logs::LogBatch, logs::SdkLogRecord, Resource};
236+
use std::borrow::Cow;
236237

237238
#[derive(Debug)]
238239
struct MockProcessor;
@@ -317,4 +318,41 @@ mod tests {
317318
assert_eq!(scope_logs_1.log_records.len(), 1);
318319
assert_eq!(scope_logs_2.log_records.len(), 1);
319320
}
321+
322+
#[test]
323+
fn test_group_logs_preserves_scope_version_and_attributes_when_target_set() {
324+
let resource = Resource::builder().build();
325+
let processor = MockProcessor {};
326+
let logger = SdkLoggerProvider::builder()
327+
.with_log_processor(processor)
328+
.build()
329+
.logger("test");
330+
331+
let mut logrecord = logger.create_log_record();
332+
logrecord.set_timestamp(now());
333+
logrecord.set_observed_timestamp(now());
334+
logrecord.set_target(Cow::Borrowed("my_app::handlers"));
335+
336+
let instrumentation = InstrumentationScope::builder("my-lib")
337+
.with_version("1.0.0")
338+
.with_attributes([KeyValue::new("feature", "metrics")])
339+
.build();
340+
341+
let logs = [(&logrecord, &instrumentation)];
342+
let log_batch = LogBatch::new(&logs);
343+
let resource: ResourceAttributesWithSchema = (&resource).into();
344+
345+
let grouped_logs =
346+
crate::transform::logs::tonic::group_logs_by_resource_and_scope(&log_batch, &resource);
347+
348+
assert_eq!(grouped_logs.len(), 1);
349+
let resource_logs = &grouped_logs[0];
350+
assert_eq!(resource_logs.scope_logs.len(), 1);
351+
352+
let scope = resource_logs.scope_logs[0].scope.as_ref().unwrap();
353+
assert_eq!(scope.name, "my_app::handlers");
354+
assert_eq!(scope.version, "1.0.0");
355+
assert_eq!(scope.attributes.len(), 1);
356+
assert_eq!(scope.attributes[0].key, "feature");
357+
}
320358
}

0 commit comments

Comments
 (0)