1use std::fmt;
4use std::str::FromStr;
5use std::sync::atomic::{AtomicBool, Ordering};
6
7use serde::{de, Serialize, Serializer, Deserialize, Deserializer};
8use yansi::{Paint, Painted, Condition};
9
10pub use log as private;
12
13macro_rules! define_log_macro {
15 ($name:ident: $kind:ident, $target:expr, $d:tt) => (
16 #[doc(hidden)]
17 #[macro_export]
18 macro_rules! $name {
19 ($d ($t:tt)*) => ($crate::log::private::$kind!(target: $target, $d ($t)*))
20 }
21 );
22 ($name:ident ($indented:ident): $kind:ident, $target:expr, $d:tt) => (
23 define_log_macro!($name: $kind, $target, $d);
24 define_log_macro!($indented: $kind, concat!($target, "::_"), $d);
25 );
26 ($kind:ident, $indented:ident) => (
27 define_log_macro!($kind: $kind, module_path!(), $);
28 define_log_macro!($indented: $kind, concat!(module_path!(), "::_"), $);
29
30 pub use $indented;
31 );
32}
33
34define_log_macro!(error, error_);
35define_log_macro!(warn, warn_);
36define_log_macro!(info, info_);
37define_log_macro!(debug, debug_);
38define_log_macro!(trace, trace_);
39define_log_macro!(launch_meta (launch_meta_): info, "rocket::launch", $);
40define_log_macro!(launch_info (launch_msg_): warn, "rocket::launch", $);
41
42#[cfg(not(any(debug_assertions, test, doctest)))]
55macro_rules! write_out {
56 ($($arg:tt)*) => ({
57 use std::io::{Write, stdout, stderr};
58 let _ = write!(stdout(), $($arg)*).or_else(|e| write!(stderr(), "{}", e));
59 })
60}
61
62#[cfg(any(debug_assertions, test, doctest))]
63macro_rules! write_out {
64 ($($arg:tt)*) => (print!($($arg)*))
65}
66
67#[derive(Debug)]
68struct RocketLogger;
69
70#[derive(PartialEq, Eq, Debug, Clone, Copy)]
72pub enum LogLevel {
73 Critical,
75 Normal,
77 Debug,
79 Off,
81}
82
83pub trait PaintExt: Sized {
84 fn emoji(self) -> Painted<Self>;
85}
86
87fn is_launch_record(record: &log::Metadata<'_>) -> bool {
89 record.target().contains("rocket::launch")
90}
91
92impl log::Log for RocketLogger {
93 #[inline(always)]
94 fn enabled(&self, record: &log::Metadata<'_>) -> bool {
95 match log::max_level().to_level() {
96 Some(max) => record.level() <= max || is_launch_record(record),
97 None => false
98 }
99 }
100
101 fn log(&self, record: &log::Record<'_>) {
102 if !self.enabled(record.metadata()) {
104 return;
105 }
106
107 let max = log::max_level();
109 let from = |path| record.module_path().map_or(false, |m| m.starts_with(path));
110 let debug_only = from("hyper") || from("rustls") || from("r2d2");
111 if log::LevelFilter::from(LogLevel::Debug) > max && debug_only {
112 return;
113 }
114
115 let indented = record.target().ends_with('_');
117 if indented {
118 write_out!(" {} ", ">>".bold());
119 }
120
121 let level = is_launch_record(record.metadata())
123 .then(|| log::Level::Info)
124 .unwrap_or_else(|| record.level());
125
126 match level {
127 log::Level::Error if !indented => {
128 write_out!("{} {}\n", "Error:".red().bold(), record.args().red().wrap());
129 }
130 log::Level::Warn if !indented => {
131 write_out!("{} {}\n", "Warning:".yellow().bold(), record.args().yellow().wrap());
132 }
133 log::Level::Info => write_out!("{}\n", record.args().blue().wrap()),
134 log::Level::Trace => write_out!("{}\n", record.args().magenta().wrap()),
135 log::Level::Warn => write_out!("{}\n", record.args().yellow().wrap()),
136 log::Level::Error => write_out!("{}\n", &record.args().red().wrap()),
137 log::Level::Debug => {
138 write_out!("\n{} ", "-->".blue().bold());
139 if let Some(file) = record.file() {
140 write_out!("{}", file.blue());
141 }
142
143 if let Some(line) = record.line() {
144 write_out!(":{}\n", line.blue());
145 }
146
147 write_out!("\t{}\n", record.args());
148 }
149 }
150 }
151
152 fn flush(&self) {
153 }
155}
156
157pub(crate) fn init_default() {
158 crate::log::init(&crate::Config::debug_default())
159}
160
161pub(crate) fn init(config: &crate::Config) {
162 static ROCKET_LOGGER_SET: AtomicBool = AtomicBool::new(false);
163
164 if log::set_boxed_logger(Box::new(RocketLogger)).is_ok() {
166 ROCKET_LOGGER_SET.store(true, Ordering::Release);
167 }
168
169 let should_color = config.cli_colors && Condition::stdouterr_are_tty();
171 yansi::whenever(Condition::cached(should_color));
172
173 if ROCKET_LOGGER_SET.load(Ordering::Acquire) {
175 log::set_max_level(config.log_level.into());
176 }
177}
178
179impl From<LogLevel> for log::LevelFilter {
180 fn from(level: LogLevel) -> Self {
181 match level {
182 LogLevel::Critical => log::LevelFilter::Warn,
183 LogLevel::Normal => log::LevelFilter::Info,
184 LogLevel::Debug => log::LevelFilter::Trace,
185 LogLevel::Off => log::LevelFilter::Off
186 }
187 }
188}
189
190impl LogLevel {
191 fn as_str(&self) -> &str {
192 match self {
193 LogLevel::Critical => "critical",
194 LogLevel::Normal => "normal",
195 LogLevel::Debug => "debug",
196 LogLevel::Off => "off",
197 }
198 }
199}
200
201impl FromStr for LogLevel {
202 type Err = &'static str;
203
204 fn from_str(s: &str) -> Result<Self, Self::Err> {
205 let level = match &*s.to_ascii_lowercase() {
206 "critical" => LogLevel::Critical,
207 "normal" => LogLevel::Normal,
208 "debug" => LogLevel::Debug,
209 "off" => LogLevel::Off,
210 _ => return Err("a log level (off, debug, normal, critical)")
211 };
212
213 Ok(level)
214 }
215}
216
217impl fmt::Display for LogLevel {
218 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
219 write!(f, "{}", self.as_str())
220 }
221}
222
223impl Serialize for LogLevel {
224 fn serialize<S: Serializer>(&self, ser: S) -> Result<S::Ok, S::Error> {
225 ser.serialize_str(self.as_str())
226 }
227}
228
229impl<'de> Deserialize<'de> for LogLevel {
230 fn deserialize<D: Deserializer<'de>>(de: D) -> Result<Self, D::Error> {
231 let string = String::deserialize(de)?;
232 LogLevel::from_str(&string).map_err(|_| de::Error::invalid_value(
233 de::Unexpected::Str(&string),
234 &figment::error::OneOf( &["critical", "normal", "debug", "off"])
235 ))
236 }
237}
238
239impl PaintExt for &str {
240 fn emoji(self) -> Painted<Self> {
242 #[cfg(windows)] { Paint::new("").mask() }
243 #[cfg(not(windows))] { Paint::new(self).mask() }
244 }
245}