Skip to content

Commit a969b9e

Browse files
authored
Fix example installing OpenTelemetryLayer into a global subscriber
1 parent f6fc075 commit a969b9e

File tree

2 files changed

+108
-11
lines changed

2 files changed

+108
-11
lines changed

examples/opentelemetry-otlp.rs

+23-11
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use opentelemetry::{global, trace::TracerProvider as _, KeyValue};
22
use opentelemetry_sdk::{
33
metrics::{MeterProviderBuilder, PeriodicReader, SdkMeterProvider},
44
runtime,
5-
trace::{RandomIdGenerator, Sampler, Tracer, TracerProvider},
5+
trace::{RandomIdGenerator, Sampler, TracerProvider},
66
Resource,
77
};
88
use opentelemetry_semantic_conventions::{
@@ -55,14 +55,14 @@ fn init_meter_provider() -> SdkMeterProvider {
5555
meter_provider
5656
}
5757

58-
// Construct Tracer for OpenTelemetryLayer
59-
fn init_tracer() -> Tracer {
58+
// Construct TracerProvider for OpenTelemetryLayer
59+
fn init_tracer_provider() -> TracerProvider {
6060
let exporter = opentelemetry_otlp::SpanExporter::builder()
6161
.with_tonic()
6262
.build()
6363
.unwrap();
6464

65-
let provider = TracerProvider::builder()
65+
TracerProvider::builder()
6666
.with_config(
6767
opentelemetry_sdk::trace::Config::default()
6868
// Customize sampling strategy
@@ -74,18 +74,24 @@ fn init_tracer() -> Tracer {
7474
.with_resource(resource()),
7575
)
7676
.with_batch_exporter(exporter, runtime::Tokio)
77-
.build();
78-
79-
global::set_tracer_provider(provider.clone());
80-
provider.tracer("tracing-otel-subscriber")
77+
.build()
8178
}
8279

8380
// Initialize tracing-subscriber and return OtelGuard for opentelemetry-related termination processing
8481
fn init_tracing_subscriber() -> OtelGuard {
82+
let tracer_provider = init_tracer_provider();
8583
let meter_provider = init_meter_provider();
86-
let tracer = init_tracer();
84+
85+
let tracer = tracer_provider.tracer("tracing-otel-subscriber");
8786

8887
tracing_subscriber::registry()
88+
// The global level filter prevents the exporter network stack
89+
// from reentering the globally installed OpenTelemetryLayer with
90+
// its own spans while exporting, as the libraries should not use
91+
// tracing levels below DEBUG. If the OpenTelemetry layer needs to
92+
// trace spans and events with higher verbosity levels, consider using
93+
// per-layer filtering to target the telemetry layer specifically,
94+
// e.g. by target matching.
8995
.with(tracing_subscriber::filter::LevelFilter::from_level(
9096
Level::INFO,
9197
))
@@ -94,19 +100,25 @@ fn init_tracing_subscriber() -> OtelGuard {
94100
.with(OpenTelemetryLayer::new(tracer))
95101
.init();
96102

97-
OtelGuard { meter_provider }
103+
OtelGuard {
104+
tracer_provider,
105+
meter_provider,
106+
}
98107
}
99108

100109
struct OtelGuard {
110+
tracer_provider: TracerProvider,
101111
meter_provider: SdkMeterProvider,
102112
}
103113

104114
impl Drop for OtelGuard {
105115
fn drop(&mut self) {
116+
if let Err(err) = self.tracer_provider.shutdown() {
117+
eprintln!("{err:?}");
118+
}
106119
if let Err(err) = self.meter_provider.shutdown() {
107120
eprintln!("{err:?}");
108121
}
109-
opentelemetry::global::shutdown_tracer_provider();
110122
}
111123
}
112124

tests/batch_global_subscriber.rs

+85
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
use futures_util::future::BoxFuture;
2+
use opentelemetry::{global as otel_global, trace::TracerProvider as _};
3+
use opentelemetry_sdk::{
4+
export::trace::{ExportResult, SpanData, SpanExporter},
5+
runtime,
6+
trace::TracerProvider,
7+
};
8+
use tokio::runtime::Runtime;
9+
use tracing::{info_span, subscriber, Level, Subscriber};
10+
use tracing_opentelemetry::layer;
11+
use tracing_subscriber::filter;
12+
use tracing_subscriber::prelude::*;
13+
14+
use std::sync::{Arc, Mutex};
15+
16+
#[derive(Clone, Debug, Default)]
17+
struct TestExporter(Arc<Mutex<Vec<SpanData>>>);
18+
19+
impl SpanExporter for TestExporter {
20+
fn export(&mut self, mut batch: Vec<SpanData>) -> BoxFuture<'static, ExportResult> {
21+
let spans = self.0.clone();
22+
Box::pin(async move {
23+
if let Ok(mut inner) = spans.lock() {
24+
inner.append(&mut batch);
25+
}
26+
Ok(())
27+
})
28+
}
29+
}
30+
31+
fn test_tracer(runtime: &Runtime) -> (TracerProvider, TestExporter, impl Subscriber) {
32+
let _guard = runtime.enter();
33+
34+
let exporter = TestExporter::default();
35+
let provider = TracerProvider::builder()
36+
.with_batch_exporter(exporter.clone(), runtime::Tokio)
37+
.build();
38+
let tracer = provider.tracer("test");
39+
40+
let subscriber = tracing_subscriber::registry().with(
41+
layer()
42+
.with_tracer(tracer)
43+
.with_filter(filter::Targets::new().with_target("test_telemetry", Level::INFO)),
44+
);
45+
46+
(provider, exporter, subscriber)
47+
}
48+
49+
#[test]
50+
fn shutdown_in_scope() {
51+
let rt = Runtime::new().unwrap();
52+
let (provider, exporter, subscriber) = test_tracer(&rt);
53+
54+
subscriber::set_global_default(subscriber).unwrap();
55+
56+
for _ in 0..1000 {
57+
let _span = info_span!(target: "test_telemetry", "test_span").entered();
58+
}
59+
60+
// Should flush all batched telemetry spans
61+
provider.shutdown().unwrap();
62+
63+
let spans = exporter.0.lock().unwrap();
64+
assert_eq!(spans.len(), 1000);
65+
}
66+
67+
#[test]
68+
#[ignore = "https://github.com/open-telemetry/opentelemetry-rust/issues/1961"]
69+
fn shutdown_global() {
70+
let rt = Runtime::new().unwrap();
71+
let (provider, exporter, subscriber) = test_tracer(&rt);
72+
73+
otel_global::set_tracer_provider(provider);
74+
subscriber::set_global_default(subscriber).unwrap();
75+
76+
for _ in 0..1000 {
77+
let _span = info_span!(target: "test_telemetry", "test_span").entered();
78+
}
79+
80+
// Should flush all batched telemetry spans
81+
otel_global::shutdown_tracer_provider();
82+
83+
let spans = exporter.0.lock().unwrap();
84+
assert_eq!(spans.len(), 1000);
85+
}

0 commit comments

Comments
 (0)