clnrm_core/telemetry/
init.rs1use crate::error::Result;
7use crate::telemetry::config::{ExporterConfig, OtlpProtocol, TelemetryConfig};
8use crate::telemetry::exporters::{
9 create_span_exporter, validate_exporter_config, SpanExporterType,
10};
11use opentelemetry::global;
12use opentelemetry::trace::TracerProvider;
13use opentelemetry::KeyValue;
14use opentelemetry_sdk::{
15 trace::{self, RandomIdGenerator, Sampler},
16 Resource,
17};
18use tracing_opentelemetry;
19use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
20
21#[derive(Debug)]
23pub struct TelemetryHandle {
24 config: TelemetryConfig,
25}
26
27impl TelemetryHandle {
28 pub fn disabled() -> Self {
30 Self {
31 config: TelemetryConfig {
32 enabled: false,
33 ..Default::default()
34 },
35 }
36 }
37
38 pub fn is_enabled(&self) -> bool {
40 self.config.enabled
41 }
42
43 pub fn service_name(&self) -> &str {
45 &self.config.service_name
46 }
47
48 pub fn service_version(&self) -> &str {
50 &self.config.service_version
51 }
52
53 pub fn shutdown(self) -> Result<()> {
55 if self.config.enabled {
56 }
59 Ok(())
60 }
61}
62
63pub struct TelemetryBuilder {
65 config: TelemetryConfig,
66}
67
68impl TelemetryBuilder {
69 pub fn new(config: TelemetryConfig) -> Self {
71 Self { config }
72 }
73
74 pub fn init(self) -> Result<TelemetryHandle> {
76 if !self.config.enabled {
77 return Ok(TelemetryHandle::disabled());
78 }
79
80 self.init_tracing()?;
82
83 self.init_metrics()?;
85
86 Ok(TelemetryHandle {
87 config: self.config,
88 })
89 }
90
91 fn create_resource(&self) -> Result<Resource> {
93 let mut resource_builder = Resource::builder_empty()
95 .with_service_name(self.config.service_name.clone())
96 .with_attributes([
97 KeyValue::new("service.version", self.config.service_version.clone()),
98 KeyValue::new("telemetry.sdk.language", "rust"),
99 KeyValue::new("telemetry.sdk.name", "opentelemetry"),
100 KeyValue::new("telemetry.sdk.version", "0.31.0"),
101 KeyValue::new(
102 "service.instance.id",
103 format!("clnrm-{}", std::process::id()),
104 ),
105 ]);
106
107 for (key, value) in &self.config.resource_attributes {
109 resource_builder =
110 resource_builder.with_attributes([KeyValue::new(key.clone(), value.clone())]);
111 }
112
113 let resource = resource_builder.build();
114 Ok(resource)
115 }
116
117 fn create_exporters(&self) -> Result<Vec<SpanExporterType>> {
119 let mut exporters = Vec::new();
120
121 for exporter_config in &self.config.exporters {
122 validate_exporter_config(exporter_config)?;
124
125 let exporter = create_span_exporter(exporter_config)?;
127 exporters.push(exporter);
128 }
129
130 Ok(exporters)
131 }
132
133 fn init_tracing(&self) -> Result<()> {
135 let resource = self.create_resource()?;
137
138 let tracer_provider_builder = trace::SdkTracerProvider::builder()
139 .with_sampler(Sampler::TraceIdRatioBased(
140 self.config.sampling.trace_sampling_ratio,
141 ))
142 .with_id_generator(RandomIdGenerator::default())
143 .with_resource(resource);
144
145 let exporters = self.create_exporters()?;
147
148 if let Some(exporter) = exporters.into_iter().next() {
150 let tracer_provider = tracer_provider_builder
151 .with_batch_exporter(exporter)
152 .build();
153 let tracer = tracer_provider.tracer("clnrm");
154
155 global::set_tracer_provider(tracer_provider);
156
157 tracing_subscriber::registry()
158 .with(tracing_opentelemetry::layer().with_tracer(tracer))
159 .with(tracing_subscriber::fmt::layer())
160 .init();
161 } else {
162 let exporter = opentelemetry_sdk::trace::InMemorySpanExporter::default();
164 let tracer_provider = tracer_provider_builder
165 .with_batch_exporter(exporter)
166 .build();
167 let tracer = tracer_provider.tracer("clnrm");
168
169 global::set_tracer_provider(tracer_provider);
170
171 tracing_subscriber::registry()
172 .with(tracing_opentelemetry::layer().with_tracer(tracer))
173 .with(tracing_subscriber::fmt::layer())
174 .init();
175 }
176
177 Ok(())
178 }
179
180 fn init_metrics(&self) -> Result<()> {
182 let resource = self.create_resource()?;
184
185 let meter_provider = opentelemetry_sdk::metrics::SdkMeterProvider::builder()
187 .with_resource(resource)
188 .build();
189
190 global::set_meter_provider(meter_provider);
191
192 Ok(())
193 }
194}
195
196pub fn init_default() -> Result<TelemetryHandle> {
198 let config = TelemetryConfig::default();
199 TelemetryBuilder::new(config).init()
200}
201
202pub fn init_otlp(endpoint: &str) -> Result<TelemetryHandle> {
204 let config = TelemetryConfig {
205 enabled: true,
206 exporters: vec![ExporterConfig::Otlp {
207 endpoint: endpoint.to_string(),
208 protocol: OtlpProtocol::HttpProto,
209 headers: std::collections::HashMap::new(),
210 }],
211 ..Default::default()
212 };
213 TelemetryBuilder::new(config).init()
214}
215
216pub fn init_stdout() -> Result<TelemetryHandle> {
218 let config = TelemetryConfig {
219 enabled: true,
220 exporters: vec![ExporterConfig::Stdout { pretty_print: true }],
221 ..Default::default()
222 };
223 TelemetryBuilder::new(config).init()
224}