lambda_otel_lite/
logger.rs1use std::env;
26use std::sync::OnceLock;
27
28const DEFAULT_LOG_LEVEL: &str = "info";
30
31static LOG_LEVEL: OnceLock<&'static str> = OnceLock::new();
33
34fn get_log_level() -> &'static str {
36 LOG_LEVEL.get_or_init(|| {
37 let level = env::var("AWS_LAMBDA_LOG_LEVEL")
38 .or_else(|_| env::var("LOG_LEVEL"))
39 .unwrap_or_else(|_| "info".to_string())
40 .to_lowercase();
41
42 match level.as_str() {
43 "none" | "error" | "warn" | "info" | "debug" => Box::leak(level.into_boxed_str()),
44 _ => "info",
45 }
46 })
47}
48
49#[derive(Clone)]
51pub struct Logger {
52 prefix: &'static str,
53 log_level_fn: fn() -> &'static str,
54}
55
56impl Logger {
57 pub fn new(prefix: impl Into<String>) -> Self {
59 let static_prefix = Box::leak(prefix.into().into_boxed_str());
61
62 Self {
63 prefix: static_prefix,
64 log_level_fn: get_log_level,
65 }
66 }
67
68 pub const fn const_new(prefix: &'static str) -> Self {
70 Self {
71 prefix,
72 log_level_fn: || DEFAULT_LOG_LEVEL,
73 }
74 }
75
76 fn log_level(&self) -> &'static str {
78 (self.log_level_fn)()
79 }
80
81 fn should_log(&self, level: &str) -> bool {
82 match self.log_level() {
83 "none" => false,
84 "error" => level == "error",
85 "warn" => matches!(level, "error" | "warn"),
86 "info" => matches!(level, "error" | "warn" | "info"),
87 "debug" => matches!(level, "error" | "warn" | "info" | "debug"),
88 _ => matches!(level, "error" | "warn" | "info"),
89 }
90 }
91
92 fn format_message(&self, message: &str) -> String {
93 format!("[{}] {}", self.prefix, message)
94 }
95
96 pub fn debug(&self, message: impl AsRef<str>) {
98 if self.should_log("debug") {
99 println!("{}", self.format_message(message.as_ref()));
100 }
101 }
102
103 pub fn info(&self, message: impl AsRef<str>) {
105 if self.should_log("info") {
106 println!("{}", self.format_message(message.as_ref()));
107 }
108 }
109
110 pub fn warn(&self, message: impl AsRef<str>) {
112 if self.should_log("warn") {
113 eprintln!("{}", self.format_message(message.as_ref()));
114 }
115 }
116
117 pub fn error(&self, message: impl AsRef<str>) {
119 if self.should_log("error") {
120 eprintln!("{}", self.format_message(message.as_ref()));
121 }
122 }
123}
124
125#[cfg(test)]
126mod tests {
127 use super::*;
128 use serial_test::serial;
129
130 #[test]
131 #[serial]
132 fn test_log_levels() {
133 let logger = Logger::new("test");
134
135 assert!(logger.should_log("error"));
136 assert!(logger.should_log("warn"));
137 assert!(logger.should_log("info"));
138 assert!(!logger.should_log("invalid"));
139 }
140
141 #[test]
142 #[serial]
143 fn test_format_message() {
144 let logger = Logger::new("test");
145
146 assert_eq!(logger.format_message("hello"), "[test] hello");
147 }
148}