diff --git a/Cargo.lock b/Cargo.lock index 29ea0e4424..0e78e385b4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -139,6 +139,12 @@ version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc4662175ead9cd84451d5c35070517777949a2ed84551764129cedb88384841" +[[package]] +name = "arc-swap" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d25d88fd6b8041580a654f9d0c581a047baee2b3efee13275f2fc392fc75034" + [[package]] name = "arrayref" version = "0.3.6" @@ -835,6 +841,8 @@ dependencies = [ "lazy_static", "libc", "libflate", + "log 0.4.11", + "log4rs", "metrics", "metrics-core", "metrics-runtime", @@ -1230,6 +1238,12 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" +[[package]] +name = "dtoa" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "134951f4028bdadb9b84baf4232681efbf277da25144b9b0ad65df75946c422b" + [[package]] name = "dyn-clonable" version = "0.9.0" @@ -2698,6 +2712,12 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "linked-hash-map" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8dd5a6d5999d9907cda8ed67bbd137d3af8085216c2ac62de5be860bd41f304a" + [[package]] name = "lock_api" version = "0.1.5" @@ -2742,6 +2762,38 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b" dependencies = [ "cfg-if", + "serde", +] + +[[package]] +name = "log-mdc" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a94d21414c1f4a51209ad204c1776a3d0765002c76c6abcb602a6f09f1e881c7" + +[[package]] +name = "log4rs" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1e1ad45e4584824d760c35d71868dd7e6e5acd8f5195a9573743b369fc86cd6" +dependencies = [ + "arc-swap 0.4.7", + "chrono", + "flate2", + "fnv", + "humantime", + "libc", + "log 0.4.11", + "log-mdc", + "parking_lot 0.11.0", + "serde", + "serde-value", + "serde_derive", + "serde_json", + "serde_yaml", + "thread-id", + "typemap", + "winapi 0.3.8", ] [[package]] @@ -2886,7 +2938,7 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "beb3035626782c533953bcc1a349467543b7f0e9d7b0c92edc61ee2bda7033d6" dependencies = [ - "arc-swap", + "arc-swap 0.3.11", "crossbeam-utils 0.6.6", "im", "metrics", @@ -3273,6 +3325,15 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" +[[package]] +name = "ordered-float" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3741934be594d77de1c8461ebcbbe866f585ea616a9753aa78f2bdc69f0e4579" +dependencies = [ + "num-traits 0.2.12", +] + [[package]] name = "owning_ref" version = "0.4.1" @@ -4420,6 +4481,16 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde-value" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a65a7291a8a568adcae4c10a677ebcedbc6c9cec91c054dee2ce40b0e3290eb" +dependencies = [ + "ordered-float", + "serde", +] + [[package]] name = "serde_bencode" version = "0.2.1" @@ -4471,6 +4542,18 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_yaml" +version = "0.8.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7baae0a99f1a324984bcdc5f0718384c1f69775f1c7eec8b859b71b443e3fd7" +dependencies = [ + "dtoa", + "linked-hash-map", + "serde", + "yaml-rust", +] + [[package]] name = "serialization" version = "0.1.0" @@ -5150,6 +5233,17 @@ dependencies = [ "syn 1.0.33", ] +[[package]] +name = "thread-id" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7fbf4c9d56b320106cd64fd024dadfa0be7cb4706725fc44a7d7ce952d820c1" +dependencies = [ + "libc", + "redox_syscall", + "winapi 0.3.8", +] + [[package]] name = "thread_local" version = "1.0.1" @@ -5376,6 +5470,12 @@ dependencies = [ "tracing-serde", ] +[[package]] +name = "traitobject" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efd1f82c56340fdf16f2a953d7bda4f8fdffba13d93b00844c25572110b26079" + [[package]] name = "trie-db" version = "0.22.1" @@ -5415,6 +5515,15 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "typemap" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "653be63c80a3296da5551e1bfd2cca35227e13cdd08c6668903ae2f4f77aa1f6" +dependencies = [ + "unsafe-any", +] + [[package]] name = "typenum" version = "1.12.0" @@ -5507,6 +5616,15 @@ dependencies = [ "subtle 2.2.3", ] +[[package]] +name = "unsafe-any" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f30360d7979f5e9c6e6cea48af192ea8fab4afb3cf72597154b8f08935bc9c7f" +dependencies = [ + "traitobject", +] + [[package]] name = "unsigned-varint" version = "0.3.3" @@ -5933,6 +6051,15 @@ dependencies = [ "libc", ] +[[package]] +name = "yaml-rust" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39f0c922f1a334134dc2f7a8b67dc5d25f0735263feec974345ff706bcf20b0d" +dependencies = [ + "linked-hash-map", +] + [[package]] name = "yamux" version = "0.8.0" diff --git a/mm2src/common/Cargo.toml b/mm2src/common/Cargo.toml index bc0b247754..1a54578cea 100644 --- a/mm2src/common/Cargo.toml +++ b/mm2src/common/Cargo.toml @@ -6,7 +6,7 @@ edition = "2018" [features] native = [ - "hyper", "hyper-rustls", "libc", "tokio" + "hyper", "hyper-rustls", "libc", "tokio" ] w-bindgen = ["wasm-bindgen", "web-sys"] @@ -18,7 +18,7 @@ doctest = false [dependencies] arrayref = "0.3" -async-std = {version = "1.5", features = ["unstable"]} +async-std = { version = "1.5", features = ["unstable"] } atomic = "^0.4.6" async-trait = "0.1" backtrace = "0.3" @@ -45,8 +45,10 @@ itertools = "0.8" keys = { git = "https://github.com/artemii235/parity-bitcoin.git" } lazy_static = "1.2" libc = { version = "0.2", optional = true } +log = "0.4.8" +log4rs = "0.13" metrics = "0.12" -metrics-runtime = {version = "0.13", default-features = false, features = ["metrics-observer-prometheus"] } +metrics-runtime = { version = "0.13", default-features = false, features = ["metrics-observer-prometheus"] } metrics-core = "0.5" metrics-util = "0.3" num-bigint = { version = "0.2", features = ["serde", "std"] } @@ -72,7 +74,7 @@ winapi = "0.3" [dependencies.web-sys] version = "0.3.4" features = [ - 'console', + 'console', ] optional = true diff --git a/mm2src/common/log.rs b/mm2src/common/log.rs index b3c971a528..03cfdaf78e 100644 --- a/mm2src/common/log.rs +++ b/mm2src/common/log.rs @@ -854,6 +854,144 @@ impl Drop for LogState { } } +pub mod unified_log { + use super::chunk2log; + pub use log::LevelFilter; + use log::Record; + use log4rs::{append, config, + encode::{pattern, writer::simple}}; + + const MM_FORMAT: &str = "{d(%d %H:%M:%S)(utc)}, {f}:{L}] {l} {m}"; + const DEFAULT_FORMAT: &str = "[{d(%Y-%m-%d %H:%M:%S %Z)(utc)} {h({l})} {M}:{f}:{L}] {m}"; + const DEFAULT_LEVEL_FILTER: LevelFilter = LevelFilter::Info; + + pub struct UnifiedLoggerBuilder { + console_format: String, + mm_format: String, + filter: LevelPolicy, + console: bool, + mm_log: bool, + } + + impl Default for UnifiedLoggerBuilder { + fn default() -> UnifiedLoggerBuilder { + UnifiedLoggerBuilder { + console_format: DEFAULT_FORMAT.to_owned(), + mm_format: MM_FORMAT.to_owned(), + filter: LevelPolicy::Exact(DEFAULT_LEVEL_FILTER), + console: true, + mm_log: false, + } + } + } + + impl UnifiedLoggerBuilder { + pub fn new() -> UnifiedLoggerBuilder { UnifiedLoggerBuilder::default() } + + pub fn console_format(mut self, console_format: &str) -> UnifiedLoggerBuilder { + self.console_format = console_format.to_owned(); + self + } + + pub fn mm_format(mut self, mm_format: &str) -> UnifiedLoggerBuilder { + self.mm_format = mm_format.to_owned(); + self + } + + pub fn level_filter(mut self, filter: LevelFilter) -> UnifiedLoggerBuilder { + self.filter = LevelPolicy::Exact(filter); + self + } + + pub fn level_filter_from_env_or_default(mut self, default: LevelFilter) -> UnifiedLoggerBuilder { + self.filter = LevelPolicy::FromEnvOrDefault(default); + self + } + + pub fn console(mut self, console: bool) -> UnifiedLoggerBuilder { + self.console = console; + self + } + + pub fn mm_log(mut self, mm_log: bool) -> UnifiedLoggerBuilder { + self.mm_log = mm_log; + self + } + + pub fn try_init(self) -> Result<(), String> { + let mut appenders = Vec::new(); + let level_filter = match self.filter { + LevelPolicy::Exact(l) => l, + LevelPolicy::FromEnvOrDefault(default) => Self::get_level_filter_from_env().unwrap_or(default), + }; + + if self.mm_log { + let appender = MmLogAppender::new(&self.mm_format); + appenders.push(config::Appender::builder().build("mm_log", Box::new(appender))); + } + + // TODO console appender prints without '/n' + if self.console { + let encoder = Box::new(pattern::PatternEncoder::new(&self.console_format)); + let appender = append::console::ConsoleAppender::builder() + .encoder(encoder) + .target(append::console::Target::Stdout) + .build(); + appenders.push(config::Appender::builder().build("console", Box::new(appender))); + } + + let app_names: Vec<_> = appenders.iter().map(|app| app.name()).collect(); + let root = config::Root::builder().appenders(app_names).build(level_filter); + let config = try_s!(config::Config::builder().appenders(appenders).build(root)); + + try_s!(log4rs::init_config(config)); + Ok(()) + } + + fn get_level_filter_from_env() -> Option { + match std::env::var("RUST_LOG").ok()?.to_lowercase().as_str() { + "off" => Some(LevelFilter::Off), + "error" => Some(LevelFilter::Error), + "warn" => Some(LevelFilter::Warn), + "info" => Some(LevelFilter::Info), + "debug" => Some(LevelFilter::Debug), + "trace" => Some(LevelFilter::Trace), + _ => None, + } + } + } + + enum LevelPolicy { + Exact(LevelFilter), + FromEnvOrDefault(LevelFilter), + } + + #[derive(Debug)] + struct MmLogAppender { + pattern: Box, + } + + impl MmLogAppender { + fn new(pattern: &str) -> MmLogAppender { + MmLogAppender { + pattern: Box::new(pattern::PatternEncoder::new(pattern)), + } + } + } + + impl append::Append for MmLogAppender { + fn append(&self, record: &Record) -> Result<(), Box> { + let mut buf = Vec::new(); + self.pattern.encode(&mut simple::SimpleWriter(&mut buf), record)?; + let as_string = String::from_utf8(buf).map_err(|e| Box::new(e))?; + chunk2log(as_string); + Ok(()) + } + + fn flush(&self) {} + } +} + #[doc(hidden)] pub mod tests { use super::LogState; diff --git a/mm2src/mm2.rs b/mm2src/mm2.rs index 52edcacdb6..bb4c16a956 100644 --- a/mm2src/mm2.rs +++ b/mm2src/mm2.rs @@ -23,6 +23,7 @@ #![cfg_attr(not(feature = "native"), allow(unused_imports))] use common::crash_reports::init_crash_reports; +use common::log::unified_log::{LevelFilter, UnifiedLoggerBuilder}; use common::mm_ctx::MmCtxBuilder; use common::{block_on, double_panic_crash, safe_slurp, MM_DATETIME, MM_VERSION}; @@ -54,9 +55,16 @@ mod mm2_tests; /// * `ctx_cb` - callback used to share the `MmCtx` ID with the call site. pub fn lp_main(conf: Json, ctx_cb: &dyn Fn(u32)) -> Result<(), String> { // std::env::set_var("RUST_LOG", "debug"); - if let Err(e) = env_logger::from_env(env_logger::Env::default().default_filter_or("info")).try_init() { - log!("Env logger initialization failed: "(e)) + if let Err(e) = UnifiedLoggerBuilder::default() + .level_filter_from_env_or_default(LevelFilter::Info) + .console(false) + .mm_log(true) + .try_init() + { + log!("Unified logger initialization failed: "(e)) } + + // std::env::set_var("RUST_LOG", "debug"); if !conf["rpc_password"].is_null() { if !conf["rpc_password"].is_string() { return ERR!("rpc_password must be string");