1#[cfg(all(not(debug_assertions)))]
2use std::fs::{create_dir, metadata};
3
4#[cfg(target_os = "android")]
5use std::ffi::{c_char, c_int};
6
7use fern::Dispatch;
8use log::LevelFilter;
9use thiserror::Error;
10
11#[cfg(not(debug_assertions))]
12use chrono::Local;
13
14#[cfg(debug_assertions)]
15use fern::colors::{Color, ColoredLevelConfig};
16
17#[cfg(all(not(debug_assertions)))]
18use fern::DateBased;
19
20#[derive(Debug, Error)]
21pub enum LoggerInitError {
22 #[error(transparent)]
23 LogError(#[from] log::SetLoggerError),
24 #[error(transparent)]
25 IoError(#[from] std::io::Error),
26}
27
28#[allow(unused_variables)]
29pub fn init_logger(level: LevelFilter, path: Option<&str>) -> Result<(), LoggerInitError> {
30 let mut logger = Dispatch::new()
31 .level(level)
32 .level_for("wgpu", LevelFilter::Warn)
33 .level_for("wgpu_core", LevelFilter::Warn)
34 .level_for("wgpu_hal", LevelFilter::Warn)
35 .level_for("wgpu_hal::auxil::dxgi::exception", LevelFilter::Error);
36
37 #[cfg(debug_assertions)]
38 {
39 let colors = ColoredLevelConfig::new()
40 .info(Color::Blue)
41 .warn(Color::Yellow)
42 .error(Color::Red);
43
44 logger = logger
45 .format(move |out, message, record| {
46 out.finish(format_args!(
47 "[{}] - ({}) - {}",
48 colors.color(record.level()),
49 record.file_static().unwrap_or("*"),
50 message
51 ))
52 })
53 .chain(std::io::stdout());
54 }
55
56 #[cfg(not(debug_assertions))]
57 {
58 logger = logger.format(move |out, message, record| {
59 out.finish(format_args!(
60 "{} - [{}] - ({}) - {}",
61 Local::now().format("%m-%d %H:%M:%S"),
62 record.level(),
63 record.file_static().unwrap_or("*"),
64 message
65 ))
66 });
67
68 if let Some(path) = path {
69 if metadata(path).is_err() {
70 create_dir(path)?;
71 }
72
73 logger = logger.chain(DateBased::new(path, "%Y-%m-%d-mirror.log"))
74 } else {
75 logger = logger.chain(std::io::stdout());
76 }
77 }
78
79 logger.apply()?;
80
81 #[cfg(not(debug_assertions))]
82 std::panic::set_hook(Box::new(|info| {
83 log::error!(
84 "pnaic: location={:?}, message={:?}",
85 info.location(),
86 info.payload().downcast_ref::<String>(),
87 );
88 }));
89
90 Ok(())
91}
92
93#[repr(C)]
94#[derive(Debug, Clone, Copy, PartialEq, Eq)]
95pub enum AndroidLogLevel {
96 Verbose = 2,
97 Debug,
98 Info,
99 Warn,
100 Error,
101}
102
103impl AndroidLogLevel {
104 pub fn from_level(level: log::Level) -> Self {
105 match level {
106 log::Level::Trace => Self::Verbose,
107 log::Level::Debug => Self::Debug,
108 log::Level::Info => Self::Info,
109 log::Level::Warn => Self::Warn,
110 log::Level::Error => Self::Error,
111 }
112 }
113}
114
115#[cfg(target_os = "android")]
116extern "C" {
117 #[link_name = "__android_log_write"]
128 fn android_log_write(prio: c_int, tag: *const c_char, text: *const c_char) -> c_int;
129}
130
131pub struct AndroidLogger;
132
133impl log::Log for AndroidLogger {
134 fn flush(&self) {}
135 fn enabled(&self, _: &log::Metadata) -> bool {
136 true
137 }
138
139 #[allow(unused_variables)]
140 fn log(&self, record: &log::Record) {
141 #[cfg(target_os = "android")]
142 unsafe {
143 android_log_write(
144 AndroidLogLevel::from_level(record.level()) as c_int,
145 "com.github.mycrl.mirror\0".as_ptr() as *const _,
146 format!("{}\0", record.args()).as_ptr() as *const _,
147 );
148 }
149 }
150}
151
152pub fn init_with_android(level: LevelFilter) {
153 log::set_boxed_logger(Box::new(AndroidLogger)).unwrap();
154 log::set_max_level(level);
155
156 #[cfg(not(debug_assertions))]
157 std::panic::set_hook(Box::new(|info| {
158 log::error!(
159 "pnaic: location={:?}, message={:?}",
160 info.location(),
161 info.payload().downcast_ref::<String>(),
162 );
163 }));
164}