Skip to content

Commit ac0c3bd

Browse files
committed
feat: add a readable span interface
Currently there is no way to read the span data without cloning it. This causes performance issues when the span for span processors that need to read the span data. This commit introduces a new trait `ReadableSpan` in the sdk implemented by SDK Spans that allows to read the span data without cloning it.
1 parent 5c60f12 commit ac0c3bd

File tree

7 files changed

+396
-54
lines changed

7 files changed

+396
-54
lines changed

examples/tracing-http-propagator/src/server.rs

Lines changed: 66 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,16 @@ use opentelemetry_sdk::{
1515
error::OTelSdkResult,
1616
logs::{LogProcessor, SdkLogRecord, SdkLoggerProvider},
1717
propagation::{BaggagePropagator, TraceContextPropagator},
18-
trace::{SdkTracerProvider, SpanProcessor},
18+
trace::{FinishedSpan, ReadableSpan, SdkTracerProvider, SpanProcessor},
1919
};
2020
use opentelemetry_semantic_conventions::trace;
2121
use opentelemetry_stdout::{LogExporter, SpanExporter};
22-
use std::{convert::Infallible, net::SocketAddr, sync::OnceLock};
22+
use std::{
23+
collections::HashMap,
24+
convert::Infallible,
25+
net::SocketAddr,
26+
sync::{Mutex, OnceLock},
27+
};
2328
use tokio::net::TcpListener;
2429
use tracing::info;
2530
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
@@ -83,6 +88,7 @@ async fn router(
8388
let span = tracer
8489
.span_builder("router")
8590
.with_kind(SpanKind::Server)
91+
.with_attributes([KeyValue::new("http.route", req.uri().path().to_string())])
8692
.start_with_context(tracer, &parent_cx);
8793

8894
info!(name = "router", message = "Dispatching request");
@@ -104,6 +110,62 @@ async fn router(
104110
response
105111
}
106112

113+
#[derive(Debug, Default)]
114+
/// A custom span processor that counts concurrent requests for each route (indetified by the http.route
115+
/// attribute) and adds that information to the span attributes.
116+
struct RouteConcurrencyCounterSpanProcessor(Mutex<HashMap<opentelemetry::Key, usize>>);
117+
118+
impl SpanProcessor for RouteConcurrencyCounterSpanProcessor {
119+
fn force_flush(&self) -> OTelSdkResult {
120+
Ok(())
121+
}
122+
123+
fn shutdown(&self) -> OTelSdkResult {
124+
Ok(())
125+
}
126+
127+
fn on_start(&self, span: &mut opentelemetry_sdk::trace::Span, _cx: &Context) {
128+
if !matches!(span.span_kind(), SpanKind::Server) {
129+
return;
130+
}
131+
let Some(route) = span
132+
.attributes()
133+
.iter()
134+
.find(|kv| kv.key.as_str() == "http.route")
135+
else {
136+
return;
137+
};
138+
let mut counts = self.0.lock().unwrap();
139+
let count = counts.entry(route.key.clone()).or_default();
140+
*count += 1;
141+
span.set_attribute(KeyValue::new(
142+
"http.route.concurrent_requests",
143+
count.to_string(),
144+
));
145+
}
146+
147+
fn on_end(&self, span: &mut FinishedSpan) {
148+
if !matches!(span.span_kind(), SpanKind::Server) {
149+
return;
150+
}
151+
let Some(route) = span
152+
.attributes()
153+
.iter()
154+
.find(|kv| kv.key.as_str() == "http.route")
155+
else {
156+
return;
157+
};
158+
let mut counts = self.0.lock().unwrap();
159+
let Some(count) = counts.get_mut(&route.key) else {
160+
return;
161+
};
162+
*count -= 1;
163+
if *count == 0 {
164+
counts.remove(&route.key);
165+
}
166+
}
167+
}
168+
107169
/// A custom log processor that enriches LogRecords with baggage attributes.
108170
/// Baggage information is not added automatically without this processor.
109171
#[derive(Debug)]
@@ -145,7 +207,7 @@ impl SpanProcessor for EnrichWithBaggageSpanProcessor {
145207
}
146208
}
147209

148-
fn on_end(&self, _span: opentelemetry_sdk::trace::SpanData) {}
210+
fn on_end(&self, _span: &mut opentelemetry_sdk::trace::FinishedSpan) {}
149211
}
150212

151213
fn init_tracer() -> SdkTracerProvider {
@@ -161,6 +223,7 @@ fn init_tracer() -> SdkTracerProvider {
161223
// Setup tracerprovider with stdout exporter
162224
// that prints the spans to stdout.
163225
let provider = SdkTracerProvider::builder()
226+
.with_span_processor(RouteConcurrencyCounterSpanProcessor::default())
164227
.with_span_processor(EnrichWithBaggageSpanProcessor)
165228
.with_simple_exporter(SpanExporter::default())
166229
.build();

opentelemetry-sdk/src/trace/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ pub use id_generator::{IdGenerator, RandomIdGenerator};
3939
pub use links::SpanLinks;
4040
pub use provider::{SdkTracerProvider, TracerProviderBuilder};
4141
pub use sampler::{Sampler, ShouldSample};
42-
pub use span::Span;
42+
pub use span::{Span, ReadableSpan, FinishedSpan};
4343
pub use span_limit::SpanLimits;
4444
pub use span_processor::{
4545
BatchConfig, BatchConfigBuilder, BatchSpanProcessor, BatchSpanProcessorBuilder,
@@ -136,7 +136,7 @@ mod tests {
136136
}
137137
}
138138

139-
fn on_end(&self, _span: SpanData) {
139+
fn on_end(&self, _span: &mut FinishedSpan) {
140140
// TODO: Accessing Context::current() will panic today and hence commented out.
141141
// See https://github.com/open-telemetry/opentelemetry-rust/issues/2871
142142
// let _c = Context::current();

opentelemetry-sdk/src/trace/provider.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -462,8 +462,8 @@ mod tests {
462462
SERVICE_NAME, TELEMETRY_SDK_LANGUAGE, TELEMETRY_SDK_NAME, TELEMETRY_SDK_VERSION,
463463
};
464464
use crate::trace::provider::TracerProviderInner;
465-
use crate::trace::{Config, Span, SpanProcessor};
466-
use crate::trace::{SdkTracerProvider, SpanData};
465+
use crate::trace::SdkTracerProvider;
466+
use crate::trace::{Config, FinishedSpan, Span, SpanProcessor};
467467
use crate::Resource;
468468
use opentelemetry::trace::{Tracer, TracerProvider};
469469
use opentelemetry::{Context, Key, KeyValue, Value};
@@ -516,7 +516,7 @@ mod tests {
516516
.fetch_add(1, Ordering::SeqCst);
517517
}
518518

519-
fn on_end(&self, _span: SpanData) {
519+
fn on_end(&self, _span: &mut FinishedSpan) {
520520
// ignore
521521
}
522522

@@ -779,7 +779,7 @@ mod tests {
779779
// No operation needed for this processor
780780
}
781781

782-
fn on_end(&self, _span: SpanData) {
782+
fn on_end(&self, _span: &mut FinishedSpan) {
783783
// No operation needed for this processor
784784
}
785785

0 commit comments

Comments
 (0)