1use opentelemetry::KeyValue;
2use opentelemetry_sdk::trace::{self, Sampler};
3use opentelemetry_sdk::Resource;
4use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, EnvFilter, Registry};
5use ferrox_errors::AppError;
6
7pub struct LoggerConfig {
8 pub service_name: String,
9 pub otlp_endpoint: Option<String>,
10 pub sentry_dsn: Option<String>,
11}
12
13pub fn setup_logger(config: LoggerConfig) -> Result<Option<sentry::ClientInitGuard>, AppError> {
14 let env_filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info"));
15
16 let formatting_layer = tracing_subscriber::fmt::layer()
18 .json()
19 .with_file(true)
20 .with_line_number(true)
21 .with_target(false); let subscriber = Registry::default().with(env_filter).with(formatting_layer);
24
25 let mut sentry_guard = None;
27 if let Some(dsn) = config.sentry_dsn {
28 sentry_guard = Some(sentry::init((
29 dsn,
30 sentry::ClientOptions {
31 release: sentry::release_name!(),
32 traces_sample_rate: 1.0,
33 ..Default::default()
34 },
35 )));
36
37 let sentry_layer = sentry_tracing::layer();
38
39 if let Some(endpoint) = config.otlp_endpoint {
40 let tracer = opentelemetry_otlp::new_pipeline()
41 .tracing()
42 .with_exporter(
43 opentelemetry_otlp::new_exporter()
44 .tonic()
45 .with_endpoint(endpoint),
46 )
47 .with_trace_config(
48 trace::config()
49 .with_sampler(Sampler::AlwaysOn)
50 .with_resource(Resource::new(vec![KeyValue::new("service.name", config.service_name)])),
51 )
52 .install_batch(opentelemetry_sdk::runtime::Tokio)
53 .map_err(|e| AppError::InternalServerError(Box::new(e)))?;
54
55 let telemetry_layer = tracing_opentelemetry::layer().with_tracer(tracer);
56
57 subscriber.with(telemetry_layer).with(sentry_layer).init();
58 } else {
59 subscriber.with(sentry_layer).init();
60 }
61 } else {
62 if let Some(endpoint) = config.otlp_endpoint {
63 let tracer = opentelemetry_otlp::new_pipeline()
64 .tracing()
65 .with_exporter(
66 opentelemetry_otlp::new_exporter()
67 .tonic()
68 .with_endpoint(endpoint),
69 )
70 .with_trace_config(
71 trace::config()
72 .with_sampler(Sampler::AlwaysOn)
73 .with_resource(Resource::new(vec![KeyValue::new("service.name", config.service_name)])),
74 )
75 .install_batch(opentelemetry_sdk::runtime::Tokio)
76 .map_err(|e| AppError::InternalServerError(Box::new(e)))?;
77
78 let telemetry_layer = tracing_opentelemetry::layer().with_tracer(tracer);
79
80 subscriber.with(telemetry_layer).init();
81 } else {
82 subscriber.init();
83 }
84 }
85
86 tracing::info!("ferrox-logger initialized: Structured JSON logging enabled.");
87 Ok(sentry_guard)
88}