Skip to content

Commit

Permalink
docs: Modify examples to show best practices - reuse tracer (#2709)
Browse files Browse the repository at this point in the history
  • Loading branch information
cijothomas authored Feb 26, 2025
1 parent 11ed8e0 commit edb0d58
Show file tree
Hide file tree
Showing 5 changed files with 126 additions and 15 deletions.
30 changes: 19 additions & 11 deletions examples/tracing-http-propagator/src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,22 @@ use http_body_util::{combinators::BoxBody, BodyExt, Full};
use hyper::{body::Incoming, service::service_fn, Request, Response, StatusCode};
use hyper_util::rt::{TokioExecutor, TokioIo};
use opentelemetry::{
global,
global::{self, BoxedTracer},
trace::{FutureExt, Span, SpanKind, TraceContextExt, Tracer},
Context, KeyValue,
};
use opentelemetry_http::{Bytes, HeaderExtractor};
use opentelemetry_sdk::{propagation::TraceContextPropagator, trace::SdkTracerProvider};
use opentelemetry_semantic_conventions::trace;
use opentelemetry_stdout::SpanExporter;
use std::{convert::Infallible, net::SocketAddr};
use std::{convert::Infallible, net::SocketAddr, sync::OnceLock};
use tokio::net::TcpListener;

fn get_tracer() -> &'static BoxedTracer {
static TRACER: OnceLock<BoxedTracer> = OnceLock::new();
TRACER.get_or_init(|| global::tracer("example/server"))
}

