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"] }
|
uuid = { version = "1.7", features = ["v4", "fast-rng"] }
|
||||||
urlencoding = "2.1"
|
urlencoding = "2.1"
|
||||||
xml-rs = "0.8"
|
xml-rs = "0.8"
|
||||||
log = "0.4"
|
log = { version = "0.4", features = ["std"] }
|
||||||
socket2 = "0.5"
|
socket2 = "0.5"
|
||||||
async-stream = "0.3"
|
async-stream = "0.3"
|
||||||
walkdir = "2.3"
|
walkdir = "2.3"
|
||||||
|
|
|
@ -73,6 +73,7 @@ Options:
|
||||||
--render-spa Serve SPA(Single Page Application)
|
--render-spa Serve SPA(Single Page Application)
|
||||||
--assets <path> Set the path to the assets directory for overriding the built-in assets
|
--assets <path> Set the path to the assets directory for overriding the built-in assets
|
||||||
--log-format <format> Customize http log format
|
--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]
|
--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]
|
--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
|
--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
|
--config <file> DUFS_CONFIG=config.yaml
|
||||||
-b, --bind <addrs> DUFS_BIND=0.0.0.0
|
-b, --bind <addrs> DUFS_BIND=0.0.0.0
|
||||||
-p, --port <port> DUFS_PORT=5000
|
-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
|
--hidden <value> DUFS_HIDDEN=tmp,*.log,*.lock
|
||||||
-a, --auth <rules> DUFS_AUTH="admin:admin@/:rw|@/"
|
-a, --auth <rules> DUFS_AUTH="admin:admin@/:rw|@/"
|
||||||
-A, --allow-all DUFS_ALLOW_ALL=true
|
-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-index DUFS_RENDER_INDEX=true
|
||||||
--render-try-index DUFS_RENDER_TRY_INDEX=true
|
--render-try-index DUFS_RENDER_TRY_INDEX=true
|
||||||
--render-spa DUFS_RENDER_SPA=true
|
--render-spa DUFS_RENDER_SPA=true
|
||||||
--assets <path> DUFS_ASSETS=/assets
|
--assets <path> DUFS_ASSETS=./assets
|
||||||
--log-format <format> DUFS_LOG_FORMAT=""
|
--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-cert <path> DUFS_TLS_CERT=cert.pem
|
||||||
--tls-key <path> DUFS_TLS_KEY=key.pem
|
--tls-key <path> DUFS_TLS_KEY=key.pem
|
||||||
```
|
```
|
||||||
|
@ -380,6 +382,7 @@ render-try-index: true
|
||||||
render-spa: true
|
render-spa: true
|
||||||
assets: ./assets/
|
assets: ./assets/
|
||||||
log-format: '$remote_addr "$request" $status $http_user_agent'
|
log-format: '$remote_addr "$request" $status $http_user_agent'
|
||||||
|
log-file: ./dufs.log
|
||||||
compress: low
|
compress: low
|
||||||
tls-cert: tests/data/cert.pem
|
tls-cert: tests/data/cert.pem
|
||||||
tls-key: tests/data/key_pkcs1.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")
|
.value_name("format")
|
||||||
.help("Customize http log 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(
|
||||||
Arg::new("compress")
|
Arg::new("compress")
|
||||||
.env("DUFS_COMPRESS")
|
.env("DUFS_COMPRESS")
|
||||||
|
@ -280,6 +289,7 @@ pub struct Args {
|
||||||
#[serde(deserialize_with = "deserialize_log_http")]
|
#[serde(deserialize_with = "deserialize_log_http")]
|
||||||
#[serde(rename = "log-format")]
|
#[serde(rename = "log-format")]
|
||||||
pub http_logger: HttpLogger,
|
pub http_logger: HttpLogger,
|
||||||
|
pub log_file: Option<PathBuf>,
|
||||||
pub compress: Compress,
|
pub compress: Compress,
|
||||||
pub tls_cert: Option<PathBuf>,
|
pub tls_cert: Option<PathBuf>,
|
||||||
pub tls_key: Option<PathBuf>,
|
pub tls_key: Option<PathBuf>,
|
||||||
|
@ -392,6 +402,10 @@ impl Args {
|
||||||
args.http_logger = log_format.parse()?;
|
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") {
|
if let Some(compress) = matches.get_one::<Compress>("compress") {
|
||||||
args.compress = *compress;
|
args.compress = *compress;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,14 @@
|
||||||
|
use anyhow::{Context, Result};
|
||||||
use chrono::{Local, SecondsFormat};
|
use chrono::{Local, SecondsFormat};
|
||||||
use log::{Level, Metadata, Record};
|
use log::{Level, LevelFilter, Metadata, Record};
|
||||||
use log::{LevelFilter, SetLoggerError};
|
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 {
|
impl log::Log for SimpleLogger {
|
||||||
fn enabled(&self, metadata: &Metadata) -> bool {
|
fn enabled(&self, metadata: &Metadata) -> bool {
|
||||||
|
@ -12,10 +18,20 @@ impl log::Log for SimpleLogger {
|
||||||
fn log(&self, record: &Record) {
|
fn log(&self, record: &Record) {
|
||||||
if self.enabled(record.metadata()) {
|
if self.enabled(record.metadata()) {
|
||||||
let timestamp = Local::now().to_rfc3339_opts(SecondsFormat::Secs, true);
|
let timestamp = Local::now().to_rfc3339_opts(SecondsFormat::Secs, true);
|
||||||
if record.level() < Level::Info {
|
let text = format!("{} {} - {}", timestamp, record.level(), record.args());
|
||||||
eprintln!("{} {} - {}", timestamp, record.level(), record.args());
|
match &self.file {
|
||||||
} else {
|
Some(file) => {
|
||||||
println!("{} {} - {}", timestamp, record.level(), record.args());
|
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) {}
|
fn flush(&self) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
static LOGGER: SimpleLogger = SimpleLogger;
|
pub fn init(log_file: Option<PathBuf>) -> Result<()> {
|
||||||
|
let file = match log_file {
|
||||||
pub fn init() -> Result<(), SetLoggerError> {
|
None => None,
|
||||||
log::set_logger(&LOGGER).map(|()| log::set_max_level(LevelFilter::Info))
|
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]
|
#[tokio::main]
|
||||||
async fn main() -> Result<()> {
|
async fn main() -> Result<()> {
|
||||||
logger::init().map_err(|e| anyhow!("Failed to init logger, {e}"))?;
|
|
||||||
let cmd = build_cli();
|
let cmd = build_cli();
|
||||||
let matches = cmd.get_matches();
|
let matches = cmd.get_matches();
|
||||||
if let Some(generator) = matches.get_one::<Shell>("completions") {
|
if let Some(generator) = matches.get_one::<Shell>("completions") {
|
||||||
|
@ -46,6 +45,7 @@ async fn main() -> Result<()> {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
let mut args = Args::parse(matches)?;
|
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)?;
|
let (new_addrs, print_addrs) = check_addrs(&args)?;
|
||||||
args.addrs = new_addrs;
|
args.addrs = new_addrs;
|
||||||
let running = Arc::new(AtomicBool::new(true));
|
let running = Arc::new(AtomicBool::new(true));
|
||||||
|
|
Loading…
Reference in a new issue