rust_template_foundation/
logging.rs1use serde::{Deserialize, Serialize};
8use std::str::FromStr;
9use thiserror::Error;
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
13#[serde(rename_all = "lowercase")]
14pub enum LogLevel {
15 Trace,
16 Debug,
17 Info,
18 Warn,
19 Error,
20}
21
22impl FromStr for LogLevel {
23 type Err = LogLevelParseError;
24
25 fn from_str(s: &str) -> Result<Self, Self::Err> {
26 match s.to_lowercase().as_str() {
27 "trace" => Ok(LogLevel::Trace),
28 "debug" => Ok(LogLevel::Debug),
29 "info" => Ok(LogLevel::Info),
30 "warn" | "warning" => Ok(LogLevel::Warn),
31 "error" => Ok(LogLevel::Error),
32 _ => Err(LogLevelParseError::InvalidLevel(s.to_string())),
33 }
34 }
35}
36
37impl From<LogLevel> for tracing::Level {
38 fn from(level: LogLevel) -> Self {
39 match level {
40 LogLevel::Trace => tracing::Level::TRACE,
41 LogLevel::Debug => tracing::Level::DEBUG,
42 LogLevel::Info => tracing::Level::INFO,
43 LogLevel::Warn => tracing::Level::WARN,
44 LogLevel::Error => tracing::Level::ERROR,
45 }
46 }
47}
48
49impl std::fmt::Display for LogLevel {
50 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
51 match self {
52 LogLevel::Trace => write!(f, "trace"),
53 LogLevel::Debug => write!(f, "debug"),
54 LogLevel::Info => write!(f, "info"),
55 LogLevel::Warn => write!(f, "warn"),
56 LogLevel::Error => write!(f, "error"),
57 }
58 }
59}
60
61#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
63#[serde(rename_all = "lowercase")]
64pub enum LogFormat {
65 Text,
66 Json,
67}
68
69impl FromStr for LogFormat {
70 type Err = LogFormatParseError;
71
72 fn from_str(s: &str) -> Result<Self, Self::Err> {
73 match s.to_lowercase().as_str() {
74 "text" | "pretty" => Ok(LogFormat::Text),
75 "json" => Ok(LogFormat::Json),
76 _ => Err(LogFormatParseError::InvalidFormat(s.to_string())),
77 }
78 }
79}
80
81impl std::fmt::Display for LogFormat {
82 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
83 match self {
84 LogFormat::Text => write!(f, "text"),
85 LogFormat::Json => write!(f, "json"),
86 }
87 }
88}
89
90#[derive(Debug, Error)]
91pub enum LogLevelParseError {
92 #[error(
93 "Invalid log level: {0}. Valid values are: trace, debug, info, warn, error"
94 )]
95 InvalidLevel(String),
96}
97
98#[derive(Debug, Error)]
99pub enum LogFormatParseError {
100 #[error("Invalid log format: {0}. Valid values are: text, json")]
101 InvalidFormat(String),
102}
103
104#[cfg(feature = "cli")]
107pub fn init_cli_logging(level: LogLevel, format: LogFormat) {
108 use tracing_subscriber::{
109 fmt, layer::SubscriberExt, util::SubscriberInitExt, EnvFilter, Layer,
110 };
111
112 let env_filter = EnvFilter::try_from_default_env()
113 .unwrap_or_else(|_| EnvFilter::new(level.to_string()));
114
115 match format {
116 LogFormat::Text => {
117 tracing_subscriber::registry()
118 .with(
119 fmt::layer()
120 .with_writer(std::io::stderr)
121 .with_target(true)
122 .with_line_number(true)
123 .with_filter(env_filter),
124 )
125 .init();
126 }
127 LogFormat::Json => {
128 tracing_subscriber::registry()
129 .with(
130 fmt::layer()
131 .json()
132 .with_writer(std::io::stderr)
133 .with_target(true)
134 .with_line_number(true)
135 .with_filter(env_filter),
136 )
137 .init();
138 }
139 }
140}
141
142#[cfg(feature = "server")]
145pub fn init_server_logging(level: LogLevel, format: LogFormat) {
146 use tracing_subscriber::{
147 fmt, layer::SubscriberExt, util::SubscriberInitExt, EnvFilter, Layer,
148 };
149
150 let env_filter = EnvFilter::try_from_default_env()
151 .unwrap_or_else(|_| EnvFilter::new(level.to_string()));
152
153 #[cfg(unix)]
154 if let Ok(journald) = tracing_journald::layer() {
155 tracing_subscriber::registry()
156 .with(tracing_subscriber::Layer::with_filter(journald, env_filter))
157 .init();
158 return;
159 }
160
161 match format {
162 LogFormat::Text => {
163 tracing_subscriber::registry()
164 .with(
165 fmt::layer()
166 .with_writer(std::io::stderr)
167 .with_target(true)
168 .with_line_number(true)
169 .with_filter(env_filter),
170 )
171 .init();
172 }
173 LogFormat::Json => {
174 tracing_subscriber::registry()
175 .with(
176 fmt::layer()
177 .json()
178 .with_writer(std::io::stderr)
179 .with_target(true)
180 .with_line_number(true)
181 .with_filter(env_filter),
182 )
183 .init();
184 }
185 }
186}