/* This file is part of DarkFi (https://dark.fi) * * Copyright (C) 2020-2025 Dyne.org foundation * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ use file_rotate::{compression::Compression, suffix::AppendCount, ContentLimit, FileRotate}; use log::{Level, LevelFilter, Log, Metadata, Record}; use simplelog::{ ColorChoice, CombinedLogger, Config, ConfigBuilder, SharedLogger, TermLogger, TerminalMode, WriteLogger, }; use std::{path::PathBuf, thread::sleep, time::Duration}; // Measured in bytes const LOGFILE_MAXSIZE: usize = 5_000_000; static MUTED_TARGETS: &[&'static str] = &[ "sled", "rustls", "net::channel", "net::message_publisher", "net::hosts", "net::protocol", "net::session", "net::outbound_session", "net::tcp", "net::p2p::seed", "net::refinery::handshake_node()", "system::publisher", "event_graph::dag_sync()", "event_graph::dag_insert()", "event_graph::protocol", ]; static ALLOW_TRACE: &[&'static str] = &["ui", "app", "gfx"]; #[cfg(target_os = "android")] fn logfile_path() -> PathBuf { use crate::android::get_external_storage_path; get_external_storage_path().join("darkfi-app.log") } #[cfg(not(target_os = "android"))] fn logfile_path() -> PathBuf { dirs::cache_dir().unwrap().join("darkfi/darkfi-app.log") } #[cfg(target_os = "android")] mod android { use super::*; use android_logger::{AndroidLogger, Config as AndroidConfig}; /// Implements a wrapper around the android logger so it's compatible with simplelog. pub struct AndroidLoggerWrapper { logger: AndroidLogger, level: LevelFilter, config: Config, } impl AndroidLoggerWrapper { pub fn new(level: LevelFilter, config: Config) -> Box { let cfg = AndroidConfig::default().with_max_level(level).with_tag("darkfi"); Box::new(Self { logger: AndroidLogger::new(cfg), level, config }) } } impl Log for AndroidLoggerWrapper { fn enabled(&self, metadata: &Metadata<'_>) -> bool { let target = metadata.target(); for allow in ALLOW_TRACE { if target.starts_with(allow) { return true } } for muted in MUTED_TARGETS { if target.starts_with(muted) { return false } } if metadata.level() > self.level { return false } self.logger.enabled(metadata) } fn log(&self, record: &Record<'_>) { if self.enabled(record.metadata()) { self.logger.log(record) } } fn flush(&self) {} } impl SharedLogger for AndroidLoggerWrapper { fn level(&self) -> LevelFilter { self.level } fn config(&self) -> Option<&Config> { Some(&self.config) } fn as_log(self: Box) -> Box { Box::new(*self) } } } #[cfg(not(target_os = "android"))] mod desktop { use super::*; /// Implements a wrapper around the android logger so it's compatible with simplelog. pub struct CustomTermLogger { logger: TermLogger, } impl CustomTermLogger { pub fn new(level: LevelFilter, cfg: Config) -> Box { let logger = TermLogger::new(LevelFilter::Trace, cfg, TerminalMode::Mixed, ColorChoice::Auto); Box::new(Self { logger: *logger }) } } impl Log for CustomTermLogger { fn enabled(&self, metadata: &Metadata<'_>) -> bool { let target = metadata.target(); for allow in ALLOW_TRACE { if target.starts_with(allow) { return true } } for muted in MUTED_TARGETS { if target.starts_with(muted) && metadata.level() > LevelFilter::Info { return false } } if metadata.level() > self.level() { return false } self.logger.enabled(metadata) } fn log(&self, record: &Record<'_>) { if self.enabled(record.metadata()) { self.logger.log(record) } } fn flush(&self) { self.logger.flush() } } impl SharedLogger for CustomTermLogger { fn level(&self) -> LevelFilter { self.logger.level() } fn config(&self) -> Option<&Config> { self.logger.config() } fn as_log(self: Box) -> Box { Box::new(self.logger).as_log() } } } pub fn setup_logging() { // https://gist.github.com/jb-alvarado/6e223936446bb88cd9a93e7028fc2c4f let mut loggers: Vec> = vec![]; let mut cfg = ConfigBuilder::new(); #[cfg(feature = "enable-filelog")] { let mut cfg = cfg.clone(); cfg.add_filter_ignore_str("sled"); cfg.add_filter_ignore_str("rustls"); let cfg = cfg.build(); let log_file = FileRotate::new( logfile_path(), AppendCount::new(0), ContentLimit::BytesSurpassed(LOGFILE_MAXSIZE), Compression::None, #[cfg(unix)] None, ); let file_logger = WriteLogger::new(LevelFilter::Trace, cfg, log_file); loggers.push(file_logger); } let cfg = cfg.build(); #[cfg(target_os = "android")] { use android::AndroidLoggerWrapper; let android_logger = AndroidLoggerWrapper::new(LevelFilter::Debug, cfg); loggers.push(android_logger); } #[cfg(not(target_os = "android"))] { use desktop::CustomTermLogger; // For ANSI colors in the terminal colored::control::set_override(true); let term_logger = CustomTermLogger::new(LevelFilter::Debug, cfg); loggers.push(term_logger); } CombinedLogger::init(loggers).expect("logger"); }