1pub mod export;
26pub mod health;
27pub mod log;
28pub mod metrics;
29pub mod trace;
30
31pub use log::format::{JsonFormatter, PrettyFormatter};
34pub use log::subscriber::{
35 set_global_subscriber, set_min_level, LogFormat, LogSubscriber, Subscriber,
36};
37pub use log::{Event, Level, Value};
38
39pub use trace::context::SpanContext;
42pub use trace::span::{Span, SpanGuard};
43pub use trace::{SpanId, TraceId};
44
45pub use metrics::counter::Counter;
48pub use metrics::gauge::Gauge;
49pub use metrics::histogram::Histogram;
50pub use metrics::registry::MetricsRegistry;
51
52pub use health::{AsyncHealthCheck, HealthCheck, HealthRegistry, HealthStatus};
55
56pub use export::prometheus::PrometheusExporter;
59pub use export::stdout::StdoutExporter;
60pub use export::Exporter;
61
62pub mod prelude {
65 pub use crate::{
66 Counter, Event, Gauge, HealthCheck, HealthRegistry, HealthStatus, Histogram, Level,
67 MetricsRegistry, Span, SpanContext, SpanGuard, SpanId, Subscriber, TraceId, Value,
68 };
69}
70
71pub fn init_logging() {
87 let sub = LogSubscriber::from_env();
88 set_min_level(sub.min_level);
91 let _ = set_global_subscriber(sub);
93}
94
95#[macro_export]
102macro_rules! log_event {
103 ($level:expr, $msg:expr $(, $key:ident = $val:expr)* $(,)?) => {{
104 if ($level as u8) >= $crate::log::subscriber::min_level() as u8 {
106 let event = $crate::Event::now($level, $msg)
107 $(.field(stringify!($key), $val))*;
108 $crate::log::subscriber::dispatch(&event);
109 }
110 }};
111}
112
113#[macro_export]
115macro_rules! trace {
116 ($msg:expr $(, $key:ident = $val:expr)* $(,)?) => {
117 $crate::log_event!($crate::Level::Trace, $msg $(, $key = $val)*)
118 };
119}
120
121#[macro_export]
123macro_rules! debug {
124 ($msg:expr $(, $key:ident = $val:expr)* $(,)?) => {
125 $crate::log_event!($crate::Level::Debug, $msg $(, $key = $val)*)
126 };
127}
128
129#[macro_export]
131macro_rules! info {
132 ($msg:expr $(, $key:ident = $val:expr)* $(,)?) => {
133 $crate::log_event!($crate::Level::Info, $msg $(, $key = $val)*)
134 };
135}
136
137#[macro_export]
139macro_rules! warn {
140 ($msg:expr $(, $key:ident = $val:expr)* $(,)?) => {
141 $crate::log_event!($crate::Level::Warn, $msg $(, $key = $val)*)
142 };
143}
144
145#[macro_export]
147macro_rules! error {
148 ($msg:expr $(, $key:ident = $val:expr)* $(,)?) => {
149 $crate::log_event!($crate::Level::Error, $msg $(, $key = $val)*)
150 };
151}
152
153#[macro_export]
155macro_rules! trace_event {
156 ($msg:expr $(, $key:ident = $val:expr)* $(,)?) => {
157 $crate::log_event!($crate::Level::Trace, $msg $(, $key = $val)*)
158 };
159}
160
161#[cfg(test)]
164mod tests {
165 use crate::log::subscriber::{min_level, set_min_level, LogFormat, LogSubscriber};
166 use crate::log::{Event, Level};
167
168 fn with_level<F: FnOnce()>(level: Level, f: F) {
171 let prev = min_level();
172 set_min_level(level);
173 f();
174 set_min_level(prev);
175 }
176
177 #[test]
178 fn log_event_macro_passes_at_or_above_min_level() {
179 with_level(Level::Debug, || {
182 crate::log_event!(Level::Debug, "debug msg");
183 crate::log_event!(Level::Info, "info msg");
184 });
185 }
186
187 #[test]
188 fn trace_macro_compiles() {
189 with_level(Level::Trace, || {
190 crate::trace!("trace message");
191 crate::trace!("trace with field", key = "value");
192 });
193 }
194
195 #[test]
196 fn debug_macro_compiles() {
197 with_level(Level::Debug, || {
198 crate::debug!("debug message");
199 crate::debug!("debug with field", count = 42_i32);
200 });
201 }
202
203 #[test]
204 fn info_macro_compiles() {
205 with_level(Level::Info, || {
206 crate::info!("info message");
207 crate::info!("info with fields", status = 200_i32, path = "/health");
208 });
209 }
210
211 #[test]
212 fn warn_macro_compiles() {
213 with_level(Level::Warn, || {
214 crate::warn!("warn message");
215 });
216 }
217
218 #[test]
219 fn error_macro_compiles() {
220 with_level(Level::Error, || {
221 crate::error!("error message");
222 crate::error!("error with field", code = 500_i32);
223 });
224 }
225
226 #[test]
227 fn trace_event_alias_compiles() {
228 with_level(Level::Trace, || {
229 crate::trace_event!("trace alias");
230 });
231 }
232
233 #[test]
234 fn log_event_macro_filtered_when_below_min() {
235 with_level(Level::Error, || {
237 crate::log_event!(Level::Debug, "should be filtered");
239 crate::log_event!(Level::Info, "should be filtered");
240 crate::log_event!(Level::Warn, "should be filtered");
241 crate::log_event!(Level::Error, "should pass filter");
243 });
244 }
245
246 #[test]
247 fn log_subscriber_new_fields_accessible() {
248 let sub = LogSubscriber::new(Level::Debug, LogFormat::Json);
249 assert_eq!(sub.min_level, Level::Debug);
250 assert_eq!(sub.format, LogFormat::Json);
251 }
252
253 #[test]
254 fn init_logging_is_idempotent() {
255 crate::init_logging();
258 crate::init_logging();
259 }
260
261 #[test]
262 fn json_format_output_is_valid_structure() {
263 let event = Event::now(Level::Info, "test")
264 .field("key", "val")
265 .field("n", 42_i32);
266 let mut buf = Vec::new();
267 crate::JsonFormatter::format(&event, &mut buf).unwrap();
268 let s = String::from_utf8(buf).unwrap();
269 assert!(s.starts_with('{'));
271 assert!(s.trim_end().ends_with('}'));
272 assert!(s.contains("\"level\":\"INFO\""));
273 assert!(s.contains("\"msg\":\"test\""));
274 assert!(s.contains("\"key\":\"val\""));
275 assert!(s.contains("\"n\":42"));
276 }
277
278 #[test]
279 fn set_min_level_affects_macro_gate() {
280 with_level(Level::Error, || {
282 assert_eq!(min_level(), Level::Error);
283 assert!((Level::Trace as u8) < (min_level() as u8));
285 });
286 }
287}