feat: add log-file option (#383)
This commit is contained in:
parent
cb7d417fd3
commit
6b6d69a8ef
5 changed files with 64 additions and 16 deletions
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
14
src/args.rs
14
src/args.rs
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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(())
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
|
|
Loading…
Reference in a new issue