1use opentelemetry::{global, trace::TracerProvider, KeyValue};
2use opentelemetry_otlp::OTEL_EXPORTER_OTLP_ENDPOINT;
3use opentelemetry_sdk::{
4 metrics::{MeterProviderBuilder, PeriodicReader, SdkMeterProvider},
5 propagation::TraceContextPropagator,
6 resource::{EnvResourceDetector, SdkProvidedResourceDetector, TelemetryResourceDetector},
7 trace::{RandomIdGenerator, Sampler, SdkTracerProvider},
8 Resource,
9};
10use opentelemetry_semantic_conventions::attribute::{SERVICE_NAME, SERVICE_VERSION};
11use std::{fs::OpenOptions, path::Path};
12use tracing_opentelemetry::{MetricsLayer, OpenTelemetryLayer};
13use tracing_subscriber::{layer::SubscriberExt, prelude::*, util::SubscriberInitExt};
14
15pub mod grpc;
16
17#[tracing::instrument(skip(log_path))]
18pub async fn init_tracing_subscriber<Logfile: AsRef<Path>>(
19 telemetry_enabled: bool,
20 log_path: Logfile,
21) {
22 global::set_text_map_propagator(TraceContextPropagator::new());
23
24 let fmt_layer = tracing_subscriber::fmt::layer()
25 .with_ansi(true)
26 .with_target(true)
27 .boxed();
28
29 let mut layers = vec![fmt_layer];
30
31 if telemetry_enabled {
32 let tracer_provider = get_tracer_provider();
33 let meter_provider = init_meter_provider();
34 let tracer = tracer_provider.tracer("main");
35
36 let metrics_layer = MetricsLayer::new(meter_provider).boxed();
37 let telemetry_layer = OpenTelemetryLayer::new(tracer).boxed();
38
39 layers.push(metrics_layer);
40 layers.push(telemetry_layer);
41 }
42
43 let log_file = OpenOptions::new()
44 .write(true)
45 .create(true)
46 .truncate(true)
47 .open(log_path)
48 .expect("failed to open log file");
49
50 let file_layer = tracing_subscriber::fmt::layer()
51 .json()
52 .with_writer(log_file)
53 .boxed();
54
55 layers.push(file_layer);
56
57 let env_filter = tracing_subscriber::EnvFilter::try_from_default_env()
58 .unwrap_or_else(|_| "info,".into())
59 .add_directive("tokio_postgres=info".parse().unwrap())
60 .add_directive("hyper=info".parse().unwrap())
61 .add_directive("axum::rejection=trace".parse().unwrap())
63 .add_directive("hyper_util=info".parse().unwrap());
64
65 tracing_subscriber::registry()
66 .with(layers)
67 .with(env_filter)
68 .init();
69}
70
71pub fn resource() -> Resource {
72 let environment = if cfg!(debug_assertions) {
73 "debug"
74 } else {
75 "release"
76 };
77
78 let svc_name =
79 std::env::var("SERVICE_NAME").unwrap_or_else(|_| env!("CARGO_PKG_NAME").to_string());
80
81 let svc_version =
82 std::env::var("SERVICE_VERSION").unwrap_or_else(|_| env!("CARGO_PKG_VERSION").to_string());
83
84 Resource::builder_empty()
85 .with_detectors(&[
86 Box::new(SdkProvidedResourceDetector),
87 Box::new(EnvResourceDetector::new()),
88 Box::new(TelemetryResourceDetector),
89 ])
90 .with_attributes([
91 KeyValue::new(SERVICE_NAME, svc_name),
92 KeyValue::new(SERVICE_VERSION, svc_version),
93 KeyValue::new("deployment.environment.name", environment),
94 ])
95 .build()
96}
97
98pub fn init_meter_provider() -> SdkMeterProvider {
99 let exporter = opentelemetry_otlp::MetricExporter::builder()
100 .with_http()
101 .with_temporality(opentelemetry_sdk::metrics::Temporality::default())
102 .build()
103 .unwrap();
104
105 let reader = PeriodicReader::builder(exporter)
106 .with_interval(std::time::Duration::from_secs(30))
107 .build();
108
109 let meter_provider = MeterProviderBuilder::default()
110 .with_resource(resource())
111 .with_reader(reader)
112 .build();
113
114 global::set_meter_provider(meter_provider.clone());
115
116 meter_provider
117}
118
119pub fn get_tracer_provider() -> SdkTracerProvider {
120 let exporter = opentelemetry_otlp::SpanExporter::builder()
121 .with_http()
122 .build()
123 .unwrap();
124
125 let tracer_provider = SdkTracerProvider::builder()
126 .with_batch_exporter(exporter)
127 .with_sampler(Sampler::AlwaysOn)
128 .with_id_generator(RandomIdGenerator::default())
129 .with_max_events_per_span(64)
130 .with_max_attributes_per_span(16)
131 .with_resource(resource())
132 .build();
133
134 global::set_tracer_provider(tracer_provider.clone());
135
136 tracing::info!(
137 "OpenTelemetry Tracer Provider initialized: {}",
138 OTEL_EXPORTER_OTLP_ENDPOINT
139 );
140
141 tracer_provider
142}