lib.rs 12 KB

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