]> OzVa Git service - ozva-cloud/commitdiff
feat: add log-file option (#383)
authorsigoden <sigoden@gmail.com>
Sat, 11 May 2024 09:13:31 +0000 (17:13 +0800)
committerGitHub <noreply@github.com>
Sat, 11 May 2024 09:13:31 +0000 (17:13 +0800)
Cargo.toml
README.md
src/args.rs
src/logger.rs
src/main.rs

index b38095cbe9f3cb544005262a31d5f3ec7cd6c5d6..f4620f8ab0167a6a4727b9c41a151cf65e0c55b1 100644 (file)
@@ -32,7 +32,7 @@ lazy_static = "1.4"
 uuid = { version = "1.7", features = ["v4", "fast-rng"] }
 urlencoding = "2.1"
 xml-rs = "0.8"
-log = "0.4"
+log = { version = "0.4", features = ["std"] }
 socket2 = "0.5"
 async-stream = "0.3"
 walkdir = "2.3"
index 5eb2949fadcf0767c63cfbc52a6d4b0d8e8ce057..0982c2f3c51525a541b3f52ba2154e2907c6ea36 100644 (file)
--- a/README.md
+++ b/README.md
@@ -73,6 +73,7 @@ Options:
       --render-spa           Serve SPA(Single Page Application)
       --assets <path>        Set the path to the assets directory for overriding the built-in assets
       --log-format <format>  Customize http log format
+      --log-file <file>      Specify the file to save logs to, other than stdout/stderr
       --compress <level>     Set zip compress level [default: low] [possible values: none, low, medium, high]
       --completions <shell>  Print shell completion script for <shell> [possible values: bash, elvish, fish, powershell, zsh]
       --tls-cert <path>      Path to an SSL/TLS certificate to serve with HTTPS
@@ -329,7 +330,7 @@ All options can be set using environment variables prefixed with `DUFS_`.
     --config <file>         DUFS_CONFIG=config.yaml
 -b, --bind <addrs>          DUFS_BIND=0.0.0.0
 -p, --port <port>           DUFS_PORT=5000
-    --path-prefix <path>    DUFS_PATH_PREFIX=/static
+    --path-prefix <path>    DUFS_PATH_PREFIX=/dufs
     --hidden <value>        DUFS_HIDDEN=tmp,*.log,*.lock
 -a, --auth <rules>          DUFS_AUTH="admin:admin@/:rw|@/" 
 -A, --allow-all             DUFS_ALLOW_ALL=true
@@ -342,9 +343,10 @@ All options can be set using environment variables prefixed with `DUFS_`.
     --render-index          DUFS_RENDER_INDEX=true
     --render-try-index      DUFS_RENDER_TRY_INDEX=true
     --render-spa            DUFS_RENDER_SPA=true
-    --assets <path>         DUFS_ASSETS=/assets
+    --assets <path>         DUFS_ASSETS=./assets
     --log-format <format>   DUFS_LOG_FORMAT=""
-    --compress <compress>   DUFS_COMPRESS="low"
+    --log-file <file>       DUFS_LOG_FILE=./dufs.log
+    --compress <compress>   DUFS_COMPRESS=low
     --tls-cert <path>       DUFS_TLS_CERT=cert.pem
     --tls-key <path>        DUFS_TLS_KEY=key.pem
 ```
@@ -380,6 +382,7 @@ render-try-index: true
 render-spa: true
 assets: ./assets/
 log-format: '$remote_addr "$request" $status $http_user_agent'
+log-file: ./dufs.log
 compress: low
 tls-cert: tests/data/cert.pem
 tls-key: tests/data/key_pkcs1.pem
index f26dbcaf64117b37cfe568d41bdf2b046989d3cb..78aa0809d29812c4eae4a827f2845a7a3da8751f 100644 (file)
@@ -197,6 +197,15 @@ pub fn build_cli() -> Command {
                 .value_name("format")
                 .help("Customize http log format"),
         )
+        .arg(
+            Arg::new("log-file")
+                .env("DUFS_LOG_FILE")
+                .hide_env(true)
+                .long("log-file")
+                .value_name("file")
+                .value_parser(value_parser!(PathBuf))
+                .help("Specify the file to save logs to, other than stdout/stderr"),
+        )
         .arg(
             Arg::new("compress")
                 .env("DUFS_COMPRESS")
@@ -280,6 +289,7 @@ pub struct Args {
     #[serde(deserialize_with = "deserialize_log_http")]
     #[serde(rename = "log-format")]
     pub http_logger: HttpLogger,
+    pub log_file: Option<PathBuf>,
     pub compress: Compress,
     pub tls_cert: Option<PathBuf>,
     pub tls_key: Option<PathBuf>,
@@ -392,6 +402,10 @@ impl Args {
             args.http_logger = log_format.parse()?;
         }
 
+        if let Some(log_file) = matches.get_one::<PathBuf>("log-file") {
+            args.log_file = Some(log_file.clone());
+        }
+
         if let Some(compress) = matches.get_one::<Compress>("compress") {
             args.compress = *compress;
         }
index bfa808e2f8b1773e18999a729da617242da6bdea..d60f35c93f29b13fff54ac5f1151a0ca00591f9c 100644 (file)
@@ -1,8 +1,14 @@
+use anyhow::{Context, Result};
 use chrono::{Local, SecondsFormat};
-use log::{Level, Metadata, Record};
-use log::{LevelFilter, SetLoggerError};
+use log::{Level, LevelFilter, Metadata, Record};
+use std::fs::{File, OpenOptions};
+use std::io::Write;
+use std::path::PathBuf;
+use std::sync::Mutex;
 
-struct SimpleLogger;
+struct SimpleLogger {
+    file: Option<Mutex<File>>,
+}
 
 impl log::Log for SimpleLogger {
     fn enabled(&self, metadata: &Metadata) -> bool {
@@ -12,10 +18,20 @@ impl log::Log for SimpleLogger {
     fn log(&self, record: &Record) {
         if self.enabled(record.metadata()) {
             let timestamp = Local::now().to_rfc3339_opts(SecondsFormat::Secs, true);
-            if record.level() < Level::Info {
-                eprintln!("{} {} - {}", timestamp, record.level(), record.args());
-            } else {
-                println!("{} {} - {}", timestamp, record.level(), record.args());
+            let text = format!("{} {} - {}", timestamp, record.level(), record.args());
+            match &self.file {
+                Some(file) => {
+                    if let Ok(mut file) = file.lock() {
+                        let _ = writeln!(file, "{text}");
+                    }
+                }
+                None => {
+                    if record.level() < Level::Info {
+                        eprintln!("{text}");
+                    } else {
+                        println!("{text}");
+                    }
+                }
             }
         }
     }
@@ -23,8 +39,23 @@ impl log::Log for SimpleLogger {
     fn flush(&self) {}
 }
 
-static LOGGER: SimpleLogger = SimpleLogger;
-
-pub fn init() -> Result<(), SetLoggerError> {
-    log::set_logger(&LOGGER).map(|()| log::set_max_level(LevelFilter::Info))
+pub fn init(log_file: Option<PathBuf>) -> Result<()> {
+    let file = match log_file {
+        None => None,
+        Some(log_file) => {
+            let file = OpenOptions::new()
+                .create(true)
+                .append(true)
+                .open(&log_file)
+                .with_context(|| {
+                    format!("Failed to open the log file at '{}'", log_file.display())
+                })?;
+            Some(Mutex::new(file))
+        }
+    };
+    let logger = SimpleLogger { file };
+    log::set_boxed_logger(Box::new(logger))
+        .map(|_| log::set_max_level(LevelFilter::Info))
+        .with_context(|| "Failed to init logger")?;
+    Ok(())
 }
index 298de0429ba174453622722e614a891e47ccf10c..b32029b176e736a4826c1fdbbef36b566b748423 100644 (file)
@@ -37,7 +37,6 @@ use tokio_rustls::{rustls::ServerConfig, TlsAcceptor};
 
 #[tokio::main]
 async fn main() -> Result<()> {
-    logger::init().map_err(|e| anyhow!("Failed to init logger, {e}"))?;
     let cmd = build_cli();
     let matches = cmd.get_matches();
     if let Some(generator) = matches.get_one::<Shell>("completions") {
@@ -46,6 +45,7 @@ async fn main() -> Result<()> {
         return Ok(());
     }
     let mut args = Args::parse(matches)?;
+    logger::init(args.log_file.clone()).map_err(|e| anyhow!("Failed to init logger, {e}"))?;
     let (new_addrs, print_addrs) = check_addrs(&args)?;
     args.addrs = new_addrs;
     let running = Arc::new(AtomicBool::new(true));