From 1db36a1c73b33bb168faafcf9c0f76e5e96075da Mon Sep 17 00:00:00 2001 From: zonyitoo Date: Mon, 17 Nov 2025 01:30:11 +0800 Subject: [PATCH] feat(shadowsocks-rust): logging support syslog (#2044) --- Cargo.lock | 28 +++++++++++----- Cargo.toml | 1 + README.md | 11 +++++++ src/config.rs | 21 ++++++++++++ src/logging/tracing.rs | 75 +++++++++++++++++++++++++++++++++++++----- 5 files changed, 119 insertions(+), 17 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 84e58933..56fd70a3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -976,7 +976,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -1541,7 +1541,7 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2 0.5.10", + "socket2 0.6.1", "system-configuration", "tokio", "tower-service", @@ -2592,7 +2592,7 @@ dependencies = [ "quinn-udp", "rustc-hash", "rustls", - "socket2 0.5.10", + "socket2 0.6.1", "thiserror 2.0.17", "tokio", "tracing", @@ -2629,9 +2629,9 @@ dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2 0.5.10", + "socket2 0.6.1", "tracing", - "windows-sys 0.52.0", + "windows-sys 0.60.2", ] [[package]] @@ -2904,7 +2904,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -3273,6 +3273,7 @@ dependencies = [ "shadowsocks-service", "snmalloc-rs", "sysexits", + "syslog-tracing", "tcmalloc", "thiserror 2.0.17", "time", @@ -3545,6 +3546,17 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf9154bb31a0b747e214520c60cb9a842df871bf6a5fea5be4352b59d6c432ab" +[[package]] +name = "syslog-tracing" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d349bc2df408b4bf656709a29643641cef7f1795d708f88b105c626a8f64f6e4" +dependencies = [ + "libc", + "tracing-core", + "tracing-subscriber", +] + [[package]] name = "system-configuration" version = "0.6.1" @@ -3603,7 +3615,7 @@ dependencies = [ "getrandom 0.3.3", "once_cell", "rustix", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -4274,7 +4286,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.61.2", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 6afe7dde..dbb27610 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -249,6 +249,7 @@ windows-service = { version = "0.8", optional = true } [target.'cfg(unix)'.dependencies] xdg = "3.0" +syslog-tracing = "0.3" [target.'cfg(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64"))'.dependencies] reqwest = { version = "0.12", features = [ diff --git a/README.md b/README.md index cac87356..f1af1e86 100644 --- a/README.md +++ b/README.md @@ -913,6 +913,17 @@ Example configuration: // Optional. If set, keeps the last N log files "max_files": 5 } + }, + { + // Configure a syslog writer, only supported on *nix system + "syslog": { + // `level` and `format` can also be set here, if not set, it will use the default values + + // Optional. Set the "identity" when calling openlog(). Use current service name by default. + "identity": "identity_name", + // Optional. Set the "facility" when calling openlog(). 1 (user-level messages) by default. See RFC5424. + "facility": 1 + } } ] }, diff --git a/src/config.rs b/src/config.rs index d8f89da7..dacfeb3d 100644 --- a/src/config.rs +++ b/src/config.rs @@ -198,6 +198,8 @@ pub struct LogFormatConfig { pub enum LogWriterConfig { Console(LogConsoleWriterConfig), File(LogFileWriterConfig), + #[cfg(unix)] + Syslog(LogSyslogWriterConfig), } /// Console appender configuration for logging @@ -269,6 +271,25 @@ impl From for tracing_appender::rolling::Rotation { } } +/// File appender configuration for logging +#[cfg(all(feature = "logging", unix))] +#[derive(Deserialize, Debug, Clone)] +pub struct LogSyslogWriterConfig { + /// Level override + #[serde(default)] + pub level: Option, + /// Format override + #[serde(default)] + pub format: LogFormatConfigOverride, + + /// syslog identity, process name by default + #[serde(default)] + pub identity: Option, + /// Facility, 1 (USER) by default + #[serde(default)] + pub facility: Option, +} + /// Runtime mode (Tokio) #[derive(Deserialize, Debug, Clone, Copy, Default, Eq, PartialEq)] #[serde(rename_all = "snake_case")] diff --git a/src/logging/tracing.rs b/src/logging/tracing.rs index 42a1482d..3fc194df 100644 --- a/src/logging/tracing.rs +++ b/src/logging/tracing.rs @@ -1,18 +1,21 @@ //! Logging facilities with tracing -use std::io; -use std::io::IsTerminal; +use std::io::{self, IsTerminal}; -use time::UtcOffset; -use time::format_description::well_known::Rfc3339; +#[cfg(unix)] +use syslog_tracing::Syslog; +use time::{UtcOffset, format_description::well_known::Rfc3339}; use tracing::level_filters::LevelFilter; use tracing_appender::rolling::{InitError, RollingFileAppender}; -use tracing_subscriber::fmt::MakeWriter; -use tracing_subscriber::fmt::time::OffsetTime; -use tracing_subscriber::layer::SubscriberExt; -use tracing_subscriber::util::SubscriberInitExt; -use tracing_subscriber::{EnvFilter, Layer, Registry, fmt}; +use tracing_subscriber::{ + fmt::{MakeWriter, time::OffsetTime}, + layer::SubscriberExt, + util::SubscriberInitExt, + {EnvFilter, Layer, Registry, fmt}, +}; +#[cfg(unix)] +use crate::config::LogSyslogWriterConfig; use crate::config::{ LogConfig, LogConsoleWriterConfig, LogFileWriterConfig, LogFormatConfig, LogFormatConfigOverride, LogWriterConfig, }; @@ -38,6 +41,8 @@ impl MakeLayer for LogWriterConfig { match self { LogWriterConfig::Console(console_config) => console_config.make_layer(bin_name, global), LogWriterConfig::File(file_config) => file_config.make_layer(bin_name, global), + #[cfg(unix)] + LogWriterConfig::Syslog(syslog_config) => syslog_config.make_layer(bin_name, global), } } } @@ -63,6 +68,17 @@ impl MakeLayer for LogFileWriterConfig { } } +#[cfg(unix)] +impl MakeLayer for LogSyslogWriterConfig { + fn make_layer(&self, bin_name: &str, global: &LogConfig) -> BoxedLayer { + let level = self.level.unwrap_or(global.level); + let format = apply_override(&global.format, &self.format); + + let syslog_writer = make_syslog_writer(bin_name, self); + make_fmt_layer(bin_name, level, &format, false, syslog_writer) + } +} + /// Boilerplate for configuring a `fmt::Layer` with `level` and `format` for different writers. fn make_fmt_layer(bin_name: &str, level: u32, format: &LogFormatConfig, ansi: bool, writer: W) -> BoxedLayer where @@ -162,3 +178,44 @@ fn apply_override(global: &LogFormatConfig, override_config: &LogFormatConfigOve without_time: override_config.without_time.unwrap_or(global.without_time), } } + +#[cfg(unix)] +fn make_syslog_writer(bin_name: &str, config: &LogSyslogWriterConfig) -> Syslog { + use std::ffi::CString; + + use syslog_tracing::{Facility, Options}; + + let identity = config.identity.as_deref().unwrap_or(bin_name); + let facility = match config.facility { + None => Facility::default(), + Some(f) => match f { + 1 => Facility::User, + 2 => Facility::Mail, + 3 => Facility::Daemon, + 4 => Facility::Auth, + 6 => Facility::Lpr, + 7 => Facility::News, + 8 => Facility::Uucp, + 9 => Facility::Cron, + 10 => Facility::AuthPriv, + 16 => Facility::Local0, + 17 => Facility::Local1, + 18 => Facility::Local2, + 19 => Facility::Local3, + 20 => Facility::Local4, + 21 => Facility::Local5, + 22 => Facility::Local6, + 23 => Facility::Local7, + _ => panic!("unsupported syslog facility: {}", f), + }, + }; + let options = Options::default(); + let identity = CString::new(identity).expect("syslog identity contains null-byte ('\\0')"); + + let syslogger = match Syslog::new(identity, options, facility) { + Some(l) => l, + None => panic!("syslog is already initialized"), + }; + + syslogger +}