logger.rs 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2025 Dyne.org foundation
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU Affero General Public License as
  7. * published by the Free Software Foundation, either version 3 of the
  8. * License, or (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU Affero General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU Affero General Public License
  16. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  17. */
  18. use file_rotate::{compression::Compression, suffix::AppendCount, ContentLimit, FileRotate};
  19. use log::{Level, LevelFilter, Log, Metadata, Record};
  20. use simplelog::{
  21. ColorChoice, CombinedLogger, Config, ConfigBuilder, SharedLogger, TermLogger, TerminalMode,
  22. WriteLogger,
  23. };
  24. use std::{path::PathBuf, thread::sleep, time::Duration};
  25. // Measured in bytes
  26. const LOGFILE_MAXSIZE: usize = 5_000_000;
  27. static MUTED_TARGETS: &[&'static str] = &[
  28. "sled",
  29. "rustls",
  30. "net::channel",
  31. "net::message_publisher",
  32. "net::hosts",
  33. "net::protocol",
  34. "net::session",
  35. "net::outbound_session",
  36. "net::tcp",
  37. "net::p2p::seed",
  38. "net::refinery::handshake_node()",
  39. "system::publisher",
  40. "event_graph::dag_sync()",
  41. "event_graph::dag_insert()",
  42. "event_graph::protocol",
  43. ];
  44. static ALLOW_TRACE: &[&'static str] = &["ui", "app", "gfx"];
  45. #[cfg(target_os = "android")]
  46. fn logfile_path() -> PathBuf {
  47. use crate::android::get_external_storage_path;
  48. get_external_storage_path().join("darkfi-app.log")
  49. }
  50. #[cfg(not(target_os = "android"))]
  51. fn logfile_path() -> PathBuf {
  52. dirs::cache_dir().unwrap().join("darkfi/darkfi-app.log")
  53. }
  54. #[cfg(target_os = "android")]
  55. mod android {
  56. use super::*;
  57. use android_logger::{AndroidLogger, Config as AndroidConfig};
  58. /// Implements a wrapper around the android logger so it's compatible with simplelog.
  59. pub struct AndroidLoggerWrapper {
  60. logger: AndroidLogger,
  61. level: LevelFilter,
  62. config: Config,
  63. }
  64. impl AndroidLoggerWrapper {
  65. pub fn new(level: LevelFilter, config: Config) -> Box<Self> {
  66. let cfg = AndroidConfig::default().with_max_level(level).with_tag("darkfi");
  67. Box::new(Self { logger: AndroidLogger::new(cfg), level, config })
  68. }
  69. }
  70. impl Log for AndroidLoggerWrapper {
  71. fn enabled(&self, metadata: &Metadata<'_>) -> bool {
  72. let target = metadata.target();
  73. for allow in ALLOW_TRACE {
  74. if target.starts_with(allow) {
  75. return true
  76. }
  77. }
  78. for muted in MUTED_TARGETS {
  79. if target.starts_with(muted) {
  80. return false
  81. }
  82. }
  83. if metadata.level() > self.level {
  84. return false
  85. }
  86. self.logger.enabled(metadata)
  87. }
  88. fn log(&self, record: &Record<'_>) {
  89. if self.enabled(record.metadata()) {
  90. self.logger.log(record)
  91. }
  92. }
  93. fn flush(&self) {}
  94. }
  95. impl SharedLogger for AndroidLoggerWrapper {
  96. fn level(&self) -> LevelFilter {
  97. self.level
  98. }
  99. fn config(&self) -> Option<&Config> {
  100. Some(&self.config)
  101. }
  102. fn as_log(self: Box<Self>) -> Box<dyn Log> {
  103. Box::new(*self)
  104. }
  105. }
  106. }
  107. #[cfg(not(target_os = "android"))]
  108. mod desktop {
  109. use super::*;
  110. /// Implements a wrapper around the android logger so it's compatible with simplelog.
  111. pub struct CustomTermLogger {
  112. logger: TermLogger,
  113. }
  114. impl CustomTermLogger {
  115. pub fn new(level: LevelFilter, cfg: Config) -> Box<Self> {
  116. let logger =
  117. TermLogger::new(LevelFilter::Trace, cfg, TerminalMode::Mixed, ColorChoice::Auto);
  118. Box::new(Self { logger: *logger })
  119. }
  120. }
  121. impl Log for CustomTermLogger {
  122. fn enabled(&self, metadata: &Metadata<'_>) -> bool {
  123. let target = metadata.target();
  124. for allow in ALLOW_TRACE {
  125. if target.starts_with(allow) {
  126. return true
  127. }
  128. }
  129. for muted in MUTED_TARGETS {
  130. if target.starts_with(muted) && metadata.level() > LevelFilter::Info {
  131. return false
  132. }
  133. }
  134. if metadata.level() > self.level() {
  135. return false
  136. }
  137. self.logger.enabled(metadata)
  138. }
  139. fn log(&self, record: &Record<'_>) {
  140. if self.enabled(record.metadata()) {
  141. self.logger.log(record)
  142. }
  143. }
  144. fn flush(&self) {
  145. self.logger.flush()
  146. }
  147. }
  148. impl SharedLogger for CustomTermLogger {
  149. fn level(&self) -> LevelFilter {
  150. self.logger.level()
  151. }
  152. fn config(&self) -> Option<&Config> {
  153. self.logger.config()
  154. }
  155. fn as_log(self: Box<Self>) -> Box<dyn Log> {
  156. Box::new(self.logger).as_log()
  157. }
  158. }
  159. }
  160. pub fn setup_logging() {
  161. // https://gist.github.com/jb-alvarado/6e223936446bb88cd9a93e7028fc2c4f
  162. let mut loggers: Vec<Box<dyn SharedLogger>> = vec![];
  163. let mut cfg = ConfigBuilder::new();
  164. #[cfg(feature = "enable-filelog")]
  165. {
  166. let mut cfg = cfg.clone();
  167. cfg.add_filter_ignore_str("sled");
  168. cfg.add_filter_ignore_str("rustls");
  169. let cfg = cfg.build();
  170. let log_file = FileRotate::new(
  171. logfile_path(),
  172. AppendCount::new(0),
  173. ContentLimit::BytesSurpassed(LOGFILE_MAXSIZE),
  174. Compression::None,
  175. #[cfg(unix)]
  176. None,
  177. );
  178. let file_logger = WriteLogger::new(LevelFilter::Trace, cfg, log_file);
  179. loggers.push(file_logger);
  180. }
  181. let cfg = cfg.build();
  182. #[cfg(target_os = "android")]
  183. {
  184. use android::AndroidLoggerWrapper;
  185. let android_logger = AndroidLoggerWrapper::new(LevelFilter::Debug, cfg);
  186. loggers.push(android_logger);
  187. }
  188. #[cfg(not(target_os = "android"))]
  189. {
  190. use desktop::CustomTermLogger;
  191. // For ANSI colors in the terminal
  192. colored::control::set_override(true);
  193. let term_logger = CustomTermLogger::new(LevelFilter::Debug, cfg);
  194. loggers.push(term_logger);
  195. }
  196. CombinedLogger::init(loggers).expect("logger");
  197. }