From 5fea9ac05c7296dce3f8dec363b7442bec929c55 Mon Sep 17 00:00:00 2001 From: Sebastian Martinez Date: Fri, 19 Sep 2025 11:40:00 +0200 Subject: [PATCH] node: Add `--log-logger structured` and `--log-format json` Optionally depend on crate `structured-logger`. Allow enabling it via `--log-logger structured`. For future compatibility, provide `--log-format` which currently only supports JSON. Co-authored-by: Lorenz Leutgeb --- Cargo.lock | 176 +++++++++++++++++++++++++++++++- crates/radicle-node/Cargo.toml | 3 +- crates/radicle-node/src/main.rs | 41 +++++++- 3 files changed, 217 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d169540b..1ca78641 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -910,7 +910,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -1893,6 +1893,17 @@ dependencies = [ "popol", ] +[[package]] +name = "io-uring" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "046fa2d4d00aea763528b4950358d0ead425372445dc8ff86312b3c69ff7727b" +dependencies = [ + "bitflags 2.9.1", + "cfg-if", + "libc", +] + [[package]] name = "itertools" version = "0.14.0" @@ -2090,6 +2101,10 @@ name = "log" version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" +dependencies = [ + "serde", + "value-bag", +] [[package]] name = "matchers" @@ -2922,6 +2937,7 @@ dependencies = [ "snapbox", "socket2", "sqlite", + "structured-logger", "tempfile", "test-log", "thiserror 1.0.69", @@ -3369,6 +3385,15 @@ dependencies = [ "syn 2.0.89", ] +[[package]] +name = "serde_fmt" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d4ddca14104cd60529e8c7f7ba71a2c8acd8f7f5cfcdc2faf97eeb7c3010a4" +dependencies = [ + "serde", +] + [[package]] name = "serde_json" version = "1.0.140" @@ -3516,6 +3541,12 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" +[[package]] +name = "slab" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" + [[package]] name = "smallvec" version = "1.13.2" @@ -3671,12 +3702,103 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b2231b7c3057d5e4ad0156fb3dc807d900806020c5ffa3ee6ff2c8c76fb8520" +[[package]] +name = "structured-logger" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f41647ab1dfedac6dccb4622ded5f3bea80ade9257a9ddcc89e36a43e1769cdf" +dependencies = [ + "log", + "parking_lot", + "serde", + "serde_json", + "tokio", +] + [[package]] name = "subtle" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" +[[package]] +name = "sval" +version = "2.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cc9739f56c5d0c44a5ed45473ec868af02eb896af8c05f616673a31e1d1bb09" + +[[package]] +name = "sval_buffer" +version = "2.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f39b07436a8c271b34dad5070c634d1d3d76d6776e938ee97b4a66a5e8003d0b" +dependencies = [ + "sval", + "sval_ref", +] + +[[package]] +name = "sval_dynamic" +version = "2.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffcb072d857431bf885580dacecf05ed987bac931230736739a79051dbf3499b" +dependencies = [ + "sval", +] + +[[package]] +name = "sval_fmt" +version = "2.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f214f427ad94a553e5ca5514c95c6be84667cbc5568cce957f03f3477d03d5c" +dependencies = [ + "itoa", + "ryu", + "sval", +] + +[[package]] +name = "sval_json" +version = "2.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ed34b32e638dec9a99c8ac92d0aa1220d40041026b625474c2b6a4d6f4feb" +dependencies = [ + "itoa", + "ryu", + "sval", +] + +[[package]] +name = "sval_nested" +version = "2.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14bae8fcb2f24fee2c42c1f19037707f7c9a29a0cda936d2188d48a961c4bb2a" +dependencies = [ + "sval", + "sval_buffer", + "sval_ref", +] + +[[package]] +name = "sval_ref" +version = "2.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a4eaea3821d3046dcba81d4b8489421da42961889902342691fb7eab491d79e" +dependencies = [ + "sval", +] + +[[package]] +name = "sval_serde" +version = "2.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "172dd4aa8cb3b45c8ac8f3b4111d644cd26938b0643ede8f93070812b87fb339" +dependencies = [ + "serde", + "sval", + "sval_nested", +] + [[package]] name = "syn" version = "1.0.109" @@ -3882,6 +4004,22 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" +[[package]] +name = "tokio" +version = "1.47.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" +dependencies = [ + "backtrace", + "bytes", + "io-uring", + "libc", + "mio 1.0.4", + "parking_lot", + "pin-project-lite", + "slab", +] + [[package]] name = "toml" version = "0.9.5" @@ -4234,6 +4372,42 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" +[[package]] +name = "value-bag" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "943ce29a8a743eb10d6082545d861b24f9d1b160b7d741e0f2cdf726bec909c5" +dependencies = [ + "value-bag-serde1", + "value-bag-sval2", +] + +[[package]] +name = "value-bag-serde1" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35540706617d373b118d550d41f5dfe0b78a0c195dc13c6815e92e2638432306" +dependencies = [ + "erased-serde", + "serde", + "serde_fmt", +] + +[[package]] +name = "value-bag-sval2" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fe7e140a2658cc16f7ee7a86e413e803fc8f9b5127adc8755c19f9fefa63a52" +dependencies = [ + "sval", + "sval_buffer", + "sval_dynamic", + "sval_fmt", + "sval_json", + "sval_ref", + "sval_serde", +] + [[package]] name = "vcpkg" version = "0.2.15" diff --git a/crates/radicle-node/Cargo.toml b/crates/radicle-node/Cargo.toml index 69da87d3..8e6d4d82 100644 --- a/crates/radicle-node/Cargo.toml +++ b/crates/radicle-node/Cargo.toml @@ -10,7 +10,7 @@ build = "build.rs" rust-version.workspace = true [features] -default = ["systemd"] +default = ["systemd", "structured-logger"] systemd = ["dep:radicle-systemd"] test = ["radicle/test", "radicle-crypto/test", "radicle-crypto/cyphernet", "radicle-protocol/test", "qcheck", "snapbox"] @@ -44,6 +44,7 @@ serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true, features = ["preserve_order"] } snapbox = { workspace = true, optional = true } socket2 = "0.5.7" +structured-logger = { version = "1.0.4", optional = true } tempfile = { workspace = true } thiserror = { workspace = true } diff --git a/crates/radicle-node/src/main.rs b/crates/radicle-node/src/main.rs index 3cb8fcac..efa0aacd 100644 --- a/crates/radicle-node/src/main.rs +++ b/crates/radicle-node/src/main.rs @@ -32,8 +32,9 @@ Options --listen
Address to listen on --log-level Set log level (default: info) - --log-logger (radicle | systemd) Set logger implementation + --log-logger (radicle | structured | systemd) Set logger implementation (default: radicle) + --log-format json Set log format for logger implementation --version Print program version --help Print help "#; @@ -41,6 +42,8 @@ Options #[derive(Debug, Clone)] enum Logger { Radicle, + #[cfg(feature = "structured-logger")] + Structured, #[cfg(all(feature = "systemd", target_os = "linux"))] Systemd, } @@ -62,6 +65,8 @@ impl FromStr for Logger { fn from_str(s: &str) -> Result { match s { "radicle" => Ok(Logger::Radicle), + #[cfg(feature = "structured-logger")] + "structured" => Ok(Logger::Structured), #[cfg(all(feature = "systemd", target_os = "linux"))] "systemd" => Ok(Logger::Systemd), _ => Err("unknown logger"), @@ -69,9 +74,28 @@ impl FromStr for Logger { } } +#[derive(Clone, Copy)] +enum LogFormat { + #[cfg(feature = "structured-logger")] + Json, +} + +impl FromStr for LogFormat { + type Err = &'static str; + + fn from_str(s: &str) -> Result { + match s { + #[cfg(feature = "structured-logger")] + "json" => Ok(LogFormat::Json), + _ => Err("unknown log format"), + } + } +} + struct LogOptions { level: Option, logger: Logger, + format: Option, } struct Options { @@ -91,6 +115,7 @@ fn parse_options() -> Result { let mut force = false; let mut log_level = None; let mut log_logger = Logger::default(); + let mut log_format = None; while let Some(arg) = parser.next()? { match arg { @@ -120,6 +145,9 @@ fn parse_options() -> Result { } log_logger = parsed; } + Long("log-format") => { + log_format = Some(parser.value()?.parse_with(LogFormat::from_str)?); + } Long("help") | Short('h') => { println!("{HELP_MSG}"); exit(0); @@ -141,6 +169,7 @@ fn parse_options() -> Result { log: LogOptions { level: log_level, logger: log_logger, + format: log_format, }, }) } @@ -232,6 +261,16 @@ fn initialize_logging(options: &LogOptions) -> Result<(), Box = { match options.logger { + #[cfg(feature = "structured-logger")] + Logger::Structured => { + use structured_logger::{json, Builder}; + + let writer = match options.format.unwrap_or(LogFormat::Json) { + LogFormat::Json => json::new_writer(io::stdout()), + }; + + Box::new(Builder::new().with_default_writer(writer).build()) + } #[cfg(all(feature = "systemd", target_os = "linux"))] Logger::Systemd => { use radicle_systemd::journal::*;