1use std::sync::atomic::{AtomicU8, Ordering};
10
11pub const LEVEL_TRACE: u8 = 0;
12pub const LEVEL_DEBUG: u8 = 1;
13pub const LEVEL_INFO: u8 = 2;
14pub const LEVEL_WARN: u8 = 3;
15pub const LEVEL_ERROR: u8 = 4;
16pub const LEVEL_OFF: u8 = 5;
17
18const DEFAULT_LEVEL: u8 = LEVEL_WARN;
20const UNINIT: u8 = u8::MAX;
22
23static LOG_LEVEL: AtomicU8 = AtomicU8::new(UNINIT);
24
25fn parse_level(s: &str) -> Option<u8> {
26 match s.trim().to_ascii_lowercase().as_str() {
27 "trace" => Some(LEVEL_TRACE),
28 "debug" => Some(LEVEL_DEBUG),
29 "info" => Some(LEVEL_INFO),
30 "warn" | "warning" => Some(LEVEL_WARN),
31 "error" => Some(LEVEL_ERROR),
32 "off" | "none" | "silent" => Some(LEVEL_OFF),
33 _ => None,
34 }
35}
36
37#[inline]
40pub fn log_level() -> u8 {
41 match LOG_LEVEL.load(Ordering::Relaxed) {
42 UNINIT => {
43 let lvl = std::env::var("VECTOR_LOG")
44 .ok()
45 .and_then(|v| parse_level(&v))
46 .unwrap_or(DEFAULT_LEVEL);
47 LOG_LEVEL.store(lvl, Ordering::Relaxed);
48 lvl
49 }
50 v => v,
51 }
52}
53
54#[inline]
56pub fn level_enabled(level: u8) -> bool {
57 level >= log_level()
58}
59
60pub fn set_log_level(level: u8) {
62 LOG_LEVEL.store(level, Ordering::Relaxed);
63}
64
65pub fn set_log_level_str(s: &str) -> bool {
68 match parse_level(s) {
69 Some(l) => {
70 set_log_level(l);
71 true
72 }
73 None => false,
74 }
75}
76
77type PersistSink = Box<dyn Fn(&str) + Send + Sync>;
84
85static PERSIST_SINK: std::sync::OnceLock<PersistSink> = std::sync::OnceLock::new();
86
87pub fn set_persist_sink(sink: impl Fn(&str) + Send + Sync + 'static) {
89 let _ = PERSIST_SINK.set(Box::new(sink));
90}
91
92pub fn persist(line: &str) {
94 if let Some(sink) = PERSIST_SINK.get() {
95 sink(line);
96 }
97}
98
99pub fn timestamp_utc() -> String {
103 let secs = web_time::SystemTime::now()
104 .duration_since(web_time::UNIX_EPOCH)
105 .map(|d| d.as_secs())
106 .unwrap_or(0);
107 format_unix_utc(secs)
108}
109
110pub fn format_unix_utc(secs: u64) -> String {
112 let days = (secs / 86_400) as i64;
113 let tod = secs % 86_400;
114 let z = days + 719_468;
116 let era = if z >= 0 { z } else { z - 146_096 } / 146_097;
117 let doe = (z - era * 146_097) as u64; let yoe = (doe - doe / 1460 + doe / 36_524 - doe / 146_096) / 365; let y = yoe as i64 + era * 400;
120 let doy = doe - (365 * yoe + yoe / 4 - yoe / 100); let mp = (5 * doy + 2) / 153; let d = doy - (153 * mp + 2) / 5 + 1; let m = if mp < 10 { mp + 3 } else { mp - 9 }; let y = if m <= 2 { y + 1 } else { y };
125 format!(
126 "{:04}-{:02}-{:02} {:02}:{:02}:{:02}Z",
127 y,
128 m,
129 d,
130 tod / 3600,
131 (tod % 3600) / 60,
132 tod % 60
133 )
134}
135
136#[cfg(test)]
137mod ts_tests {
138 use super::format_unix_utc;
139
140 #[test]
141 fn formats_known_instants() {
142 assert_eq!(format_unix_utc(0), "1970-01-01 00:00:00Z");
143 assert_eq!(format_unix_utc(1_000_000_000), "2001-09-09 01:46:40Z");
144 assert_eq!(format_unix_utc(1_709_164_800), "2024-02-29 00:00:00Z");
146 assert_eq!(format_unix_utc(1_735_689_599), "2024-12-31 23:59:59Z");
147 assert_eq!(format_unix_utc(1_767_225_600), "2026-01-01 00:00:00Z");
148 }
149}
150
151#[cfg(target_arch = "wasm32")]
152#[wasm_bindgen::prelude::wasm_bindgen]
153extern "C" {
154 #[wasm_bindgen::prelude::wasm_bindgen(js_namespace = console, js_name = log)]
155 fn console_log(s: &str);
156}
157
158#[cfg(target_arch = "wasm32")]
159#[doc(hidden)]
160pub fn console_line(line: &str) {
161 console_log(line);
162}