diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 23325f7c0..ff1b3f41d 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -2120,6 +2120,7 @@ dependencies = [ "tokio", "tokio-stream", "tracing", + "tracing-journald", "tracing-log", "tracing-subscriber", "url", @@ -7321,6 +7322,17 @@ dependencies = [ "valuable", ] +[[package]] +name = "tracing-journald" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0b4143302cf1022dac868d521e36e8b27691f72c84b3311750d5188ebba657" +dependencies = [ + "libc", + "tracing-core", + "tracing-subscriber", +] + [[package]] name = "tracing-log" version = "0.2.0" diff --git a/rust/Cargo.toml b/rust/Cargo.toml index c9f79fb35..298e9de02 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -140,6 +140,7 @@ tracing-log = "0.2.0" tracing-macros = { git = "https://github.com/tokio-rs/tracing", branch = "v0.1.x" } # Contains `dbg!` but for `tracing`. tracing-opentelemetry = "0.27.0" tracing-stackdriver = "0.11.0" +tracing-journald = "0.3.1" tracing-subscriber = { version = "0.3.19", features = ["parking_lot"] } trackable = "1.3.0" url = "2.5.2" diff --git a/rust/gui-client/src-common/Cargo.toml b/rust/gui-client/src-common/Cargo.toml index dc8420c98..e848b99c9 100644 --- a/rust/gui-client/src-common/Cargo.toml +++ b/rust/gui-client/src-common/Cargo.toml @@ -42,6 +42,7 @@ zip = { workspace = true, features = ["deflate", "time"] } [target.'cfg(target_os = "linux")'.dependencies] dirs = { workspace = true } +tracing-journald = { workspace = true } [target.'cfg(target_os = "windows")'.dependencies] winreg = { workspace = true } diff --git a/rust/gui-client/src-common/src/logging.rs b/rust/gui-client/src-common/src/logging.rs index ec05410e3..f91e6bbb8 100644 --- a/rust/gui-client/src-common/src/logging.rs +++ b/rust/gui-client/src-common/src/logging.rs @@ -53,34 +53,53 @@ pub fn setup(directives: &str) -> Result { } let log_path = known_dirs::logs().context("Can't compute app log dir")?; + std::fs::create_dir_all(&log_path).map_err(Error::CreateDirAll)?; // Logfilter for stdout cannot be reloaded. This is okay because we are using it only for local dev and debugging anyway. // Having multiple reload handles makes their type-signature quite complex so we don't bother with that. let (stdout_filter, stdout_reloader) = firezone_logging::try_filter(directives)?; - - let stdout = tracing_subscriber::fmt::layer() + let stdout_layer = tracing_subscriber::fmt::layer() .with_ansi(firezone_logging::stdout_supports_ansi()) - .event_format(firezone_logging::Format::new()) - .with_filter(stdout_filter); + .event_format(firezone_logging::Format::new()); + + let (system_filter, system_reloader) = firezone_logging::try_filter(directives)?; + let system_layer = system_layer().context("Failed to init system logger")?; + #[cfg(target_os = "linux")] + let syslog_identifier = Some(system_layer.syslog_identifier().to_owned()); + #[cfg(not(target_os = "linux"))] + let syslog_identifier = Option::::None; - std::fs::create_dir_all(&log_path).map_err(Error::CreateDirAll)?; let (file_layer, logger) = firezone_logging::file::layer(&log_path, "gui-client"); let (file_filter, file_reloader) = firezone_logging::try_filter(directives)?; let subscriber = Registry::default() .with(file_layer.with_filter(file_filter)) - .with(stdout) + .with(stdout_layer.with_filter(stdout_filter)) + .with(system_layer.with_filter(system_filter)) .with(firezone_logging::sentry_layer()); firezone_logging::init(subscriber)?; - tracing::debug!(log_path = %log_path.display(), "Log path"); + tracing::debug!(log_path = %log_path.display(), syslog_identifier = syslog_identifier.map(tracing::field::display)); Ok(Handles { logger, - reloader: stdout_reloader.merge(file_reloader), + reloader: stdout_reloader.merge(file_reloader).merge(system_reloader), }) } +#[cfg(target_os = "linux")] +fn system_layer() -> Result { + let layer = tracing_journald::layer()?; + + Ok(layer) +} + +#[cfg(not(target_os = "linux"))] +#[expect(clippy::unnecessary_wraps, reason = "Linux signature needs `Result`")] +fn system_layer() -> Result { + Ok(tracing_subscriber::layer::Identity::new()) +} + #[derive(Clone, Default, Serialize)] pub struct FileCount { bytes: u64, diff --git a/website/src/app/kb/client-apps/linux-gui-client/readme.mdx b/website/src/app/kb/client-apps/linux-gui-client/readme.mdx index 3abb75c6e..7d5aa11ab 100644 --- a/website/src/app/kb/client-apps/linux-gui-client/readme.mdx +++ b/website/src/app/kb/client-apps/linux-gui-client/readme.mdx @@ -158,7 +158,14 @@ captured by systemd and sent to journald. To view the logs of the IPC service, use: ```bash -journalctl -efu firezone-client-ipc.service +journalctl --pager-end --follow --unit firezone-client-ipc.service +``` + +The GUI client logs to journald directly as well with the syslog identifier +`firezone-client-gui`. To view the logs via `journalctl`, use: + +```bash +journalctl --pager-end --follow --identifier firezone-client-gui ``` ## Uninstalling