Skip to content

Commit 9322a13

Browse files
committed
feat: add separate log files for debug and trace
1 parent 3f55fb7 commit 9322a13

File tree

5 files changed

+128
-52
lines changed

5 files changed

+128
-52
lines changed

src/cli.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ enum Commands {
7575

7676
pub fn run() {
7777
if let Err(e) = setup_logger() {
78-
eprintln!("Could not setup logger {}", e);
78+
eprintln!("Could not setup logger: {}", e);
7979
process::exit(1);
8080
}
8181

src/commands.rs

+14-1
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@ use crate::packages::Package;
33
use crate::runner::run;
44
use crate::shims::{add_shim, remove_shim};
55
use colored::*;
6-
use log::info;
6+
use log::{debug, info};
77
use std::env;
88
use std::error::Error;
99

1010
pub fn show_info() -> Result<(), Box<dyn Error>> {
11+
debug!("Showing info");
1112
info!("\n{}:", "System Information".bold().underline());
1213
info!("{}:", "OS Details".bold());
1314
info!(" Name : {}", env::consts::OS.bright_blue());
@@ -27,6 +28,7 @@ pub fn show_info() -> Result<(), Box<dyn Error>> {
2728
}
2829

2930
pub fn list_packages(name: Option<&str>, verbose: bool) -> Result<(), Box<dyn Error>> {
31+
debug!("Listing packages");
3032
if let Some(name) = name {
3133
if let Some(package) = Package::load(name)? {
3234
package.print(verbose);
@@ -52,6 +54,10 @@ pub fn list_packages(name: Option<&str>, verbose: bool) -> Result<(), Box<dyn Er
5254
}
5355

5456
pub fn add_package(name: String, version: String, set_default: bool) -> Result<(), Box<dyn Error>> {
57+
debug!(
58+
"Adding package '{}' version '{}'. Set as default? = {}",
59+
&name, &version, set_default
60+
);
5561
if let Some(mut package) = Package::load(&name)? {
5662
if package.versions.versions.contains(&version) {
5763
return Err(format!("'{}' version {} already exists.", name, version).into());
@@ -79,6 +85,11 @@ pub fn add_package(name: String, version: String, set_default: bool) -> Result<(
7985
}
8086

8187
pub fn remove_package(name: String, version: Option<String>) -> Result<(), Box<dyn Error>> {
88+
debug!(
89+
"Removing version '{}' of '{}'",
90+
version.clone().unwrap_or_else(|| "<all>".to_owned()),
91+
&name
92+
);
8293
match (Package::load(&name)?, version) {
8394
(Some(mut package), Some(version)) => {
8495
if package.versions.current == version && package.versions.versions.len() > 1 {
@@ -113,6 +124,7 @@ pub fn remove_package(name: String, version: Option<String>) -> Result<(), Box<d
113124
}
114125

115126
pub fn set_package_version(name: String, version: String) -> Result<(), Box<dyn Error>> {
127+
debug!("Setting package '{}' to version '{}'", &name, &version);
116128
if let Some(mut package) = Package::load(&name)? {
117129
if package.versions.versions.contains(&version) {
118130
package.versions.current = version.clone();
@@ -132,6 +144,7 @@ pub fn set_package_version(name: String, version: String) -> Result<(), Box<dyn
132144
}
133145

134146
pub fn run_package(name: String, subcommand: Vec<String>) -> Result<(), Box<dyn Error>> {
147+
debug!("Running {} {}", &name, &subcommand.join(" "));
135148
let parts: Vec<&str> = name.split("::").collect();
136149
let (package_name, binary) = match parts.as_slice() {
137150
[package_name] => (package_name.to_string(), None),

src/files/config.rs

+20-3
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,31 @@ use serde::{Deserialize, Serialize};
22
use std::error::Error;
33

44
use crate::files::variables::AppConfig;
5-
use crate::serialization::parse_json;
5+
use crate::serialization::{parse_json, save_json};
66

77
#[derive(Serialize, Deserialize, Debug)]
88
pub struct Root {
9-
pub log_level: String,
9+
pub logs: Logs,
10+
}
11+
12+
#[derive(Serialize, Deserialize, Debug)]
13+
pub struct Logs {
14+
pub level: String,
15+
pub strategy: String,
1016
}
1117

1218
pub fn get_config() -> Result<Root, Box<dyn Error>> {
1319
let config = AppConfig::new();
14-
parse_json(&config.config_path())
20+
if config.config_path().exists() {
21+
parse_json(&config.config_path())
22+
} else {
23+
let root = Root {
24+
logs: Logs {
25+
level: "info".to_owned(),
26+
strategy: "append".to_owned(),
27+
},
28+
};
29+
save_json(&root, &config.config_path())?;
30+
Ok(root)
31+
}
1532
}

src/files/variables.rs

+4
Original file line numberDiff line numberDiff line change
@@ -42,4 +42,8 @@ impl AppConfig {
4242
pub fn shims_path(&self) -> PathBuf {
4343
self.base_dir.join("shims")
4444
}
45+
46+
pub fn logs_path(&self) -> PathBuf {
47+
self.base_dir.join("logs/log.txt")
48+
}
4549
}

src/logging.rs

+89-47
Original file line numberDiff line numberDiff line change
@@ -1,73 +1,115 @@
11
use crate::files::config::get_config;
2-
use colored::Colorize;
3-
use env_logger::{Builder, Target};
4-
use log::Level;
5-
use log::LevelFilter;
6-
use std::env;
2+
use crate::files::variables::AppConfig;
3+
use chrono::Local;
4+
use log::{Level, LevelFilter, Log, Metadata, Record};
75
use std::error::Error;
6+
use std::fs::{File, OpenOptions};
87
use std::io::Write;
8+
use std::sync::{Arc, Mutex};
99

1010
pub fn setup_logger() -> Result<(), Box<dyn Error>> {
11-
let log_level = match get_config() {
12-
Ok(config) => match config.log_level.to_lowercase().as_str() {
13-
"info" => LevelFilter::Info,
14-
"warn" => LevelFilter::Warn,
15-
"debug" => LevelFilter::Debug,
16-
"trace" => LevelFilter::Trace,
17-
_ => LevelFilter::Error,
18-
},
19-
Err(_) => LevelFilter::Error,
11+
let (log_level, log_strategy) = if let Ok(config) = get_config() {
12+
(
13+
match config.logs.level.to_lowercase().as_str() {
14+
"info" => LevelFilter::Info,
15+
"warn" => LevelFilter::Warn,
16+
"debug" => LevelFilter::Debug,
17+
"trace" => LevelFilter::Trace,
18+
_ => LevelFilter::Error,
19+
},
20+
config.logs.strategy,
21+
)
22+
} else {
23+
(LevelFilter::Info, "truncate".to_string())
2024
};
2125

22-
let mut builder = Builder::new();
26+
let config = AppConfig::new();
27+
let log_file_path = config.logs_path();
2328

24-
// Log the errors to stderr
25-
builder.filter_level(LevelFilter::Error);
26-
builder.target(Target::Stderr);
29+
// Ensure the logs directory exists
30+
if let Some(parent) = log_file_path.parent() {
31+
std::fs::create_dir_all(parent)?;
32+
}
33+
34+
// Open or create the log file
35+
let file = OpenOptions::new()
36+
.create(true)
37+
.write(true)
38+
.truncate(log_strategy == "truncate")
39+
.append(log_strategy == "append")
40+
.open(log_file_path)?;
41+
42+
let file = Arc::new(Mutex::new(file));
43+
44+
// Set the logger
45+
log::set_boxed_logger(Box::new(Logger {
46+
file: file.clone(),
47+
level: log_level,
48+
}))?;
49+
log::set_max_level(log_level);
50+
51+
Ok(())
52+
}
53+
54+
struct Logger {
55+
file: Arc<Mutex<File>>,
56+
level: LevelFilter,
57+
}
58+
59+
impl Log for Logger {
60+
fn enabled(&self, metadata: &Metadata) -> bool {
61+
metadata.level() <= self.level
62+
}
63+
64+
fn log(&self, record: &Record) {
65+
if !self.enabled(record.metadata()) {
66+
return;
67+
}
68+
69+
let now = Local::now();
70+
let timestamp = now.format("%Y-%m-%d %H:%M:%S%.3f");
2771

28-
// Customize format
29-
builder.format(move |buf, record| {
3072
let file_info = match (record.file(), record.line()) {
3173
(Some(file), Some(line)) => format!("{}:{}", file, line),
3274
_ => String::from("unknown"),
3375
};
3476

35-
let now = chrono::Local::now();
36-
let timestamp = now.format("%Y-%m-%d %H:%M:%S%.3f");
77+
let log_line = format!(
78+
"[{} {} {}] - {}",
79+
timestamp,
80+
record.level(),
81+
file_info,
82+
record.args()
83+
);
84+
85+
if self.level <= LevelFilter::Debug
86+
&& (record.level() == Level::Info
87+
|| record.level() == Level::Warn
88+
|| record.level() == Level::Error)
89+
{
90+
let mut file = self.file.lock().unwrap();
91+
writeln!(file, "{}", log_line).unwrap();
92+
}
3793

3894
match record.level() {
39-
Level::Trace => {
40-
let log_line = format!("[{} TRACE {}] - {}", timestamp, file_info, record.args());
41-
writeln!(buf, "{}", log_line.italic())
42-
}
43-
Level::Debug => {
44-
let log_line = format!("[{} DEBUG {}] - {}", timestamp, file_info, record.args());
45-
writeln!(buf, "{}", log_line.italic())
95+
Level::Trace | Level::Debug => {
96+
let mut file = self.file.lock().unwrap();
97+
writeln!(file, "{}", log_line).unwrap();
4698
}
4799
Level::Info => {
48-
let log_line = format!("{}", record.args());
49-
writeln!(buf, "{}", log_line)
100+
println!("{}", record.args());
50101
}
51102
Level::Warn => {
52-
let log_line = format!("{}", record.args());
53-
writeln!(buf, "{}", log_line.underline())
103+
println!("{}", record.args());
54104
}
55105
Level::Error => {
56-
let log_line = format!("{}", record.args());
57-
writeln!(buf, "{}", log_line.red().bold())
106+
eprintln!("{}", record.args());
58107
}
59108
}
60-
});
109+
}
61110

62-
// Log all other stuff to stdout
63-
builder.filter(Some("hbox"), log_level);
64-
builder.target(Target::Stdout);
65-
66-
if env::var("RUST_LOG").is_ok() {
67-
builder.parse_filters(&env::var("RUST_LOG").unwrap());
68-
};
69-
70-
builder.init();
71-
72-
Ok(())
111+
fn flush(&self) {
112+
let mut file = self.file.lock().unwrap();
113+
file.flush().unwrap();
114+
}
73115
}

0 commit comments

Comments
 (0)