Skip to content

Commit 11d57b8

Browse files
authored
chore: add opentelemetry-otlp integration example (#53)
## Motivation * Provide specific example of how to set up various opentelemetry configrations from tracing When connecting tracing and opentelemetry ecosystems ## Solution This example aims to demonstrate the following points * tracing-opentelemetry configuration is done through the open telemetry sdk * telemetry export is done using a plugin (opentelemetry-otlp), and the plugin provides the sdk construct helper methods
1 parent 680f91d commit 11d57b8

File tree

2 files changed

+167
-1
lines changed

2 files changed

+167
-1
lines changed

Cargo.toml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,17 @@ smallvec = { version = "1.0", optional = true }
4141
[dev-dependencies]
4242
async-trait = "0.1.56"
4343
criterion = { version = "0.4.0", default-features = false, features = ["html_reports"] }
44+
opentelemetry = { version = "0.20.0", features = ["trace", "metrics", "rt-tokio"] }
45+
opentelemetry_sdk = { version = "0.20.0", default-features = false, features = ["trace"] }
4446
opentelemetry-jaeger = "0.19.0"
45-
opentelemetry-stdout = { version = "0.1.0", features = ["trace"] }
47+
opentelemetry-stdout = { version = "0.1.0", features = ["trace", "metrics"] }
48+
opentelemetry-otlp = { version = "0.13.0", features = ["metrics"] }
49+
opentelemetry-semantic-conventions = "0.12.0"
4650
futures-util = { version = "0.3", default-features = false }
4751
tokio = { version = "1", features = ["full"] }
4852
tokio-stream = "0.1"
4953
tracing = { version = "0.1.35", default-features = false, features = ["std", "attributes"] }
54+
tracing-subscriber = { version = "0.3.0", default-features = false, features = ["registry", "std", "fmt"] }
5055

5156
[target.'cfg(not(target_os = "windows"))'.dev-dependencies]
5257
pprof = { version = "0.11.1", features = ["flamegraph", "criterion"] }

examples/opentelemetry-otlp.rs

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
use opentelemetry::{global, runtime, Key, KeyValue};
2+
use opentelemetry_otlp::TonicExporterBuilder;
3+
use opentelemetry_sdk::{
4+
metrics::{
5+
reader::{DefaultAggregationSelector, DefaultTemporalitySelector},
6+
Aggregation, Instrument, MeterProvider, PeriodicReader, Stream,
7+
},
8+
trace::{BatchConfig, RandomIdGenerator, Sampler, Tracer},
9+
Resource,
10+
};
11+
use opentelemetry_semantic_conventions::{
12+
resource::{DEPLOYMENT_ENVIRONMENT, SERVICE_NAME, SERVICE_VERSION},
13+
SCHEMA_URL,
14+
};
15+
use tracing_core::Level;
16+
use tracing_opentelemetry::{MetricsLayer, OpenTelemetryLayer};
17+
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
18+
19+
// Create a Resource that captures information about the entity for which telemetry is recorded.
20+
fn resource() -> Resource {
21+
Resource::from_schema_url(
22+
[
23+
KeyValue::new(SERVICE_NAME, env!("CARGO_PKG_NAME")),
24+
KeyValue::new(SERVICE_VERSION, env!("CARGO_PKG_VERSION")),
25+
KeyValue::new(DEPLOYMENT_ENVIRONMENT, "develop"),
26+
],
27+
SCHEMA_URL,
28+
)
29+
}
30+
31+
// Construct MeterProvider for MetricsLayer
32+
fn init_meter_provider() -> MeterProvider {
33+
// Currently we could not access MeterProviderBuilder from opentelemetry_otlp
34+
// However to customize View we need MeterBuilder, so manually construct.
35+
let exporter = opentelemetry_otlp::MetricsExporter::new(
36+
TonicExporterBuilder::default(),
37+
Box::new(DefaultTemporalitySelector::new()),
38+
Box::new(DefaultAggregationSelector::new()),
39+
)
40+
.unwrap();
41+
42+
let reader = PeriodicReader::builder(exporter, runtime::Tokio)
43+
.with_interval(std::time::Duration::from_secs(30))
44+
.build();
45+
46+
// For debugging in development
47+
let stdout_reader = PeriodicReader::builder(
48+
opentelemetry_stdout::MetricsExporter::default(),
49+
runtime::Tokio,
50+
)
51+
.build();
52+
53+
// Rename foo metrics to foo_named and drop key_2 attribute
54+
let view_foo = |instrument: &Instrument| -> Option<Stream> {
55+
if instrument.name == "foo" {
56+
Some(
57+
Stream::new()
58+
.name("foo_named")
59+
.allowed_attribute_keys([Key::from("key_1")]),
60+
)
61+
} else {
62+
None
63+
}
64+
};
65+
66+
// Set Custom histogram boundaries for baz metrics
67+
let view_baz = |instrument: &Instrument| -> Option<Stream> {
68+
if instrument.name == "baz" {
69+
Some(
70+
Stream::new()
71+
.name("baz")
72+
.aggregation(Aggregation::ExplicitBucketHistogram {
73+
boundaries: vec![0.0, 2.0, 4.0, 8.0, 16.0, 32.0, 64.0],
74+
record_min_max: true,
75+
}),
76+
)
77+
} else {
78+
None
79+
}
80+
};
81+
82+
let meter_provider = MeterProvider::builder()
83+
.with_resource(resource())
84+
.with_reader(reader)
85+
.with_reader(stdout_reader)
86+
.with_view(view_foo)
87+
.with_view(view_baz)
88+
.build();
89+
90+
global::set_meter_provider(meter_provider.clone());
91+
92+
meter_provider
93+
}
94+
95+
// Construct Tracer for OpenTelemetryLayer
96+
fn init_tracer() -> Tracer {
97+
opentelemetry_otlp::new_pipeline()
98+
.tracing()
99+
.with_trace_config(
100+
opentelemetry_sdk::trace::Config::default()
101+
// Customize sampling strategy
102+
.with_sampler(Sampler::ParentBased(Box::new(Sampler::TraceIdRatioBased(
103+
1.0,
104+
))))
105+
// If export trace to AWS X-Ray, you can use XrayIdGenerator
106+
.with_id_generator(RandomIdGenerator::default())
107+
.with_resource(resource()),
108+
)
109+
.with_batch_config(BatchConfig::default())
110+
.with_exporter(opentelemetry_otlp::new_exporter().tonic())
111+
.install_batch(runtime::Tokio)
112+
.unwrap()
113+
}
114+
115+
// Initialize tracing-subscriber and return OtelGuard for opentelemetry-related termination processing
116+
fn init_tracing_subscriber() -> OtelGuard {
117+
let meter_provider = init_meter_provider();
118+
119+
tracing_subscriber::registry()
120+
.with(tracing_subscriber::filter::LevelFilter::from_level(
121+
Level::INFO,
122+
))
123+
.with(tracing_subscriber::fmt::layer())
124+
.with(MetricsLayer::new(meter_provider.clone()))
125+
.with(OpenTelemetryLayer::new(init_tracer()))
126+
.init();
127+
128+
OtelGuard { meter_provider }
129+
}
130+
131+
struct OtelGuard {
132+
meter_provider: MeterProvider,
133+
}
134+
135+
impl Drop for OtelGuard {
136+
fn drop(&mut self) {
137+
if let Err(err) = self.meter_provider.shutdown() {
138+
eprintln!("{err:?}");
139+
}
140+
opentelemetry::global::shutdown_tracer_provider();
141+
}
142+
}
143+
144+
#[tokio::main]
145+
async fn main() {
146+
let _guard = init_tracing_subscriber();
147+
148+
foo().await;
149+
}
150+
151+
#[tracing::instrument]
152+
async fn foo() {
153+
tracing::info!(
154+
monotonic_counter.foo = 1_u64,
155+
key_1 = "bar",
156+
key_2 = 10,
157+
"handle foo",
158+
);
159+
160+
tracing::info!(histogram.baz = 10, "histogram example",);
161+
}

0 commit comments

Comments
 (0)