indexbus_platform_obs/
tracing_init.rs1#[cfg(feature = "tracing")]
11use std::{
12 io,
13 sync::{
14 atomic::{AtomicBool, Ordering},
15 Arc,
16 },
17};
18
19#[cfg(feature = "tracing")]
20use tracing_subscriber::{fmt, layer::SubscriberExt, util::SubscriberInitExt, EnvFilter};
21
22#[cfg(feature = "tracing")]
23use crate::errors::{Error, Result};
24
25#[cfg(feature = "tracing")]
27#[derive(Clone, Debug)]
28pub struct TracingConfig {
29 pub default_filter: String,
31 pub format: TracingFormat,
33 pub with_target: bool,
35 pub with_thread_ids: bool,
37 pub with_thread_names: bool,
39}
40
41#[cfg(feature = "tracing")]
43#[derive(Clone, Copy, Debug, Default)]
44pub enum TracingFormat {
45 #[default]
47 Pretty,
48
49 #[cfg(feature = "tracing-json")]
53 Json,
54}
55
56#[cfg(feature = "tracing")]
57impl Default for TracingConfig {
58 fn default() -> Self {
59 Self {
60 default_filter: "info".to_string(),
61 format: TracingFormat::Pretty,
62 with_target: true,
63 with_thread_ids: false,
64 with_thread_names: true,
65 }
66 }
67}
68
69#[cfg(feature = "tracing")]
73#[derive(Clone, Debug)]
74pub struct TracingHandle {
75 shutdown: Arc<AtomicBool>,
76}
77
78#[cfg(feature = "tracing")]
79impl TracingHandle {
80 pub fn request_shutdown(&self) {
82 self.shutdown.store(true, Ordering::Relaxed);
83 }
84
85 pub fn is_shutdown_requested(&self) -> bool {
87 self.shutdown.load(Ordering::Relaxed)
88 }
89}
90
91#[cfg(feature = "tracing")]
99pub fn init_tracing(config: &TracingConfig) -> Result<TracingHandle> {
100 let env_filter = EnvFilter::try_from_default_env()
101 .or_else(|_| EnvFilter::try_new(&config.default_filter))
102 .map_err(|e| Error::TracingInit(e.to_string()))?;
103
104 let base_layer = fmt::layer()
105 .with_writer(io::stderr)
106 .with_target(config.with_target)
107 .with_thread_ids(config.with_thread_ids)
108 .with_thread_names(config.with_thread_names);
109
110 #[cfg(feature = "tracing-json")]
111 {
112 match config.format {
113 TracingFormat::Pretty => tracing_subscriber::registry()
114 .with(env_filter)
115 .with(base_layer)
116 .try_init()
117 .map_err(|e| Error::TracingInit(e.to_string()))?,
118 TracingFormat::Json => tracing_subscriber::registry()
119 .with(env_filter)
120 .with(base_layer.json())
121 .try_init()
122 .map_err(|e| Error::TracingInit(e.to_string()))?,
123 }
124 }
125
126 #[cfg(not(feature = "tracing-json"))]
127 {
128 tracing_subscriber::registry()
129 .with(env_filter)
130 .with(base_layer)
131 .try_init()
132 .map_err(|e| Error::TracingInit(e.to_string()))?;
133 }
134
135 Ok(TracingHandle {
136 shutdown: Arc::new(AtomicBool::new(false)),
137 })
138}
139
140#[cfg(all(test, feature = "tracing"))]
141mod tests {
142 use super::*;
143
144 #[test]
145 fn tracing_handle_shutdown_flag_roundtrips() {
146 let h = TracingHandle {
147 shutdown: Arc::new(AtomicBool::new(false)),
148 };
149
150 assert!(!h.is_shutdown_requested());
151 h.request_shutdown();
152 assert!(h.is_shutdown_requested());
153 }
154
155 #[test]
156 fn init_tracing_fails_if_called_twice() {
157 let cfg = TracingConfig::default();
158 let _ = init_tracing(&cfg).expect("first init");
159
160 let second = init_tracing(&cfg);
161 assert!(matches!(second, Err(Error::TracingInit(_))));
162 }
163}