ceylon_observability/
logging.rs1use std::fmt::Debug;
2use std::path::PathBuf;
3use tracing::info;
4use tracing_subscriber::{fmt, prelude::*, EnvFilter};
5use uuid::Uuid;
6
7#[derive(Debug, Clone)]
9pub struct LoggingConfig {
10 pub log_level: String,
11 pub log_file_path: Option<PathBuf>,
12 pub json_output: bool,
13}
14
15impl Default for LoggingConfig {
16 fn default() -> Self {
17 Self {
18 log_level: "info".to_string(),
19 log_file_path: None,
20 json_output: false,
21 }
22 }
23}
24
25pub struct LoggingGuards {
27 pub _file_guard: Option<tracing_appender::non_blocking::WorkerGuard>,
28}
29
30pub fn init_logging(config: &LoggingConfig) -> LoggingGuards {
32 let env_filter =
33 EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new(&config.log_level));
34
35 let registry = tracing_subscriber::registry().with(env_filter);
36
37 let mut file_guard = None;
38 let file_writer = if let Some(path) = &config.log_file_path {
39 let file_appender = tracing_appender::rolling::daily(
40 path.parent().unwrap_or(&PathBuf::from(".")),
41 path.file_name().unwrap_or("ceylon.log".as_ref()),
42 );
43 let (non_blocking, guard) = tracing_appender::non_blocking(file_appender);
44 file_guard = Some(guard);
45 Some(non_blocking)
46 } else {
47 None
48 };
49
50 let fmt_layer = fmt::layer()
51 .with_target(true)
52 .with_thread_ids(true)
53 .with_level(true);
54
55 if config.json_output {
56 let stdout_layer = fmt_layer.json();
57 if let Some(writer) = file_writer {
58 let file_layer = fmt::layer().with_ansi(false).with_writer(writer).json();
59 if let Err(e) = registry.with(stdout_layer).with(file_layer).try_init() {
60 eprintln!("Global logging already initialized: {}", e);
61 }
62 } else {
63 if let Err(e) = registry.with(stdout_layer).try_init() {
64 eprintln!("Global logging already initialized: {}", e);
65 }
66 }
67 } else {
68 let stdout_layer = fmt_layer.compact();
69 if let Some(writer) = file_writer {
70 let file_layer = fmt::layer().with_ansi(false).with_writer(writer).json();
71 if let Err(e) = registry.with(stdout_layer).with(file_layer).try_init() {
72 eprintln!("Global logging already initialized: {}", e);
73 }
74 } else {
75 if let Err(e) = registry.with(stdout_layer).try_init() {
76 eprintln!("Global logging already initialized: {}", e);
77 }
78 }
79 }
80
81 info!("Logging initialized");
82
83 LoggingGuards {
84 _file_guard: file_guard,
85 }
86}
87
88#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
90pub struct CorrelationId(String);
91
92impl CorrelationId {
93 pub fn new() -> Self {
94 Self(Uuid::new_v4().to_string())
95 }
96
97 pub fn as_str(&self) -> &str {
98 &self.0
99 }
100}
101
102impl Default for CorrelationId {
103 fn default() -> Self {
104 Self::new()
105 }
106}
107
108impl std::fmt::Display for CorrelationId {
109 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
110 write!(f, "{}", self.0)
111 }
112}