lib.rs 11 KB

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