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
|
use log::{Level, LevelFilter, Metadata, Record, SetLoggerError};
use once_cell::sync::Lazy;
use std::path::{Path, PathBuf};
use test_rpc::logging::Error;
use test_rpc::logging::{LogFile, LogOutput, Output};
use tokio::{
fs::read_to_string,
sync::{
broadcast::{channel, Receiver, Sender},
Mutex,
},
};
const MAX_OUTPUT_BUFFER: usize = 10_000;
pub static LOGGER: Lazy<StdOutBuffer> = Lazy::new(|| {
let (sender, listener) = channel(MAX_OUTPUT_BUFFER);
StdOutBuffer(Mutex::new(listener), sender)
});
pub struct StdOutBuffer(pub Mutex<Receiver<Output>>, pub Sender<Output>);
impl log::Log for StdOutBuffer {
fn enabled(&self, metadata: &Metadata) -> bool {
metadata.level() <= Level::Info
}
fn log(&self, record: &Record) {
if self.enabled(record.metadata()) {
match record.metadata().level() {
Level::Error => {
self.1
.send(Output::Error(format!("{}", record.args())))
.unwrap();
}
Level::Warn => {
self.1
.send(Output::Warning(format!("{}", record.args())))
.unwrap();
}
Level::Info => {
if !record.metadata().target().contains("tarpc") {
self.1
.send(Output::Info(format!("{}", record.args())))
.unwrap();
}
}
_ => (),
}
println!("{}", record.args());
}
}
fn flush(&self) {}
}
pub fn init_logger() -> Result<(), SetLoggerError> {
log::set_logger(&*LOGGER).map(|()| log::set_max_level(LevelFilter::Info))
}
pub async fn get_mullvad_app_logs() -> LogOutput {
LogOutput {
settings_json: read_settings_file().await,
log_files: read_log_files().await,
}
}
async fn read_settings_file() -> Result<String, Error> {
let mut settings_path = mullvad_paths::get_default_settings_dir()
.map_err(|error| Error::Logs(format!("{}", error)))?;
settings_path.push("settings.json");
read_to_string(&settings_path)
.await
.map_err(|error| Error::Logs(format!("{}: {}", settings_path.display(), error)))
}
async fn read_log_files() -> Result<Vec<Result<LogFile, Error>>, Error> {
let log_dir =
mullvad_paths::get_default_log_dir().map_err(|error| Error::Logs(format!("{}", error)))?;
let paths = list_logs(log_dir)
.await
.map_err(|error| Error::Logs(format!("{}", error)))?;
let mut log_files = Vec::new();
for path in paths {
let log_file = read_to_string(&path)
.await
.map_err(|error| Error::Logs(format!("{}: {}", path.display(), error)))
.map(|content| LogFile {
content,
name: path,
});
log_files.push(log_file);
}
Ok(log_files)
}
async fn list_logs<T: AsRef<Path>>(log_dir: T) -> Result<Vec<PathBuf>, Error> {
let mut dir_entries = tokio::fs::read_dir(&log_dir)
.await
.map_err(|e| Error::Logs(format!("{}: {}", log_dir.as_ref().display(), e)))?;
let log_extension = Some(std::ffi::OsStr::new("log"));
let mut paths = Vec::new();
while let Ok(Some(entry)) = dir_entries.next_entry().await {
let path = entry.path();
if path.extension() == log_extension {
paths.push(path);
}
}
Ok(paths)
}
|