Skip to content

Commit 4d3f5bf

Browse files
committed
feat: add opentelemetry layers
1 parent c6ac468 commit 4d3f5bf

File tree

12 files changed

+128
-26
lines changed

12 files changed

+128
-26
lines changed

Cargo.lock

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/synd_api/src/main.rs

+26-12
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1+
use synd_o11y::{opentelemetry::OpenTelemetryGuard, tracing_subscriber::otel_metrics};
12
use tracing::{error, info};
23

34
use synd_api::{args, config, dependency::Dependency, serve::listen_and_serve};
45

5-
fn init_tracing() {
6+
fn init_tracing() -> Option<OpenTelemetryGuard> {
67
use synd_o11y::{
78
opentelemetry::init_propagation,
8-
tracing_subscriber::{audit, otel_log},
9+
tracing_subscriber::{audit, otel_log, otel_trace},
910
};
1011
use tracing_subscriber::{
1112
filter::EnvFilter, fmt, layer::SubscriberExt, util::SubscriberInitExt as _, Layer as _,
@@ -19,14 +20,25 @@ fn init_tracing() {
1920
let show_src = true;
2021
let show_target = !show_src;
2122

22-
let otel_log_layer = std::env::var("OTEL_EXPORTER_OTLP_ENDPOINT")
23-
.ok()
24-
.filter(|endpoint| !endpoint.is_empty())
25-
.map(|endpoint| {
26-
let resource =
27-
synd_o11y::opentelemetry::resource(config::NAME, config::VERSION, "local");
28-
otel_log::layer(endpoint, resource)
29-
});
23+
let (opentelemetry_layers, guard) = {
24+
match std::env::var("OTEL_EXPORTER_OTLP_ENDPOINT").ok() {
25+
None => (None, None),
26+
Some(endpoint) if endpoint.is_empty() => (None, None),
27+
Some(endpoint) => {
28+
let resource =
29+
synd_o11y::opentelemetry::resource(config::NAME, config::VERSION, "local");
30+
31+
let trace_layer = otel_trace::layer(resource.clone());
32+
let log_layer = otel_log::layer(endpoint, resource.clone());
33+
let metrics_layer = otel_metrics::layer(resource);
34+
35+
(
36+
Some(trace_layer.and_then(log_layer).and_then(metrics_layer)),
37+
Some(synd_o11y::opentelemetry::OpenTelemetryGuard),
38+
)
39+
}
40+
}
41+
};
3042

3143
Registry::default()
3244
.with(
@@ -36,7 +48,7 @@ fn init_tracing() {
3648
.with_file(show_src)
3749
.with_line_number(show_src)
3850
.with_target(show_target)
39-
.and_then(otel_log_layer)
51+
.and_then(opentelemetry_layers)
4052
.with_filter(
4153
EnvFilter::try_from_default_env()
4254
.or_else(|_| EnvFilter::try_new("info"))
@@ -49,13 +61,15 @@ fn init_tracing() {
4961

5062
// Set text map propagator globally
5163
init_propagation();
64+
65+
guard
5266
}
5367

5468
#[tokio::main]
5569
async fn main() {
5670
let args = args::parse();
5771

58-
init_tracing();
72+
let _guard = init_tracing();
5973

6074
let dep = Dependency::new(args.kvsd).await.unwrap();
6175

crates/synd_api/src/serve/layer/trace.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ impl<B> tower_http::trace::MakeSpan<B> for MakeSpan {
1111

1212
let request_id = cx
1313
.baggage()
14-
.get("request.id")
14+
.get(synd_o11y::REQUEST_ID_KEY)
1515
.map_or("?".into(), |v| v.as_str());
1616

1717
let span = tracing::span!(

crates/synd_o11y/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ opentelemetry-http = { workspace = true }
2020
opentelemetry-otlp = { version = "0.14.0", default-features = false, features = ["trace", "metrics", "logs", "grpc-tonic"] }
2121
opentelemetry-semantic-conventions = { workspace = true }
2222
opentelemetry_sdk = { workspace = true, features = ["logs", "rt-tokio"] }
23+
rand = { workspace = true }
2324
reqwest = { workspace = true }
2425
tracing = { workspace = true }
2526
tracing-opentelemetry = { workspace = true }

crates/synd_o11y/src/lib.rs

+17
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,19 @@
1+
use ::opentelemetry::KeyValue;
2+
13
pub mod opentelemetry;
24
pub mod tracing_subscriber;
5+
6+
/// Request id key for opentelemetry baggage
7+
pub const REQUEST_ID_KEY: &str = "request.id";
8+
9+
/// Generate random request id
10+
pub fn request_id() -> String {
11+
// https://stackoverflow.com/questions/54275459/how-do-i-create-a-random-string-by-sampling-from-alphanumeric-characters
12+
use rand::distributions::{Alphanumeric, DistString};
13+
Alphanumeric.sample_string(&mut rand::thread_rng(), 10)
14+
}
15+
16+
/// Generate random request id key value
17+
pub fn request_id_key_value() -> KeyValue {
18+
KeyValue::new(REQUEST_ID_KEY, request_id())
19+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/// `OpenTelemetry` terminination process handler
2+
pub struct OpenTelemetryGuard;
3+
4+
impl Drop for OpenTelemetryGuard {
5+
fn drop(&mut self) {
6+
opentelemetry::global::shutdown_tracer_provider();
7+
opentelemetry::global::shutdown_meter_provider();
8+
opentelemetry::global::shutdown_logger_provider();
9+
}
10+
}

crates/synd_o11y/src/opentelemetry/mod.rs

+3
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,6 @@ mod propagation;
55
pub use propagation::{extension, http, init_propagation};
66

77
pub use opentelemetry::KeyValue;
8+
9+
mod guard;
10+
pub use guard::OpenTelemetryGuard;
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
11
pub mod audit;
22
pub mod otel_log;
3+
pub mod otel_metrics;
4+
pub mod otel_trace;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
use std::time::Duration;
2+
3+
use opentelemetry_sdk::{metrics::MeterProvider, runtime, Resource};
4+
use tracing::Subscriber;
5+
use tracing_opentelemetry::MetricsLayer;
6+
use tracing_subscriber::{registry::LookupSpan, Layer};
7+
8+
pub fn layer<S>(resource: Resource) -> impl Layer<S>
9+
where
10+
S: Subscriber + for<'span> LookupSpan<'span>,
11+
{
12+
MetricsLayer::new(init_meter_provider(resource))
13+
}
14+
15+
fn init_meter_provider(resource: Resource) -> MeterProvider {
16+
let provider = opentelemetry_otlp::new_pipeline()
17+
.metrics(runtime::Tokio)
18+
.with_resource(resource)
19+
.with_exporter(opentelemetry_otlp::new_exporter().tonic())
20+
.with_period(Duration::from_secs(60 * 3))
21+
.build()
22+
.unwrap();
23+
24+
opentelemetry::global::set_meter_provider(provider.clone());
25+
26+
provider
27+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
use opentelemetry_sdk::{
2+
runtime,
3+
trace::{BatchConfig, Sampler, Tracer},
4+
Resource,
5+
};
6+
use tracing::Subscriber;
7+
use tracing_opentelemetry::OpenTelemetryLayer;
8+
use tracing_subscriber::{registry::LookupSpan, Layer};
9+
10+
pub fn layer<S>(resource: Resource) -> impl Layer<S>
11+
where
12+
S: Subscriber + for<'span> LookupSpan<'span>,
13+
{
14+
OpenTelemetryLayer::new(init_tracer(resource))
15+
}
16+
17+
fn init_tracer(resource: Resource) -> Tracer {
18+
// https://opentelemetry.io/docs/specs/otel/configuration/sdk-environment-variables/
19+
let sampler_ratio = std::env::var("OTEL_TRACES_SAMPLER_ARG")
20+
.ok()
21+
.and_then(|v| v.parse::<f64>().ok())
22+
.unwrap_or(0.);
23+
24+
// TODO: construct tonic transport channel
25+
opentelemetry_otlp::new_pipeline()
26+
.tracing()
27+
.with_trace_config(
28+
opentelemetry_sdk::trace::Config::default()
29+
.with_sampler(Sampler::ParentBased(Box::new(Sampler::TraceIdRatioBased(
30+
sampler_ratio,
31+
))))
32+
.with_resource(resource),
33+
)
34+
.with_batch_config(BatchConfig::default())
35+
.with_exporter(opentelemetry_otlp::new_exporter().tonic())
36+
.install_batch(runtime::Tokio)
37+
.unwrap()
38+
}

crates/synd_term/Cargo.toml

-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ futures-util = "0.3.30"
3030
graphql_client = { workspace = true }
3131
html2text = { version = "0.12" }
3232
open = "5.0.1"
33-
rand = { workspace = true }
3433
ratatui = { git = "https://github.com/ratatui-org/ratatui.git", branch = "main" }
3534
reqwest = { workspace = true }
3635
serde = { workspace = true, features = ["derive"] }

crates/synd_term/src/client/mod.rs

+2-11
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use anyhow::anyhow;
44
use graphql_client::{GraphQLQuery, Response};
55
use reqwest::header::{self, HeaderValue};
66
use serde::{de::DeserializeOwned, Serialize};
7-
use synd_o11y::opentelemetry::{extension::*, KeyValue};
7+
use synd_o11y::opentelemetry::extension::*;
88
use tracing::{error, Span};
99
use url::Url;
1010

@@ -124,13 +124,10 @@ impl Client {
124124
.json(&body)
125125
.build()?;
126126

127-
// TODO: use trace_id
128-
let request_id = Self::request_id();
129-
130127
synd_o11y::opentelemetry::http::inject_with_baggage(
131128
&Span::current().context(),
132129
request.headers_mut(),
133-
std::iter::once(KeyValue::new("request.id", request_id)),
130+
std::iter::once(synd_o11y::request_id_key_value()),
134131
);
135132

136133
let response: Response<ResponseData> = self
@@ -152,10 +149,4 @@ impl Client {
152149
_ => Err(anyhow::anyhow!("unexpected response",)),
153150
}
154151
}
155-
156-
fn request_id() -> String {
157-
// https://stackoverflow.com/questions/54275459/how-do-i-create-a-random-string-by-sampling-from-alphanumeric-characters
158-
use rand::distributions::{Alphanumeric, DistString};
159-
Alphanumeric.sample_string(&mut rand::thread_rng(), 10)
160-
}
161152
}

0 commit comments

Comments
 (0)