// Utility function to extract the context from the incoming request headers
fn extract_context_from_request(req: &Request<Incoming>) -> Context {
global::get_text_map_propagator(|propagator| {
Expand All @@ -24,11 +29,11 @@ fn extract_context_from_request(req: &Request<Incoming>) -> Context {
async fn handle_health_check(
_req: Request<Incoming>,
) -> Result<Response<BoxBody<Bytes, hyper::Error>>, Infallible> {
let tracer = global::tracer("example/server");
let tracer = get_tracer();
let mut span = tracer
.span_builder("health_check")
.with_kind(SpanKind::Internal)
.start(&tracer);
.start(tracer);
span.add_event("Health check accessed", vec![]);

let res = Response::new(
Expand All @@ -44,11 +49,11 @@ async fn handle_health_check(
async fn handle_echo(
req: Request<Incoming>,
) -> Result<Response<BoxBody<Bytes, hyper::Error>>, Infallible> {
let tracer = global::tracer("example/server");
let tracer = get_tracer();
let mut span = tracer
.span_builder("echo")
.with_kind(SpanKind::Internal)
.start(&tracer);
.start(tracer);
span.add_event("Echoing back the request", vec![]);

let res = Response::new(req.into_body().boxed());
Expand All @@ -63,11 +68,11 @@ async fn router(
let parent_cx = extract_context_from_request(&req);
let response = {
// Create a span parenting the remote client span.
let tracer = global::tracer("example/server");
let tracer = get_tracer();
let mut span = tracer
.span_builder("router")
.with_kind(SpanKind::Server)
.start_with_context(&tracer, &parent_cx);
.start_with_context(tracer, &parent_cx);

span.add_event("dispatching request", vec![]);

Expand All @@ -88,7 +93,7 @@ async fn router(
response
}

fn init_tracer() {
fn init_tracer() -> SdkTracerProvider {
global::set_text_map_propagator(TraceContextPropagator::new());

// Setup tracerprovider with stdout exporter
Expand All @@ -97,14 +102,15 @@ fn init_tracer() {
.with_simple_exporter(SpanExporter::default())
.build();

global::set_tracer_provider(provider);
global::set_tracer_provider(provider.clone());
provider
}

#[tokio::main]
async fn main() {
use hyper_util::server::conn::auto::Builder;

init_tracer();
let provider = init_tracer();
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
let listener = TcpListener::bind(addr).await.unwrap();

Expand All @@ -116,4 +122,6 @@ async fn main() {
eprintln!("{err}");
}
}

provider.shutdown().expect("Shutdown provider failed");
}
5 changes: 4 additions & 1 deletion opentelemetry-sdk/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -83,12 +83,15 @@ harness = false
[[bench]]
name = "trace"
harness = false
required-features = ["testing"]

[[bench]]
name = "log_processor"
harness = false

[[bench]]
name = "tracer_creation"
harness = false

[[bench]]
name = "log_exporter"
harness = false
Expand Down
100 changes: 100 additions & 0 deletions opentelemetry-sdk/benches/tracer_creation.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use opentelemetry::{
global::{self, BoxedTracer},
InstrumentationScope, KeyValue,
};
use opentelemetry_sdk::trace as sdktrace;
use std::sync::OnceLock;

#[cfg(not(target_os = "windows"))]
use pprof::criterion::{Output, PProfProfiler};

/*
Adding results in comments for a quick reference.
Apple M4 Pro
Total Number of Cores: 14 (10 performance and 4 efficiency)
Tracer_With_Name/new_each_time 20 ns
Tracer_With_Name/reuse_existing 383 ps
Tracer_With_Name_And_Scope_Attrs/new_each_time 63 ns
Tracer_With_Name_And_Scope_Attrs/reuse_existing 385 ps
*/

fn get_tracer() -> &'static BoxedTracer {
static TRACER: OnceLock<BoxedTracer> = OnceLock::new();
TRACER.get_or_init(|| global::tracer("tracer"))
}

fn get_tracer_with_scope_attrs() -> &'static BoxedTracer {
static TRACER_WITH_ATTRS: OnceLock<BoxedTracer> = OnceLock::new();
TRACER_WITH_ATTRS.get_or_init(|| {
let scope = InstrumentationScope::builder("tracer")
.with_attributes([KeyValue::new("key", "value")])
.build();
global::tracer_with_scope(scope)
})
}

fn create_provider() -> sdktrace::SdkTracerProvider {
// Provider is empty, no exporters, no processors etc.
// as the goal is measurement of tracer creation time.
sdktrace::SdkTracerProvider::builder().build()
}

fn criterion_benchmark(c: &mut Criterion) {
let mut group = c.benchmark_group("Tracer_With_Name");
group.bench_function("new_each_time", |b| {
let provider = create_provider();
global::set_tracer_provider(provider);
b.iter(|| {
black_box(global::tracer("tracer"));
});
});

group.bench_function("reuse_existing", |b| {
let provider = create_provider();
global::set_tracer_provider(provider);
b.iter(|| {
black_box(get_tracer());
});
});

group.finish();

let mut group = c.benchmark_group("Tracer_With_Name_And_Scope_Attrs");
group.bench_function("new_each_time", |b| {
let provider = create_provider();
global::set_tracer_provider(provider);
b.iter(|| {
let scope = InstrumentationScope::builder("tracer")
.with_attributes([KeyValue::new("key", "value")])
.build();
black_box(global::tracer_with_scope(scope));
});
});

group.bench_function("reuse_existing", |b| {
let provider = create_provider();
global::set_tracer_provider(provider);
b.iter(|| {
black_box(get_tracer_with_scope_attrs());
});
});
group.finish();
}

#[cfg(not(target_os = "windows"))]
criterion_group! {
name = benches;
config = Criterion::default().with_profiler(PProfProfiler::new(100, Output::Flamegraph(None)));
targets = criterion_benchmark
}

#[cfg(target_os = "windows")]
criterion_group! {
name = benches;
config = Criterion::default();
targets = criterion_benchmark
}

criterion_main!(benches);
2 changes: 1 addition & 1 deletion opentelemetry/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,7 @@ impl fmt::Debug for Context {
}
}

dbg.field("entries", &entries).finish()
dbg.field("entries count", &entries).finish()
}
}

Expand Down
4 changes: 2 additions & 2 deletions opentelemetry/src/trace/span_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -400,7 +400,7 @@ mod tests {
let cx = Context::current();
assert_eq!(
format!("{:?}", cx),
"Context { span: \"None\", entries: 0 }"
"Context { span: \"None\", entries count: 0 }"
);
let cx = Context::current().with_remote_span_context(SpanContext::NONE);
assert_eq!(
Expand All @@ -413,7 +413,7 @@ mod tests {
is_remote: false, \
trace_state: TraceState(None) \
}, \
entries: 1 \
entries count: 1 \
}"
);
}
Expand Down

0 comments on commit edb0d58

Please sign in to comment.