Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ dns-over-https = ["shadowsocks-service/dns-over-https"]
dns-over-h3 = ["shadowsocks-service/dns-over-h3"]

# Enable logging output
logging = ["log4rs", "tracing", "tracing-subscriber", "time"]
logging = ["log4rs", "tracing", "tracing-subscriber", "time", "tracing-appender"]

# Enable DNS-relay
local-dns = ["local", "shadowsocks-service/local-dns"]
Expand Down Expand Up @@ -208,6 +208,7 @@ tracing-subscriber = { version = "0.3", optional = true, features = [
"time",
"local-time",
] }
tracing-appender = { version = "0.2.3", optional = true, default-features = false }
time = { version = "0.3", optional = true }

serde = { version = "1.0", features = ["derive"] }
Expand Down
20 changes: 17 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -877,9 +877,23 @@ Example configuration:
// Euiqvalent to `--log-without-time`
"without_time": false,
},
// Equivalent to `--log-config`
// More detail could be found in https://crates.io/crates/log4rs
"config_path": "/path/to/log4rs/config.yaml"
// File logging configuration (will disable stdout logging)
// This is particularly useful for running as a Windows Service
"file": {
// Directory to store log files. If not set, it will not log to file
"directory": "/var/log/shadowsocks-rust",
// Log rotation frequency, must be one of the following:
// - never (default): This will result in log file located at `directory/prefix.suffix`
// - daily: A new log file in the format of `directory/prefix.yyyy-MM-dd.suffix` will be created daily
// - hourly: A new log file in the format of `directory/prefix.yyyy-MM-dd-HH.suffix` will be created hourly
"rotation": "never",
// Prefix of log file, default is one of `sslocal`, `ssserver`, `ssmanager` depending on the service being run.
"prefix": "shadowsocks-rust",
// Suffix of log file, default is `log`
"suffix": "log",
// Keeps the last N log files. If not set, no limit will be applied.
"max_files": 5
}
},
// Runtime configuration
"runtime": {
Expand Down
67 changes: 67 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,31 @@ impl Config {
nlog.format = nformat;
}

if let Some(file_config) = log.file {
// directory must be configured for file logging
if let Some(directory) = file_config.directory {
let mut nfile = LogFileConfig::new(directory);
if let Some(rotation) = file_config.rotation {
nfile.rotation = match rotation.as_str() {
"never" => tracing_appender::rolling::Rotation::NEVER,
"hourly" => tracing_appender::rolling::Rotation::HOURLY,
"daily" => tracing_appender::rolling::Rotation::DAILY,
_ => return Err(ConfigError::InvalidValue(rotation)),
};
}
if let Some(prefix) = file_config.prefix {
nfile.prefix = Some(prefix);
}
if let Some(suffix) = file_config.suffix {
nfile.suffix = Some(suffix);
}
if let Some(max_files) = file_config.max_files {
nfile.max_files = Some(max_files);
}
nlog.file = Some(nfile);
}
}

if let Some(config_path) = log.config_path {
nlog.config_path = Some(PathBuf::from(config_path));
}
Expand Down Expand Up @@ -210,6 +235,8 @@ pub struct LogConfig {
pub level: u32,
/// Default logger format configuration
pub format: LogFormatConfig,
/// File appender configuration
pub file: Option<LogFileConfig>,
/// Logging configuration file path
pub config_path: Option<PathBuf>,
}
Expand All @@ -221,6 +248,35 @@ pub struct LogFormatConfig {
pub without_time: bool,
}

/// File appender configuration for logging
#[cfg(feature = "logging")]
#[derive(Debug, Clone)]
pub struct LogFileConfig {
/// Directory to store log files
pub directory: PathBuf,
/// Rotation strategy for log files. Default is `Rotation::NEVER`.
pub rotation: tracing_appender::rolling::Rotation,
/// Prefix for log file names. Default is the binary name.
pub prefix: Option<String>,
/// Suffix for log file names. Default is "log".
pub suffix: Option<String>,
/// Maximum number of log files to keep. Default is `None`, meaning no limit.
pub max_files: Option<usize>,
}

#[cfg(feature = "logging")]
impl LogFileConfig {
fn new(directory: impl Into<PathBuf>) -> Self {
Self {
directory: directory.into(),
rotation: tracing_appender::rolling::Rotation::NEVER,
prefix: None,
suffix: None,
max_files: None,
}
}
}

/// Runtime mode (Tokio)
#[derive(Debug, Clone, Copy, Default)]
pub enum RuntimeMode {
Expand Down Expand Up @@ -272,6 +328,7 @@ struct SSConfig {
struct SSLogConfig {
level: Option<u32>,
format: Option<SSLogFormat>,
file: Option<SSLogFileConfig>,
config_path: Option<String>,
}

Expand All @@ -281,6 +338,16 @@ struct SSLogFormat {
without_time: Option<bool>,
}

#[cfg(feature = "logging")]
#[derive(Deserialize)]
struct SSLogFileConfig {
directory: Option<String>,
rotation: Option<String>,
prefix: Option<String>,
suffix: Option<String>,
max_files: Option<usize>,
}

#[derive(Deserialize)]
struct SSRuntimeConfig {
#[cfg(feature = "multi-threaded")]
Expand Down
3 changes: 1 addition & 2 deletions src/logging/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use crate::config::LogConfig;
mod log4rs;
mod tracing;

/// Initialize logger ([log4rs](https://crates.io/crates/log4rs), [trace4rs](https://crates.io/crates/trace4rs)) from yaml configuration file
/// Initialize [log4rs](https://crates.io/crates/log4rs) from yaml configuration file
pub fn init_with_file<P>(path: P)
where
P: AsRef<Path>,
Expand All @@ -25,7 +25,6 @@ where

/// Initialize logger with provided configuration
pub fn init_with_config(bin_name: &str, config: &LogConfig) {
// log4rs::init_with_config(bin_name, config);
tracing::init_with_config(bin_name, config);
}

Expand Down
60 changes: 49 additions & 11 deletions src/logging/tracing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,27 @@

use std::io::IsTerminal;

use time::format_description::well_known::Rfc3339;
use time::UtcOffset;
use tracing::level_filters::LevelFilter;
use tracing_subscriber::{EnvFilter, FmtSubscriber, fmt::time::OffsetTime};
use tracing_appender::rolling::{InitError, RollingFileAppender};
use tracing_subscriber::fmt::format::{DefaultFields, Format, Full};
use tracing_subscriber::fmt::time::OffsetTime;
use tracing_subscriber::fmt::{MakeWriter, SubscriberBuilder};
use tracing_subscriber::{EnvFilter, FmtSubscriber};

use crate::config::LogConfig;
use crate::config::{LogConfig, LogFileConfig};

/// Initialize logger with provided configuration
pub fn init_with_config(bin_name: &str, config: &LogConfig) {
let debug_level = config.level;
let without_time = config.format.without_time;

let mut builder = FmtSubscriber::builder()
.with_level(true)
.with_timer(match OffsetTime::local_rfc_3339() {
Ok(t) => t,
Err(..) => {
// Reinit with UTC time
OffsetTime::new(UtcOffset::UTC, time::format_description::well_known::Rfc3339)
}
});
let mut builder = FmtSubscriber::builder().with_level(true).with_timer(
OffsetTime::local_rfc_3339()
// Fallback to UTC. Eagerly evaluate because it is cheap to create.
.unwrap_or(OffsetTime::new(UtcOffset::UTC, Rfc3339)),
);

// NOTE: ansi is enabled by default.
// Could be disabled by `NO_COLOR` environment variable.
Expand Down Expand Up @@ -74,6 +75,43 @@ pub fn init_with_config(bin_name: &str, config: &LogConfig) {
};
let builder = builder.with_env_filter(filter);

if let Some(ref file_config) = config.file {
let file_writer = make_file_writer(bin_name, file_config)
// don't have the room for a more graceful error handling here
.expect("Failed to create file writer for logging");
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please advice if you think there is a better way to handle this.

init(builder.with_ansi(false).with_writer(file_writer), without_time);
} else {
init(builder, without_time);
}
}

fn make_file_writer(bin_name: &str, config: &LogFileConfig) -> Result<RollingFileAppender, InitError> {
let rotation = config.rotation.clone();
// We provide default values here because we don't have access to the
// `bin_name` elsewhere.
let prefix = config.prefix.as_deref().unwrap_or(bin_name);
let suffix = config.suffix.as_deref().unwrap_or("log");

let mut builder = RollingFileAppender::builder()
.rotation(rotation)
.filename_prefix(prefix)
.filename_suffix(suffix);

if let Some(max_files) = config.max_files {
builder = builder.max_log_files(max_files);
}

builder.build(&config.directory)
}

/// Initialize the logger with the provided builder and options.
///
/// This handles the `without_time` option generically for builders that
/// are configured with different `MakeWriter` concrete types.
fn init<W: for<'writer> MakeWriter<'writer> + Send + Sync + 'static>(
builder: SubscriberBuilder<DefaultFields, Format<Full, OffsetTime<Rfc3339>>, EnvFilter, W>,
without_time: bool,
) {
if without_time {
builder.without_time().init();
} else {
Expand Down
2 changes: 2 additions & 0 deletions src/service/local.rs
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,8 @@ pub fn define_command_line_options(mut app: Command) -> Command {
.arg(
Arg::new("LOG_CONFIG")
.long("log-config")
// deprecated for removal
.hide(true)
.num_args(1)
.action(ArgAction::Set)
.value_parser(clap::value_parser!(PathBuf))
Expand Down
4 changes: 3 additions & 1 deletion src/service/manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,8 @@ pub fn define_command_line_options(mut app: Command) -> Command {
.arg(
Arg::new("LOG_CONFIG")
.long("log-config")
// deprecated for removal
.hide(true)
.num_args(1)
.action(ArgAction::Set)
.value_parser(clap::value_parser!(PathBuf))
Expand Down Expand Up @@ -297,7 +299,7 @@ pub fn create(matches: &ArgMatches) -> ShadowsocksResult<(Runtime, impl Future<O
logging::init_with_file(path);
}
None => {
logging::init_with_config("sslocal", &service_config.log);
logging::init_with_config("ssmanager", &service_config.log);
Comment on lines -300 to +302
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this was a copy-paste error, same for the other one.

}
}

Expand Down
4 changes: 3 additions & 1 deletion src/service/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,8 @@ pub fn define_command_line_options(mut app: Command) -> Command {
.arg(
Arg::new("LOG_CONFIG")
.long("log-config")
// deprecated for removal
.hide(true)
.num_args(1)
.action(ArgAction::Set)
.value_parser(clap::value_parser!(PathBuf))
Expand Down Expand Up @@ -309,7 +311,7 @@ pub fn create(matches: &ArgMatches) -> ShadowsocksResult<(Runtime, impl Future<O
logging::init_with_file(path);
}
None => {
logging::init_with_config("sslocal", &service_config.log);
logging::init_with_config("ssserver", &service_config.log);
}
}

Expand Down
Loading