lib.rs 12 KB

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