1use chrono::{DateTime, Local};
3use colored::{Color, Colorize};
4use std::ffi::CString;
5use std::fmt::Write;
6use std::fmt;
7use janus_plugin_sys as ffi;
8pub use ffi::janus_log_level as JANUS_LOG_LEVEL;
9
10#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
11pub enum LogLevel {
13 Fatal = 1,
14 Err = 2,
15 Warn = 3,
16 Info = 4,
17 Verb = 5,
18 Huge = 6,
19 Dbg = 7,
20}
21
22impl LogLevel {
23 fn color(self) -> Option<Color> {
25 match self {
26 LogLevel::Fatal => Some(Color::Magenta),
27 LogLevel::Err => Some(Color::Red),
28 LogLevel::Warn => Some(Color::Yellow),
29 _ => None,
30 }
31 }
32}
33
34#[derive(Debug, Clone)]
35pub struct LogParameters {
36 pub log_timestamps: bool,
37 pub log_colors: bool,
38 pub clock: fn() -> DateTime<Local>,
39}
40
41impl Default for LogParameters {
42 fn default() -> Self {
43 unsafe {
44 Self {
45 log_timestamps: ffi::janus_log_timestamps == 1,
46 log_colors: ffi::janus_log_colors == 1,
47 clock: Local::now,
48 }
49 }
50 }
51}
52
53pub fn log(level: LogLevel, message: fmt::Arguments, params: LogParameters) {
56 unsafe {
57 let output = CString::new(print_log(level, message, params)).expect("Null character in log message :(");
58 ffi::janus_vprintf(output.as_ptr())
59 }
60}
61
62pub fn print_log(level: LogLevel, message: fmt::Arguments, params: LogParameters) -> String {
65 let mut output = String::with_capacity(150); if params.log_timestamps {
67 write!(output, "{} ", (params.clock)().format("[%a %b %e %T %Y]")).unwrap();
68 }
69 if level <= LogLevel::Warn {
70 let prefix = format!("[{:?}] ", level).to_uppercase();
71 let prefix = match level.color() {
72 Some(c) if params.log_colors => format!("{}", prefix.color(c)),
73 _ => prefix,
74 };
75 write!(output, "{}", prefix).unwrap();
76 }
77 output.write_fmt(message).expect("Error constructing log message!");
78 output.push('\n');
79 output
80}
81
82#[macro_export]
83macro_rules! janus_log_enabled {
84 ($lvl:expr) => (($lvl as i32) <= unsafe { $crate::debug::JANUS_LOG_LEVEL })
85}
86
87#[macro_export]
88macro_rules! janus_log {
89 ($lvl:expr, $($arg:tt)+) => ({
90 let lvl = $lvl;
91 if $crate::janus_log_enabled!(lvl) {
92 $crate::debug::log(lvl, format_args!($($arg)+), $crate::debug::LogParameters::default())
93 }
94 })
95}
96
97#[macro_export]
98macro_rules! janus_fatal {
99 ($($arg:tt)+) => ($crate::janus_log!($crate::debug::LogLevel::Fatal, $($arg)+))
100}
101
102#[macro_export]
103macro_rules! janus_err {
104 ($($arg:tt)+) => ($crate::janus_log!($crate::debug::LogLevel::Err, $($arg)+))
105}
106
107#[macro_export]
108macro_rules! janus_warn {
109 ($($arg:tt)+) => ($crate::janus_log!($crate::debug::LogLevel::Warn, $($arg)+))
110}
111
112#[macro_export]
113macro_rules! janus_info {
114 ($($arg:tt)+) => ($crate::janus_log!($crate::debug::LogLevel::Info, $($arg)+))
115}
116
117#[macro_export]
118macro_rules! janus_verb {
119 ($($arg:tt)+) => ($crate::janus_log!($crate::debug::LogLevel::Verb, $($arg)+))
120}
121
122#[macro_export]
123macro_rules! janus_huge {
124 ($($arg:tt)+) => ($crate::janus_log!($crate::debug::LogLevel::Huge, $($arg)+))
125}
126
127#[macro_export]
128macro_rules! janus_dbg {
129 ($($arg:tt)+) => ($crate::janus_log!($crate::debug::LogLevel::Dbg, $($arg)+))
130}
131
132#[cfg(test)]
133mod tests {
134
135 use super::*;
136 use chrono::TimeZone;
137
138 fn fixed_clock(year: i32, month: u32, day: u32, hour: u32, min: u32, sec: u32) -> DateTime<Local> {
139 Local.ymd(year, month, day).and_hms(hour, min, sec)
140 }
141
142 #[test]
143 fn log_format_correctness() {
144 assert_eq!(
145 "[Tue Oct 10 01:37:46 2017] [WARN] Test message.\n",
146 print_log(
147 LogLevel::Warn,
148 format_args!("{}", "Test message."),
149 LogParameters {
150 log_timestamps: true,
151 ..default_log_parameters()
152 }
153 )
154 )
155 }
156
157 #[test]
158 fn log_colored_output() {
159 assert_eq!(
160 "\u{1b}[35m[FATAL] \u{1b}[0mCrash!\n",
161 print_log(
162 LogLevel::Fatal,
163 format_args!("{}", "Crash!"),
164 LogParameters {
165 log_colors: true,
166 ..default_log_parameters()
167 }
168 )
169 );
170
171 assert_eq!(
172 "\u{1b}[31m[ERR] \u{1b}[0mAn error occurred!\n",
173 print_log(
174 LogLevel::Err,
175 format_args!("{}", "An error occurred!"),
176 LogParameters {
177 log_colors: true,
178 ..default_log_parameters()
179 }
180 )
181 );
182
183 assert_eq!(
184 "\u{1b}[33m[WARN] \u{1b}[0mAttention!\n",
185 print_log(
186 LogLevel::Warn,
187 format_args!("{}", "Attention!"),
188 LogParameters {
189 log_colors: true,
190 ..default_log_parameters()
191 }
192 )
193 );
194
195 assert_eq!(
196 "Just a message.\n",
197 print_log(
198 LogLevel::Info,
199 format_args!("{}", "Just a message."),
200 LogParameters {
201 log_colors: true,
202 ..default_log_parameters()
203 }
204 )
205 );
206 }
207
208 fn default_log_parameters() -> LogParameters {
209 LogParameters {
210 log_timestamps: false,
211 log_colors: false,
212 clock: || fixed_clock(2017, 10, 10, 1, 37, 46),
213 }
214 }
215}