statsig_rust/
output_logger.rs1use log::{debug, error, info, warn, Level};
2
3const MAX_CHARS: usize = 400;
4const TRUNCATED_SUFFIX: &str = "...[TRUNCATED]";
5
6const DEFAULT_LOG_LEVEL: LogLevel = LogLevel::Warn;
7
8#[derive(Clone)]
9pub enum LogLevel {
10 None,
11 Debug,
12 Info,
13 Warn,
14 Error,
15}
16
17impl From<&str> for LogLevel {
18 fn from(level: &str) -> Self {
19 match level.to_lowercase().as_str() {
20 "debug" => LogLevel::Debug,
21 "info" => LogLevel::Info,
22 "warn" => LogLevel::Warn,
23 "error" => LogLevel::Error,
24 "none" => LogLevel::None,
25 _ => DEFAULT_LOG_LEVEL,
26 }
27 }
28}
29
30impl From<u32> for LogLevel {
31 fn from(level: u32) -> Self {
32 match level {
33 0 => LogLevel::None,
34 1 => LogLevel::Error,
35 2 => LogLevel::Warn,
36 3 => LogLevel::Info,
37 4 => LogLevel::Debug,
38 _ => DEFAULT_LOG_LEVEL,
39 }
40 }
41}
42
43impl LogLevel {
44 fn to_third_party_level(&self) -> Option<Level> {
45 match self {
46 LogLevel::Debug => Some(Level::Debug),
47 LogLevel::Info => Some(Level::Info),
48 LogLevel::Warn => Some(Level::Warn),
49 LogLevel::Error => Some(Level::Error),
50 LogLevel::None => None,
51 }
52 }
53}
54
55pub fn initialize_simple_output_logger(level: &Option<LogLevel>) {
56 let level = level.as_ref().unwrap_or(&DEFAULT_LOG_LEVEL).clone();
57
58 let final_level = match level {
59 LogLevel::None => {
60 return;
61 }
62 _ => match level.to_third_party_level() {
63 Some(level) => level,
64 None => return,
65 },
66 };
67
68 match simple_logger::init_with_level(final_level) {
69 Ok(()) => {}
70 Err(_) => {
71 log::set_max_level(final_level.to_level_filter());
72 }
73 }
74}
75
76pub fn log_message(tag: &str, level: LogLevel, msg: String) {
77 let truncated_msg = if msg.chars().count() > MAX_CHARS {
78 let visible_chars = MAX_CHARS.saturating_sub(TRUNCATED_SUFFIX.len());
79 format!(
80 "{}{}",
81 msg.chars().take(visible_chars).collect::<String>(),
82 TRUNCATED_SUFFIX
83 )
84 } else {
85 msg
86 };
87
88 if let Some(level) = level.to_third_party_level() {
89 match level {
90 Level::Debug => debug!("[Statsig.{}] {}", tag, truncated_msg),
91 Level::Info => info!("[Statsig.{}] {}", tag, truncated_msg),
92 Level::Warn => warn!("[Statsig.{}] {}", tag, truncated_msg),
93 Level::Error => error!("[Statsig.{}] {}", tag, truncated_msg),
94 _ => {}
95 };
96 }
97}
98
99#[macro_export]
100macro_rules! log_d {
101 ($tag:expr, $($arg:tt)*) => {
102 $crate::output_logger::log_message($tag, $crate::output_logger::LogLevel::Debug, format!($($arg)*))
103 }
104}
105
106#[macro_export]
107macro_rules! log_i {
108 ($tag:expr, $($arg:tt)*) => {
109 $crate::output_logger::log_message($tag, $crate::output_logger::LogLevel::Info, format!($($arg)*))
110 }
111}
112
113#[macro_export]
114macro_rules! log_w {
115 ($tag:expr, $($arg:tt)*) => {
116 $crate::output_logger::log_message($tag, $crate::output_logger::LogLevel::Warn, format!($($arg)*))
117 }
118}
119
120#[macro_export]
121macro_rules! log_e {
122 ($tag:expr, $($arg:tt)*) => {
123 $crate::output_logger::log_message($tag, $crate::output_logger::LogLevel::Error, format!($($arg)*))
124 }
125}
126
127#[macro_export]
128macro_rules! log_error_to_statsig_and_console {
129 ($ops_stats:expr, $tag:expr, $($arg:tt)*) => {
130 let err_message = format!($($arg)*);
131 let event = ErrorBoundaryEvent {
132 exception: err_message.clone(),
133 tag: $tag.to_string(),
134 extra: None,
135 };
136 $ops_stats.log_error(event);
137
138 $crate::output_logger::log_message(&$tag, $crate::output_logger::LogLevel::Error, err_message)
139 }
140}