lib.rs 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619
  1. //! A logger that prints all messages with a simple, readable output format.
  2. //!
  3. //! Optional features include timestamps, colored output and logging to stderr.
  4. //!
  5. //! ```
  6. //! simple_logger::SimpleLogger::new().env().init().unwrap();
  7. //!
  8. //! log::warn!("This is an example message.");
  9. //! ```
  10. //!
  11. //! Some shortcuts are available for common use cases.
  12. //!
  13. //! Just initialize logging without any configuration:
  14. //!
  15. //! ```
  16. //! simple_logger::init().unwrap();
  17. //! ```
  18. //!
  19. //! Set the log level from the `RUST_LOG` environment variable:
  20. //!
  21. //! ```
  22. //! simple_logger::init_with_env().unwrap();
  23. //! ```
  24. //!
  25. //! Hardcode a default log level:
  26. //!
  27. //! ```
  28. //! simple_logger::init_with_level(log::Level::Warn).unwrap();
  29. //! ```
  30. #![cfg_attr(feature = "nightly", feature(thread_id_value))]
  31. #[cfg(feature = "colored")]
  32. use colored::*;
  33. use log::{Level, LevelFilter, Log, Metadata, Record, SetLoggerError};
  34. use std::collections::HashMap;
  35. #[cfg(feature = "timestamps")]
  36. use time::{format_description::FormatItem, OffsetDateTime, UtcOffset};
  37. #[cfg(feature = "timestamps")]
  38. const TIMESTAMP_FORMAT_OFFSET: &[FormatItem] = time::macros::format_description!(
  39. "[year]-[month]-[day]T[hour]:[minute]:[second].[subsecond digits:3][offset_hour sign:mandatory]:[offset_minute]"
  40. );
  41. #[cfg(feature = "timestamps")]
  42. const TIMESTAMP_FORMAT_UTC: &[FormatItem] = time::macros::format_description!(
  43. "[year]-[month]-[day]T[hour]:[minute]:[second].[subsecond digits:3]Z"
  44. );
  45. #[cfg(feature = "timestamps")]
  46. #[derive(PartialEq)]
  47. enum Timestamps {
  48. None,
  49. Local,
  50. Utc,
  51. UtcOffset(UtcOffset),
  52. }
  53. /// Implements [`Log`] and a set of simple builder methods for configuration.
  54. ///
  55. /// Use the various "builder" methods on this struct to configure the logger,
  56. /// then call [`init`] to configure the [`log`] crate.
  57. pub struct SimpleLogger {
  58. /// The default logging level
  59. default_level: LevelFilter,
  60. /// The specific logging level for each module
  61. ///
  62. /// This is used to override the default value for some specific modules.
  63. /// After initialization, the vector is sorted so that the first (prefix) match
  64. /// directly gives us the desired log level.
  65. module_levels: Vec<(String, LevelFilter)>,
  66. /// Whether to include thread names (and IDs) or not
  67. ///
  68. /// This field is only available if the `threads` feature is enabled.
  69. #[cfg(feature = "threads")]
  70. threads: bool,
  71. /// Control how timestamps are displayed.
  72. ///
  73. /// This field is only available if the `timestamps` feature is enabled.
  74. #[cfg(feature = "timestamps")]
  75. timestamps: Timestamps,
  76. /// Whether to use color output or not.
  77. ///
  78. /// This field is only available if the `color` feature is enabled.
  79. #[cfg(feature = "colored")]
  80. colors: bool,
  81. }
  82. impl SimpleLogger {
  83. /// Initializes the global logger with a SimpleLogger instance with
  84. /// default log level set to `Level::Trace`.
  85. ///
  86. /// ```no_run
  87. /// use simple_logger::SimpleLogger;
  88. /// SimpleLogger::new().env().init().unwrap();
  89. /// log::warn!("This is an example message.");
  90. /// ```
  91. ///
  92. /// [`init`]: #method.init
  93. #[must_use = "You must call init() to begin logging"]
  94. pub fn new() -> SimpleLogger {
  95. SimpleLogger {
  96. default_level: LevelFilter::Trace,
  97. module_levels: Vec::new(),
  98. #[cfg(feature = "threads")]
  99. threads: false,
  100. #[cfg(feature = "timestamps")]
  101. timestamps: Timestamps::Utc,
  102. #[cfg(feature = "colored")]
  103. colors: true,
  104. }
  105. }
  106. /// Simulates env_logger behavior, which enables the user to choose log level by
  107. /// setting a `RUST_LOG` environment variable. The `RUST_LOG` is not set or its value is not
  108. /// recognized as one of the log levels, this function will use the `Error` level by default.
  109. ///
  110. /// You may use the various builder-style methods on this type to configure
  111. /// the logger, and you must call [`init`] in order to start logging messages.
  112. ///
  113. /// ```no_run
  114. /// use simple_logger::SimpleLogger;
  115. /// SimpleLogger::from_env().init().unwrap();
  116. /// log::warn!("This is an example message.");
  117. /// ```
  118. ///
  119. /// [`init`]: #method.init
  120. #[must_use = "You must call init() to begin logging"]
  121. #[deprecated(
  122. since = "1.12.0",
  123. note = "Use [`env`](#method.env) instead. Will be removed in version 2.0.0."
  124. )]
  125. pub fn from_env() -> SimpleLogger {
  126. SimpleLogger::new()
  127. .with_level(log::LevelFilter::Error)
  128. .env()
  129. }
  130. /// Simulates env_logger behavior, which enables the user to choose log
  131. /// level by setting a `RUST_LOG` environment variable. This will use
  132. /// the default level set by [`with_level`] if `RUST_LOG` is not set or
  133. /// can't be parsed as a standard log level.
  134. ///
  135. /// [`with_level`]: #method.with_level
  136. #[must_use = "You must call init() to begin logging"]
  137. pub fn env(mut self) -> SimpleLogger {
  138. if let Ok(level) = std::env::var("RUST_LOG") {
  139. match level.to_lowercase().as_str() {
  140. "trace" => self.default_level = log::LevelFilter::Trace,
  141. "debug" => self.default_level = log::LevelFilter::Debug,
  142. "info" => self.default_level = log::LevelFilter::Info,
  143. "warn" => self.default_level = log::LevelFilter::Warn,
  144. "error" => self.default_level = log::LevelFilter::Error,
  145. _ => (),
  146. }
  147. };
  148. self
  149. }
  150. /// Set the 'default' log level.
  151. ///
  152. /// You can override the default level for specific modules and their sub-modules using [`with_module_level`]
  153. ///
  154. /// [`with_module_level`]: #method.with_module_level
  155. #[must_use = "You must call init() to begin logging"]
  156. pub fn with_level(mut self, level: LevelFilter) -> SimpleLogger {
  157. self.default_level = level;
  158. self
  159. }
  160. /// Override the log level for some specific modules.
  161. ///
  162. /// This sets the log level of a specific module and all its sub-modules.
  163. /// When both the level for a parent module as well as a child module are set,
  164. /// the more specific value is taken. If the log level for the same module is
  165. /// specified twice, the resulting log level is implementation defined.
  166. ///
  167. /// # Examples
  168. ///
  169. /// Silence an overly verbose crate:
  170. ///
  171. /// ```no_run
  172. /// use simple_logger::SimpleLogger;
  173. /// use log::LevelFilter;
  174. ///
  175. /// SimpleLogger::new().with_module_level("chatty_dependency", LevelFilter::Warn).init().unwrap();
  176. /// ```
  177. ///
  178. /// Disable logging for all dependencies:
  179. ///
  180. /// ```no_run
  181. /// use simple_logger::SimpleLogger;
  182. /// use log::LevelFilter;
  183. ///
  184. /// SimpleLogger::new()
  185. /// .with_level(LevelFilter::Off)
  186. /// .with_module_level("my_crate", LevelFilter::Info)
  187. /// .init()
  188. /// .unwrap();
  189. /// ```
  190. #[must_use = "You must call init() to begin logging"]
  191. pub fn with_module_level(mut self, target: &str, level: LevelFilter) -> SimpleLogger {
  192. self.module_levels.push((target.to_string(), level));
  193. /* Normally this is only called in `init` to avoid redundancy, but we can't initialize the logger in tests */
  194. #[cfg(test)]
  195. self.module_levels
  196. .sort_by_key(|(name, _level)| name.len().wrapping_neg());
  197. self
  198. }
  199. /// Override the log level for specific targets.
  200. #[must_use = "You must call init() to begin logging"]
  201. #[deprecated(
  202. since = "1.11.0",
  203. note = "Use [`with_module_level`](#method.with_module_level) instead. Will be removed in version 2.0.0."
  204. )]
  205. pub fn with_target_levels(
  206. mut self,
  207. target_levels: HashMap<String, LevelFilter>,
  208. ) -> SimpleLogger {
  209. self.module_levels = target_levels.into_iter().collect();
  210. /* Normally this is only called in `init` to avoid redundancy, but we can't initialize the logger in tests */
  211. #[cfg(test)]
  212. self.module_levels
  213. .sort_by_key(|(name, _level)| name.len().wrapping_neg());
  214. self
  215. }
  216. /// Control whether thread names (and IDs) are printed or not.
  217. ///
  218. /// This method is only available if the `threads` feature is enabled.
  219. /// Thread names are disabled by default.
  220. #[must_use = "You must call init() to begin logging"]
  221. #[cfg(feature = "threads")]
  222. pub fn with_threads(mut self, threads: bool) -> SimpleLogger {
  223. self.threads = threads;
  224. self
  225. }
  226. /// Control whether timestamps are printed or not.
  227. ///
  228. /// Timestamps will be displayed in the local timezone.
  229. ///
  230. /// This method is only available if the `timestamps` feature is enabled.
  231. #[must_use = "You must call init() to begin logging"]
  232. #[cfg(feature = "timestamps")]
  233. #[deprecated(
  234. since = "1.16.0",
  235. note = "Use [`with_local_timestamps`] or [`with_utc_timestamps`] instead. Will be removed in version 2.0.0."
  236. )]
  237. pub fn with_timestamps(mut self, timestamps: bool) -> SimpleLogger {
  238. if timestamps {
  239. self.timestamps = Timestamps::Local
  240. } else {
  241. self.timestamps = Timestamps::None
  242. }
  243. self
  244. }
  245. /// Don't display any timestamps.
  246. ///
  247. /// This method is only available if the `timestamps` feature is enabled.
  248. #[must_use = "You must call init() to begin logging"]
  249. #[cfg(feature = "timestamps")]
  250. pub fn without_timestamps(mut self) -> SimpleLogger {
  251. self.timestamps = Timestamps::None;
  252. self
  253. }
  254. /// Display timestamps using the local timezone.
  255. ///
  256. /// This method is only available if the `timestamps` feature is enabled.
  257. #[must_use = "You must call init() to begin logging"]
  258. #[cfg(feature = "timestamps")]
  259. pub fn with_local_timestamps(mut self) -> SimpleLogger {
  260. self.timestamps = Timestamps::Local;
  261. self
  262. }
  263. /// Display timestamps using UTC.
  264. ///
  265. /// This method is only available if the `timestamps` feature is enabled.
  266. #[must_use = "You must call init() to begin logging"]
  267. #[cfg(feature = "timestamps")]
  268. pub fn with_utc_timestamps(mut self) -> SimpleLogger {
  269. self.timestamps = Timestamps::Utc;
  270. self
  271. }
  272. /// Display timestamps using a static UTC offset.
  273. ///
  274. /// This method is only available if the `timestamps` feature is enabled.
  275. #[must_use = "You must call init() to begin logging"]
  276. #[cfg(feature = "timestamps")]
  277. pub fn with_utc_offset(mut self, offset: UtcOffset) -> SimpleLogger {
  278. self.timestamps = Timestamps::UtcOffset(offset);
  279. self
  280. }
  281. /// Control whether messages are colored or not.
  282. ///
  283. /// This method is only available if the `colored` feature is enabled.
  284. #[must_use = "You must call init() to begin logging"]
  285. #[cfg(feature = "colored")]
  286. pub fn with_colors(mut self, colors: bool) -> SimpleLogger {
  287. self.colors = colors;
  288. self
  289. }
  290. /// 'Init' the actual logger, instantiate it and configure it,
  291. /// this method MUST be called in order for the logger to be effective.
  292. pub fn init(mut self) -> Result<(), SetLoggerError> {
  293. #[cfg(all(windows, feature = "colored"))]
  294. set_up_color_terminal();
  295. /* Sort all module levels from most specific to least specific. The length of the module
  296. * name is used instead of its actual depth to avoid module name parsing.
  297. */
  298. self.module_levels
  299. .sort_by_key(|(name, _level)| name.len().wrapping_neg());
  300. let max_level = self
  301. .module_levels
  302. .iter()
  303. .map(|(_name, level)| level)
  304. .copied()
  305. .max();
  306. let max_level = max_level
  307. .map(|lvl| lvl.max(self.default_level))
  308. .unwrap_or(self.default_level);
  309. log::set_max_level(max_level);
  310. log::set_boxed_logger(Box::new(self))?;
  311. Ok(())
  312. }
  313. }
  314. impl Default for SimpleLogger {
  315. /// See [this](struct.SimpleLogger.html#method.new)
  316. fn default() -> Self {
  317. SimpleLogger::new()
  318. }
  319. }
  320. impl Log for SimpleLogger {
  321. fn enabled(&self, metadata: &Metadata) -> bool {
  322. &metadata.level().to_level_filter()
  323. <= self
  324. .module_levels
  325. .iter()
  326. /* At this point the Vec is already sorted so that we can simply take
  327. * the first match
  328. */
  329. .find(|(name, _level)| metadata.target().starts_with(name))
  330. .map(|(_name, level)| level)
  331. .unwrap_or(&self.default_level)
  332. }
  333. fn log(&self, record: &Record) {
  334. if self.enabled(record.metadata()) {
  335. let level_string = {
  336. #[cfg(feature = "colored")]
  337. {
  338. if self.colors {
  339. match record.level() {
  340. Level::Error => record.level().to_string().red().to_string(),
  341. Level::Warn => record.level().to_string().yellow().to_string(),
  342. Level::Info => record.level().to_string().cyan().to_string(),
  343. Level::Debug => record.level().to_string().purple().to_string(),
  344. Level::Trace => record.level().to_string().normal().to_string(),
  345. }
  346. } else {
  347. record.level().to_string()
  348. }
  349. }
  350. #[cfg(not(feature = "colored"))]
  351. {
  352. record.level().to_string()
  353. }
  354. };
  355. let target = if !record.target().is_empty() {
  356. record.target()
  357. } else {
  358. record.module_path().unwrap_or_default()
  359. };
  360. let thread = {
  361. #[cfg(feature = "threads")]
  362. if self.threads {
  363. let thread = std::thread::current();
  364. format!("@{}", {
  365. #[cfg(feature = "nightly")]
  366. {
  367. thread.name().unwrap_or(&thread.id().as_u64().to_string())
  368. }
  369. #[cfg(not(feature = "nightly"))]
  370. {
  371. thread.name().unwrap_or("?")
  372. }
  373. })
  374. } else {
  375. "".to_string()
  376. }
  377. #[cfg(not(feature = "threads"))]
  378. ""
  379. };
  380. let timestamp = {
  381. #[cfg(feature = "timestamps")]
  382. match self.timestamps {
  383. Timestamps::None => "".to_string(),
  384. Timestamps::Local => format!("{} ", OffsetDateTime::now_local().expect(concat!(
  385. "Could not determine the UTC offset on this system. ",
  386. "Consider displaying UTC time instead. ",
  387. "Possible causes are that the time crate does not implement \"local_offset_at\" ",
  388. "on your system, or that you are running in a multi-threaded environment and ",
  389. "the time crate is returning \"None\" from \"local_offset_at\" to avoid unsafe ",
  390. "behaviour. See the time crate's documentation for more information. ",
  391. "(https://time-rs.github.io/internal-api/time/index.html#feature-flags)"
  392. )).format(&TIMESTAMP_FORMAT_OFFSET).unwrap()),
  393. Timestamps::Utc => format!("{} ", OffsetDateTime::now_utc().format(&TIMESTAMP_FORMAT_UTC).unwrap()),
  394. Timestamps::UtcOffset(offset) => format!("{} ", OffsetDateTime::now_utc().to_offset(offset).format(&TIMESTAMP_FORMAT_UTC).unwrap()),
  395. }
  396. #[cfg(not(feature = "timestamps"))]
  397. ""
  398. };
  399. let message = format!(
  400. "{}{:<5} [{}{}] {}",
  401. timestamp,
  402. level_string,
  403. target,
  404. thread,
  405. record.args()
  406. );
  407. #[cfg(not(feature = "stderr"))]
  408. println!("{}", message);
  409. #[cfg(feature = "stderr")]
  410. eprintln!("{}", message);
  411. }
  412. }
  413. fn flush(&self) {}
  414. }
  415. #[cfg(all(windows, feature = "colored"))]
  416. fn set_up_color_terminal() {
  417. use atty::Stream;
  418. if atty::is(Stream::Stdout) {
  419. unsafe {
  420. use winapi::um::consoleapi::*;
  421. use winapi::um::handleapi::*;
  422. use winapi::um::processenv::*;
  423. use winapi::um::winbase::*;
  424. use winapi::um::wincon::*;
  425. let stdout = GetStdHandle(STD_OUTPUT_HANDLE);
  426. if stdout == INVALID_HANDLE_VALUE {
  427. return;
  428. }
  429. let mut mode: winapi::shared::minwindef::DWORD = 0;
  430. if GetConsoleMode(stdout, &mut mode) == 0 {
  431. return;
  432. }
  433. SetConsoleMode(stdout, mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING);
  434. }
  435. }
  436. }
  437. /// Initialise the logger with it's default configuration.
  438. ///
  439. /// Log messages will not be filtered.
  440. /// The `RUST_LOG` environment variable is not used.
  441. pub fn init() -> Result<(), SetLoggerError> {
  442. SimpleLogger::new().init()
  443. }
  444. /// Initialise the logger with it's default configuration.
  445. ///
  446. /// Log messages will not be filtered.
  447. /// The `RUST_LOG` environment variable is not used.
  448. ///
  449. /// This function is only available if the `timestamps` feature is enabled.
  450. #[cfg(feature = "timestamps")]
  451. pub fn init_utc() -> Result<(), SetLoggerError> {
  452. SimpleLogger::new().with_utc_timestamps().init()
  453. }
  454. /// Initialise the logger with the `RUST_LOG` environment variable.
  455. ///
  456. /// Log messages will be filtered based on the `RUST_LOG` environment variable.
  457. pub fn init_with_env() -> Result<(), SetLoggerError> {
  458. SimpleLogger::new().env().init()
  459. }
  460. /// Initialise the logger with a specific log level.
  461. ///
  462. /// Log messages below the given [`Level`] will be filtered.
  463. /// The `RUST_LOG` environment variable is not used.
  464. pub fn init_with_level(level: Level) -> Result<(), SetLoggerError> {
  465. SimpleLogger::new()
  466. .with_level(level.to_level_filter())
  467. .init()
  468. }
  469. /// Use [`init_with_env`] instead.
  470. ///
  471. /// This does the same as [`init_with_env`] but unwraps the result.
  472. #[deprecated(
  473. since = "1.12.0",
  474. note = "Use [`init_with_env`] instead, which does not unwrap the result. Will be removed in version 2.0.0."
  475. )]
  476. pub fn init_by_env() {
  477. init_with_env().unwrap()
  478. }
  479. #[cfg(test)]
  480. mod test {
  481. use super::*;
  482. #[test]
  483. fn test_module_levels_allowlist() {
  484. let logger = SimpleLogger::new()
  485. .with_level(LevelFilter::Off)
  486. .with_module_level("my_crate", LevelFilter::Info);
  487. assert!(logger.enabled(&create_log("my_crate", Level::Info)));
  488. assert!(logger.enabled(&create_log("my_crate::module", Level::Info)));
  489. assert!(!logger.enabled(&create_log("my_crate::module", Level::Debug)));
  490. assert!(!logger.enabled(&create_log("not_my_crate", Level::Debug)));
  491. assert!(!logger.enabled(&create_log("not_my_crate::module", Level::Error)));
  492. }
  493. #[test]
  494. fn test_module_levels_denylist() {
  495. let logger = SimpleLogger::new()
  496. .with_level(LevelFilter::Debug)
  497. .with_module_level("my_crate", LevelFilter::Trace)
  498. .with_module_level("chatty_dependency", LevelFilter::Info);
  499. assert!(logger.enabled(&create_log("my_crate", Level::Info)));
  500. assert!(logger.enabled(&create_log("my_crate", Level::Trace)));
  501. assert!(logger.enabled(&create_log("my_crate::module", Level::Info)));
  502. assert!(logger.enabled(&create_log("my_crate::module", Level::Trace)));
  503. assert!(logger.enabled(&create_log("not_my_crate", Level::Debug)));
  504. assert!(!logger.enabled(&create_log("not_my_crate::module", Level::Trace)));
  505. assert!(logger.enabled(&create_log("chatty_dependency", Level::Info)));
  506. assert!(!logger.enabled(&create_log("chatty_dependency", Level::Debug)));
  507. assert!(!logger.enabled(&create_log("chatty_dependency::module", Level::Debug)));
  508. assert!(logger.enabled(&create_log("chatty_dependency::module", Level::Warn)));
  509. }
  510. #[test]
  511. #[cfg(feature = "timestamps")]
  512. fn test_timestamps_defaults() {
  513. let builder = SimpleLogger::new();
  514. assert!(builder.timestamps == Timestamps::Utc);
  515. }
  516. #[test]
  517. #[cfg(feature = "timestamps")]
  518. #[allow(deprecated)]
  519. fn test_with_timestamps() {
  520. let builder = SimpleLogger::new().with_timestamps(false);
  521. assert!(builder.timestamps == Timestamps::None);
  522. }
  523. #[test]
  524. #[cfg(feature = "timestamps")]
  525. fn test_with_utc_timestamps() {
  526. let builder = SimpleLogger::new().with_utc_timestamps();
  527. assert!(builder.timestamps == Timestamps::Utc);
  528. }
  529. #[test]
  530. #[cfg(feature = "timestamps")]
  531. fn test_with_local_timestamps() {
  532. let builder = SimpleLogger::new().with_local_timestamps();
  533. assert!(builder.timestamps == Timestamps::Local);
  534. }
  535. #[test]
  536. #[cfg(feature = "colored")]
  537. fn test_with_colors() {
  538. let mut builder = SimpleLogger::new();
  539. assert!(builder.colors == true);
  540. builder = builder.with_colors(false);
  541. assert!(builder.colors == false);
  542. }
  543. fn create_log(name: &str, level: Level) -> Metadata {
  544. let mut builder = Metadata::builder();
  545. builder.level(level);
  546. builder.target(name);
  547. builder.build()
  548. }
  549. }