lib.rs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368
  1. //! A logger that prints all messages with a readable output format.
  2. #[cfg(feature = "chrono")]
  3. use chrono::Local;
  4. #[cfg(feature = "colored")]
  5. use colored::*;
  6. use log::{Level, LevelFilter, Log, Metadata, Record, SetLoggerError};
  7. use std::collections::HashMap;
  8. pub struct SimpleLogger {
  9. /// The default logging level
  10. default_level: LevelFilter,
  11. /// The specific logging level for each module
  12. ///
  13. /// This is used to override the default value for some specific modules.
  14. /// After initialization, the vector is sorted so that the first (prefix) match
  15. /// directly gives us the desired log level.
  16. module_levels: Vec<(String, LevelFilter)>,
  17. /// Whether to include timestamps or not
  18. ///
  19. /// If the `chrono` feature is not enabled, this will not do anything.
  20. timestamps:bool
  21. }
  22. impl SimpleLogger {
  23. /// Initializes the global logger with a SimpleLogger instance with
  24. /// default log level set to `Level::Trace`.
  25. ///
  26. /// You may use the various builder-style methods on this type to configure
  27. /// the logger, and you must call [`init`] in order to start logging messages.
  28. ///
  29. /// ```no_run
  30. /// use simple_logger::SimpleLogger;
  31. /// SimpleLogger::new().init().unwrap();
  32. /// log::warn!("This is an example message.");
  33. /// ```
  34. ///
  35. /// [`init`]: #method.init
  36. #[must_use = "You must call init() to begin logging"]
  37. pub fn new() -> SimpleLogger {
  38. SimpleLogger {
  39. default_level: LevelFilter::Off,
  40. module_levels: Vec::new(),
  41. timestamps: true
  42. }
  43. }
  44. /// Simulates env_logger behavior, which enables the user to choose log level by
  45. /// setting a `RUST_LOG` environment variable. The `RUST_LOG` is not set or its value is not
  46. /// recognized as one of the log levels, this function will use the `Error` level by default.
  47. ///
  48. /// You may use the various builder-style methods on this type to configure
  49. /// the logger, and you must call [`init`] in order to start logging messages.
  50. ///
  51. /// ```no_run
  52. /// use simple_logger::SimpleLogger;
  53. /// SimpleLogger::from_env().init().unwrap();
  54. /// log::warn!("This is an example message.");
  55. /// ```
  56. ///
  57. /// [`init`]: #method.init
  58. #[must_use = "You must call init() to begin logging"]
  59. #[deprecated(
  60. since = "1.12.0",
  61. note = "Use [`env`](#method.env) instead. This predates use of the builder pattern in this library."
  62. )]
  63. pub fn from_env() -> SimpleLogger {
  64. SimpleLogger::new().with_level(log::LevelFilter::Error).env()
  65. }
  66. /// Simulates env_logger behavior, which enables the user to choose log
  67. /// level by setting a `RUST_LOG` environment variable. This will use
  68. /// the default level set by [`with_level`] if `RUST_LOG` is not set or
  69. /// can't be parsed as a standard log level.
  70. ///
  71. /// [`with_level`]: #method.with_level
  72. #[must_use = "You must call init() to begin logging"]
  73. pub fn env(mut self) -> SimpleLogger {
  74. if let Ok(level) = std::env::var("RUST_LOG") {
  75. match level.to_lowercase().as_str() {
  76. "trace" => self.default_level = log::LevelFilter::Trace,
  77. "debug" => self.default_level = log::LevelFilter::Debug,
  78. "info" => self.default_level = log::LevelFilter::Info,
  79. "warn" => self.default_level = log::LevelFilter::Warn,
  80. "error" => self.default_level = log::LevelFilter::Error,
  81. _ => (),
  82. }
  83. };
  84. self
  85. }
  86. /// Set the 'default' log level.
  87. ///
  88. /// You can override the default level for specific modules and their sub-modules using [`with_module_level`]
  89. ///
  90. /// [`with_module_level`]: #method.with_module_level
  91. #[must_use = "You must call init() to begin logging"]
  92. pub fn with_level(mut self, level: LevelFilter) -> SimpleLogger {
  93. self.default_level = level;
  94. self
  95. }
  96. /// Override the log level for some specific modules.
  97. ///
  98. /// This sets the log level of a specific module and all its sub-modules.
  99. /// When both the level for a parent module as well as a child module are set,
  100. /// the more specific value is taken. If the log level for the same module is
  101. /// specified twice, the resulting log level is implementation defined.
  102. ///
  103. /// # Examples
  104. ///
  105. /// Silence an overly verbose crate:
  106. ///
  107. /// ```no_run
  108. /// use simple_logger::SimpleLogger;
  109. /// use log::LevelFilter;
  110. ///
  111. /// SimpleLogger::new().with_module_level("chatty_dependency", LevelFilter::Warn).init().unwrap();
  112. /// ```
  113. ///
  114. /// Disable logging for all dependencies:
  115. ///
  116. /// ```no_run
  117. /// use simple_logger::SimpleLogger;
  118. /// use log::LevelFilter;
  119. ///
  120. /// SimpleLogger::new()
  121. /// .with_level(LevelFilter::Off)
  122. /// .with_module_level("my_crate", LevelFilter::Info)
  123. /// .init()
  124. /// .unwrap();
  125. /// ```
  126. #[must_use = "You must call init() to begin logging"]
  127. pub fn with_module_level(mut self, target: &str, level: LevelFilter) -> SimpleLogger {
  128. self.module_levels.push((target.to_string(), level));
  129. /* Normally this is only called in `init` to avoid redundancy, but we can't initialize the logger in tests */
  130. #[cfg(test)]
  131. self.module_levels
  132. .sort_by_key(|(name, _level)| name.len().wrapping_neg());
  133. self
  134. }
  135. /// Override the log level for specific targets.
  136. #[must_use = "You must call init() to begin logging"]
  137. #[deprecated(
  138. since = "1.11.0",
  139. note = "This is a leftover from before there was the builder pattern. Use [`with_module_level`](#method.with_module_level) instead."
  140. )]
  141. pub fn with_target_levels(
  142. mut self,
  143. target_levels: HashMap<String, LevelFilter>,
  144. ) -> SimpleLogger {
  145. self.module_levels = target_levels.into_iter().collect();
  146. /* Normally this is only called in `init` to avoid redundancy, but we can't initialize the logger in tests */
  147. #[cfg(test)]
  148. self.module_levels
  149. .sort_by_key(|(name, _level)| name.len().wrapping_neg());
  150. self
  151. }
  152. /// Control whether timestamps are printed or not.
  153. ///
  154. /// Timestamps will be printed by default if the `chrono` feature is enabled.
  155. #[must_use = "You must call init() to begin logging"]
  156. pub fn with_timestamps(mut self, timestamps: bool) -> SimpleLogger {
  157. self.timestamps = timestamps;
  158. self
  159. }
  160. /// 'Init' the actual logger, instantiate it and configure it,
  161. /// this method MUST be called in order for the logger to be effective.
  162. pub fn init(mut self) -> Result<(), SetLoggerError> {
  163. #[cfg(all(windows, feature = "colored"))]
  164. set_up_color_terminal();
  165. /* Sort all module levels from most specific to least specific. The length of the module
  166. * name is used instead of its actual depth to avoid module name parsing.
  167. */
  168. self.module_levels
  169. .sort_by_key(|(name, _level)| name.len().wrapping_neg());
  170. let max_level = self
  171. .module_levels
  172. .iter()
  173. .map(|(_name, level)| level)
  174. .copied()
  175. .max();
  176. let max_level = max_level
  177. .map(|lvl| lvl.max(self.default_level))
  178. .unwrap_or(self.default_level);
  179. log::set_max_level(max_level);
  180. log::set_boxed_logger(Box::new(self))?;
  181. Ok(())
  182. }
  183. }
  184. impl Default for SimpleLogger {
  185. /// See [this](struct.SimpleLogger.html#method.new)
  186. fn default() -> Self {
  187. SimpleLogger::new()
  188. }
  189. }
  190. impl Log for SimpleLogger {
  191. fn enabled(&self, metadata: &Metadata) -> bool {
  192. &metadata.level().to_level_filter()
  193. <= self
  194. .module_levels
  195. .iter()
  196. /* At this point the Vec is already sorted so that we can simply take
  197. * the first match
  198. */
  199. .find(|(name, _level)| metadata.target().starts_with(name))
  200. .map(|(_name, level)| level)
  201. .unwrap_or(&self.default_level)
  202. }
  203. fn log(&self, record: &Record) {
  204. if self.enabled(record.metadata()) {
  205. let level_string = {
  206. #[cfg(feature = "colored")]
  207. {
  208. match record.level() {
  209. Level::Error => record.level().to_string().red(),
  210. Level::Warn => record.level().to_string().yellow(),
  211. Level::Info => record.level().to_string().cyan(),
  212. Level::Debug => record.level().to_string().purple(),
  213. Level::Trace => record.level().to_string().normal(),
  214. }
  215. }
  216. #[cfg(not(feature = "colored"))]
  217. {
  218. record.level().to_string()
  219. }
  220. };
  221. let target = if !record.target().is_empty() {
  222. record.target()
  223. } else {
  224. record.module_path().unwrap_or_default()
  225. };
  226. #[cfg(feature = "chrono")]
  227. if self.timestamps {
  228. println!(
  229. "{} {:<5} [{}] {}",
  230. Local::now().format("%Y-%m-%d %H:%M:%S,%3f"),
  231. level_string,
  232. target,
  233. record.args()
  234. );
  235. return;
  236. }
  237. println!("{:<5} [{}] {}", level_string, target, record.args());
  238. }
  239. }
  240. fn flush(&self) {}
  241. }
  242. #[cfg(windows)]
  243. fn set_up_color_terminal() {
  244. use atty::Stream;
  245. if atty::is(Stream::Stdout) {
  246. unsafe {
  247. use winapi::um::consoleapi::*;
  248. use winapi::um::handleapi::*;
  249. use winapi::um::processenv::*;
  250. use winapi::um::winbase::*;
  251. use winapi::um::wincon::*;
  252. let stdout = GetStdHandle(STD_OUTPUT_HANDLE);
  253. if stdout == INVALID_HANDLE_VALUE {
  254. return;
  255. }
  256. let mut mode: winapi::shared::minwindef::DWORD = 0;
  257. if GetConsoleMode(stdout, &mut mode) == 0 {
  258. return;
  259. }
  260. SetConsoleMode(stdout, mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING);
  261. }
  262. }
  263. }
  264. /// Initialise the logger with it's default configuration.
  265. ///
  266. /// Log messages will not be filtered.
  267. /// The `RUST_LOG` environment variable is not used.
  268. pub fn init() -> Result<(), SetLoggerError> {
  269. SimpleLogger::new().init()
  270. }
  271. /// Initialise the logger with the `RUST_LOG` environment variable.
  272. ///
  273. /// Log messages will be filtered based on the `RUST_LOG` environment variable.
  274. pub fn init_with_env() -> Result<(), SetLoggerError> {
  275. SimpleLogger::new().env().init()
  276. }
  277. /// Initialise the logger with a specific log level.
  278. ///
  279. /// Log messages below the given [`Level`] will be filtered.
  280. /// The `RUST_LOG` environment variable is not used.
  281. pub fn init_with_level(level: Level) -> Result<(), SetLoggerError> {
  282. SimpleLogger::new().with_level(level.to_level_filter()).init()
  283. }
  284. /// Use [`init_with_env`] instead.
  285. ///
  286. /// This does the same as [`init_with_env`] but unwraps the result.
  287. #[deprecated(
  288. since = "1.12.0",
  289. note = "Use [`init_with_env`] instead, which does not unwrap the result."
  290. )]
  291. pub fn init_by_env() {
  292. init_with_env().unwrap()
  293. }
  294. #[cfg(test)]
  295. mod test {
  296. use super::*;
  297. #[test]
  298. fn test_module_levels_allowlist() {
  299. let logger = SimpleLogger::new()
  300. .with_level(LevelFilter::Off)
  301. .with_module_level("my_crate", LevelFilter::Info);
  302. assert!(logger.enabled(&create_log("my_crate", Level::Info)));
  303. assert!(logger.enabled(&create_log("my_crate::module", Level::Info)));
  304. assert!(!logger.enabled(&create_log("my_crate::module", Level::Debug)));
  305. assert!(!logger.enabled(&create_log("not_my_crate", Level::Debug)));
  306. assert!(!logger.enabled(&create_log("not_my_crate::module", Level::Error)));
  307. }
  308. #[test]
  309. fn test_module_levels_denylist() {
  310. let logger = SimpleLogger::new()
  311. .with_level(LevelFilter::Debug)
  312. .with_module_level("my_crate", LevelFilter::Trace)
  313. .with_module_level("chatty_dependency", LevelFilter::Info);
  314. assert!(logger.enabled(&create_log("my_crate", Level::Info)));
  315. assert!(logger.enabled(&create_log("my_crate", Level::Trace)));
  316. assert!(logger.enabled(&create_log("my_crate::module", Level::Info)));
  317. assert!(logger.enabled(&create_log("my_crate::module", Level::Trace)));
  318. assert!(logger.enabled(&create_log("not_my_crate", Level::Debug)));
  319. assert!(!logger.enabled(&create_log("not_my_crate::module", Level::Trace)));
  320. assert!(logger.enabled(&create_log("chatty_dependency", Level::Info)));
  321. assert!(!logger.enabled(&create_log("chatty_dependency", Level::Debug)));
  322. assert!(!logger.enabled(&create_log("chatty_dependency::module", Level::Debug)));
  323. assert!(logger.enabled(&create_log("chatty_dependency::module", Level::Warn)));
  324. }
  325. fn create_log(name: &str, level: Level) -> Metadata {
  326. let mut builder = Metadata::builder();
  327. builder.level(level);
  328. builder.target(name);
  329. builder.build()
  330. }
  331. }