Skip to content

Commit afea2bd

Browse files
committed
add support for logging to a file
1 parent b7517cb commit afea2bd

5 files changed

Lines changed: 80 additions & 27 deletions

File tree

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,14 @@ rate_limiter:
4444
max_requests: 4
4545
# Maximum amount of connections that may be accepted within the specified time frame above.
4646
max_connections: 4
47+
48+
# Logging configuration
49+
logging:
50+
# Path to the log file, or null to disable
51+
file: "/var/log/pwmp-server.log" # or null to disable
52+
53+
# Whether to erase the log file on start
54+
erase_file_on_start: false
4755
```
4856
4957
## Database server

src/error.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,4 +77,12 @@ pub enum Error {
7777
/// Failed to get current username.
7878
#[error("Failed to get current username: {0}")]
7979
Whoami(#[from] whoami::Error),
80+
81+
/// Configuration load/store error.
82+
#[error("confy: {0}")]
83+
Config(#[from] confy::ConfyError),
84+
85+
/// The log file path is not absolute.
86+
#[error("Path to the log file must be absolute")]
87+
IllegalLogfilePath,
8088
}

src/logging.rs

Lines changed: 44 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
1-
use crate::error::Error;
2-
use std::time::SystemTime;
1+
use crate::{error::Error, server::config::Config};
2+
use std::{fs, time::SystemTime};
33
use tracing::level_filters::LevelFilter;
4-
use tracing_subscriber::fmt::{
5-
format::{FmtSpan, Writer},
6-
time::FormatTime,
4+
use tracing_subscriber::{
5+
fmt::{
6+
format::{FmtSpan, Writer},
7+
time::FormatTime,
8+
},
9+
layer::SubscriberExt,
10+
util::SubscriberInitExt,
711
};
812

913
struct DateTimeFormatter;
@@ -16,21 +20,49 @@ impl FormatTime for DateTimeFormatter {
1620
}
1721
}
1822

19-
pub fn setup(debug: bool) -> Result<(), Error> {
20-
let level = if cfg!(debug_assertions) | debug {
23+
pub fn setup(force_debug: bool, config: &Config) -> Result<(), Error> {
24+
let level = if cfg!(debug_assertions) | force_debug {
2125
LevelFilter::DEBUG
2226
} else {
2327
LevelFilter::INFO
2428
};
2529

26-
let subscriber = tracing_subscriber::fmt()
30+
let file_layer = if let Some(path) = &config.logging.file {
31+
if !path.is_absolute() {
32+
return Err(Error::IllegalLogfilePath);
33+
}
34+
35+
let file = fs::OpenOptions::new()
36+
.create(true)
37+
.write(true)
38+
.truncate(config.logging.erase_file_on_start)
39+
.append(!config.logging.erase_file_on_start)
40+
.open(path)?;
41+
42+
let file_layer = tracing_subscriber::fmt::layer()
43+
.with_writer(file)
44+
.with_ansi(false)
45+
.compact()
46+
.with_timer(DateTimeFormatter)
47+
.with_target(false)
48+
.with_span_events(FmtSpan::CLOSE);
49+
50+
Some(file_layer)
51+
} else {
52+
None
53+
};
54+
55+
let stdout_layer = tracing_subscriber::fmt::layer()
2756
.compact()
2857
.with_timer(DateTimeFormatter)
2958
.with_target(false)
30-
.with_max_level(level)
31-
.with_span_events(FmtSpan::CLOSE)
32-
.finish();
33-
tracing::subscriber::set_global_default(subscriber)?;
59+
.with_span_events(FmtSpan::CLOSE);
60+
61+
tracing_subscriber::registry()
62+
.with(stdout_layer)
63+
.with(file_layer)
64+
.with(level)
65+
.init();
3466

3567
Ok(())
3668
}

src/main.rs

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@ use clap::Parser;
22
use cli::Command;
33
use server::config::Config;
44
use sqlx::migrate::Migrator;
5-
use std::process::exit;
6-
use tracing::{debug, error, info};
5+
use tracing::{debug, info};
76

87
mod cli;
98
mod dbmgr;
@@ -18,24 +17,14 @@ pub static MIGRATOR: Migrator = sqlx::migrate!();
1817
#[tokio::main]
1918
async fn main() -> Result<(), error::Error> {
2019
let args = cli::Cli::parse();
20+
let config_path = args.config.clone().unwrap_or_else(Config::default_path);
21+
let (config, first_run) = server::config::setup(&config_path)?;
2122

22-
logging::setup(args.debug)?;
23+
logging::setup(args.debug, &config)?;
2324

2425
info!("PixelWeather Server v{}", env!("CARGO_PKG_VERSION"));
2526
debug!("Arguments: {args:?}");
2627

27-
let config_path = args.config.unwrap_or_else(Config::default_path);
28-
info!("Loading config from {}", config_path.display());
29-
30-
let first_run = !config_path.exists();
31-
let config: Config = match confy::load_path(&config_path) {
32-
Ok(config) => config,
33-
Err(why) => {
34-
error!("Failed to load configuration: {why}");
35-
exit(1);
36-
}
37-
};
38-
3928
if first_run {
4029
info!("Configuration initialized at {}", config_path.display());
4130
return Ok(());

src/server/config.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ use std::{
88
time::Duration,
99
};
1010

11+
use crate::error::Error;
12+
1113
#[serde_as]
1214
#[derive(Debug, Serialize, Deserialize, Default)]
1315
pub struct Config {
@@ -16,6 +18,7 @@ pub struct Config {
1618
pub limits: LimitsConfig,
1719
#[serde(rename = "rate_limiter")]
1820
pub rate_limits: RateLimitConfig,
21+
pub logging: LogConfig,
1922
}
2023

2124
#[derive(Debug, Serialize, Deserialize)]
@@ -51,6 +54,12 @@ pub struct RateLimitConfig {
5154
pub max_connections: usize,
5255
}
5356

57+
#[derive(Debug, Serialize, Deserialize, Default)]
58+
pub struct LogConfig {
59+
pub file: Option<PathBuf>,
60+
pub erase_file_on_start: bool,
61+
}
62+
5463
impl Default for RateLimitConfig {
5564
fn default() -> Self {
5665
Self {
@@ -106,3 +115,10 @@ impl Config {
106115
SocketAddrV4::new(self.server.host, self.server.port)
107116
}
108117
}
118+
119+
pub fn setup(config_path: &PathBuf) -> Result<(Config, bool), Error> {
120+
let first_run = !config_path.exists();
121+
let config: Config = confy::load_path(config_path)?;
122+
123+
Ok((config, first_run))
124+
}

0 commit comments

Comments
 (0)