1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
//! The boilerplate to have some file logging with a level given by an environment variable, //! and a facility to log execution durations according to the relevant log level. //! //! It's especially convenient for terminal applications //! because you don't want to mix log with stdout or stderr. //! //! The use of an env variable makes it possible to distribute //! the application and have users generate some logs without //! recompilation or configuration. //! //! The names of the log file and the env variable are //! computed from the name of the application. //! //! So log initialization is just //! //! ``` //! #[macro_use] extern crate log; //! #[macro_use] extern crate cli_log; //! init_cli_log!(); //! ``` //! //! Here's a complete application using cli-log (it can be found in examples): //! //! ``` //! #[macro_use] extern crate log; //! #[macro_use] extern crate cli_log; //! //! #[derive(Debug)] //! struct AppData { //! count: usize, //! } //! impl AppData { //! fn compute(&mut self) { //! self.count += 7; //! } //! } //! //! fn main() { //! init_cli_log!(); //! let mut app_data = AppData { count: 35 }; //! time!(Debug, app_data.compute()); //! info!("count is {}", app_data.count); //! debug!("data: {:#?}", &app_data); //! warn!("this application does nothing"); //! info!("bye"); //! } //! //! ``` //! //! If you don't set any `SMALL_APP_LOG` env variable, there won't be any log. //! //! A convenient way to set the env variable is to launch the app as //! //! ```cli //! SMALL_APP_LOG=debug small_app //! ``` //! //! or, during development, //! //! ```cli //! SMALL_APP_LOG=debug cargo run //! ``` //! //! This creates a `small_app.log` file containing information like the level, //! app version, and of course the log operations you did with time precise to //! the ms and the logging module (target): //! //! ```log //! 13:39:53.511 [INFO] cli_log: Starting small-app v0.1.0 with log level DEBUG //! 13:39:53.511 [INFO] small_app: count is 42 //! 13:39:53.511 [DEBUG] small_app: data: AppData { //! count: 42, //! } //! 13:39:53.511 [WARN] small_app: this application does nothing //! 13:39:53.511 [INFO] small_app: bye //! ``` //! #[macro_use] extern crate log; mod file_logger; mod time; use { file_logger::FileLogger, log::{ LevelFilter, }, std::{ env, fs::File, str::FromStr, sync::Mutex, }, }; /// configure the application log according to env variable. pub fn init(app_name: &str, app_version: &str) { let env_var_name = format!( "{}_LOG", app_name.to_ascii_uppercase().replace('-', "_"), ); let level = env::var(&env_var_name).unwrap_or_else(|_| "off".to_string()); if level == "off" { return; } if let Ok(level) = LevelFilter::from_str(&level) { let log_file_name = format!("{}.log", app_name); let file = File::create(&log_file_name) .expect("Log file can't be created"); log::set_max_level(level); let logger = FileLogger { file: Mutex::new(file), level, }; log::set_boxed_logger(Box::new(logger)).unwrap(); info!( "Starting {} v{} with log level {}", app_name, app_version, level ); } } /// configure the application log according to env variable /// /// Example: /// /// ``` /// #[macro_use] extern crate log; /// #[macro_use] extern crate cli_log; /// init_cli_log!(); /// ``` /// You may specify an altername application name instead /// of your crate name: /// /// ``` /// #[macro_use] extern crate log; /// #[macro_use] extern crate cli_log; /// init_cli_log!("my-app"); /// ``` /// /// The application name will also be used to derive the /// env variable name giving the log level, for example /// `MY_APP_LOG=info` for an application named `my-app`. // The point of this macro is to ensure `env!(CARGO_PKG_NAME)` // and `env!(CARGO_PKG_VERSION)` are expanded for the outer // package, not for cli-log #[macro_export] macro_rules! init_cli_log { () => { cli_log::init(env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION")); }; ($app_name: expr) => { cli_log::init($app_name, env!("CARGO_PKG_VERSION")); }; }