Skip to content

Commit 7c8578b

Browse files
authored
perf(sdk): store InstrumentationScope in Arc for cheaper tracer clones (#3378)
1 parent e1028c0 commit 7c8578b

3 files changed

Lines changed: 43 additions & 2 deletions

File tree

opentelemetry-sdk/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
- **Breaking** The SDK `testing` feature is now runtime agnostic. [#3407][3407]
3030
- `TokioSpanExporter` and `new_tokio_test_exporter` have been renamed to `TestSpanExporter` and `new_test_exporter`.
3131
- The following transitive dependencies and features have been removed: `tokio/rt`, `tokio/time`, `tokio/macros`, `tokio/rt-multi-thread`, `tokio-stream`, `experimental_async_runtime`
32+
- Store `InstrumentationScope` in `Arc` internally in `SdkTracer`, making tracer clones cheaper (Arc refcount increment instead of deep copy).
3233
- Add 32-bit platform support by using `portable-atomic` for `AtomicI64` and `AtomicU64` in the metrics module. This enables compilation on 32-bit ARM targets (e.g., `armv5te-unknown-linux-gnueabi`, `armv7-unknown-linux-gnueabihf`).
3334
- `Aggregation` enum and `StreamBuilder::with_aggregation()` are now stable and no longer require the `spec_unstable_metrics_views` feature flag.
3435
- Fix `service.name` Resource attribute fallback to follow OpenTelemetry

opentelemetry-sdk/src/trace/span.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -709,6 +709,42 @@ mod tests {
709709
assert_eq!(event_vec.len(), DEFAULT_MAX_EVENT_PER_SPAN as usize);
710710
}
711711

712+
#[test]
713+
fn multiple_processors_receive_span_data() {
714+
use crate::trace::InMemorySpanExporterBuilder;
715+
716+
let exporter1 = InMemorySpanExporterBuilder::new().build();
717+
let exporter2 = InMemorySpanExporterBuilder::new().build();
718+
719+
let provider = crate::trace::SdkTracerProvider::builder()
720+
.with_simple_exporter(exporter1.clone())
721+
.with_simple_exporter(exporter2.clone())
722+
.build();
723+
724+
let tracer = provider.tracer("test");
725+
let mut span = tracer.start("multi_processor_span");
726+
span.set_attribute(KeyValue::new("key", "value"));
727+
span.end();
728+
729+
let spans1 = exporter1.get_finished_spans().unwrap();
730+
let spans2 = exporter2.get_finished_spans().unwrap();
731+
732+
assert_eq!(spans1.len(), 1);
733+
assert_eq!(spans2.len(), 1);
734+
assert_eq!(spans1[0].name, "multi_processor_span");
735+
assert_eq!(spans2[0].name, "multi_processor_span");
736+
assert_eq!(spans1[0].attributes, spans2[0].attributes);
737+
// Verify instrumentation scope is correctly propagated to both processors
738+
assert_eq!(spans1[0].instrumentation_scope.name(), "test");
739+
assert_eq!(spans2[0].instrumentation_scope.name(), "test");
740+
assert_eq!(
741+
spans1[0].instrumentation_scope,
742+
spans2[0].instrumentation_scope
743+
);
744+
745+
let _ = provider.shutdown();
746+
}
747+
712748
#[test]
713749
fn test_span_exported_data() {
714750
let provider = crate::trace::SdkTracerProvider::builder()

opentelemetry-sdk/src/trace/tracer.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,12 @@ use opentelemetry::{
1717
Context, InstrumentationScope, KeyValue,
1818
};
1919
use std::fmt;
20+
use std::sync::Arc;
2021

2122
/// `Tracer` implementation to create and manage spans
2223
#[derive(Clone)]
2324
pub struct SdkTracer {
24-
scope: InstrumentationScope,
25+
scope: Arc<InstrumentationScope>,
2526
provider: SdkTracerProvider,
2627
}
2728

@@ -39,7 +40,10 @@ impl fmt::Debug for SdkTracer {
3940
impl SdkTracer {
4041
/// Create a new tracer (used internally by `TracerProvider`s).
4142
pub(crate) fn new(scope: InstrumentationScope, provider: SdkTracerProvider) -> Self {
42-
SdkTracer { scope, provider }
43+
SdkTracer {
44+
scope: Arc::new(scope),
45+
provider,
46+
}
4347
}
4448

4549
/// TracerProvider associated with this tracer.

0 commit comments

Comments
 (0)