sketching/
otel.rs
1use std::{str::FromStr, time::Duration};
2
3use opentelemetry_otlp::{Protocol, WithExportConfig};
4
5use opentelemetry::{global, trace::TracerProvider as _, KeyValue};
6
7use opentelemetry_sdk::{
8 trace::{Sampler, TracerProvider},
9 Resource,
10};
11use tracing::Subscriber;
12use tracing_core::Level;
13
14use tracing_subscriber::{filter::Directive, prelude::*, EnvFilter, Registry};
15
16pub const MAX_EVENTS_PER_SPAN: u32 = 64 * 1024;
17pub const MAX_ATTRIBUTES_PER_SPAN: u32 = 128;
18
19use opentelemetry_semantic_conventions::{
20 attribute::{SERVICE_NAME, SERVICE_VERSION},
21 SCHEMA_URL,
22};
23
24pub fn start_logging_pipeline(
43 otlp_endpoint: &Option<String>,
44 log_filter: crate::LogLevel,
45 service_name: &'static str,
46) -> Result<Box<dyn Subscriber + Send + Sync>, String> {
47 let forest_filter: EnvFilter = EnvFilter::builder()
48 .with_default_directive(log_filter.into())
49 .from_env_lossy();
50
51 match otlp_endpoint {
53 Some(endpoint) => {
54 let forest_filter = forest_filter
56 .add_directive(
57 Directive::from_str("tonic=info").expect("Failed to set tonic logging to info"),
58 )
59 .add_directive(
60 Directive::from_str("h2=info").expect("Failed to set h2 logging to info"),
61 )
62 .add_directive(
63 Directive::from_str("hyper=info").expect("Failed to set hyper logging to info"),
64 );
65 let forest_layer = tracing_forest::ForestLayer::default().with_filter(forest_filter);
66 let t_filter: EnvFilter = EnvFilter::builder()
67 .with_default_directive(log_filter.into())
68 .from_env_lossy();
69
70 let otlp_exporter = opentelemetry_otlp::SpanExporter::builder()
71 .with_tonic()
72 .with_endpoint(endpoint)
73 .with_protocol(Protocol::HttpBinary)
74 .with_timeout(Duration::from_secs(5))
75 .build()
76 .map_err(|err| err.to_string())?;
77
78 let git_rev = match option_env!("KANIDM_PKG_COMMIT_REV") {
80 Some(rev) => format!("-{}", rev),
81 None => "".to_string(),
82 };
83
84 let version = format!("{}{}", env!("CARGO_PKG_VERSION"), git_rev);
85 let resource = Resource::from_schema_url(
90 [
91 KeyValue::new(SERVICE_NAME, service_name),
93 KeyValue::new(SERVICE_VERSION, version),
94 ],
97 SCHEMA_URL,
98 );
99
100 let provider = TracerProvider::builder()
101 .with_batch_exporter(otlp_exporter, opentelemetry_sdk::runtime::Tokio)
102 .with_sampler(Sampler::AlwaysOn)
104 .with_max_events_per_span(MAX_EVENTS_PER_SPAN)
105 .with_max_attributes_per_span(MAX_ATTRIBUTES_PER_SPAN)
106 .with_resource(resource)
107 .build();
108
109 global::set_tracer_provider(provider.clone());
110 provider.tracer("tracing-otel-subscriber");
111 use tracing_opentelemetry::OpenTelemetryLayer;
112
113 let registry = tracing_subscriber::registry()
114 .with(
115 tracing_subscriber::filter::LevelFilter::from_level(Level::INFO)
116 .with_filter(t_filter),
117 )
118 .with(tracing_subscriber::fmt::layer())
119 .with(forest_layer)
121 .with(OpenTelemetryLayer::new(
122 provider.tracer("tracing-otel-subscriber"),
123 ));
124
125 Ok(Box::new(registry))
126 }
127 None => {
128 let forest_layer = tracing_forest::ForestLayer::default().with_filter(forest_filter);
129 Ok(Box::new(Registry::default().with(forest_layer)))
130 }
131 }
132}
133
134pub struct TracingPipelineGuard {}
137
138impl Drop for TracingPipelineGuard {
139 fn drop(&mut self) {
140 opentelemetry::global::shutdown_tracer_provider();
141 eprintln!("Logging pipeline completed shutdown");
142 }
143}