From a0f079f1cd98546af59d1723c792e200e2bde887 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Mon, 24 Feb 2025 15:28:56 +1100 Subject: [PATCH] feat(gui-client): send Linux GUI logs to journald (#8236) This configures the GUI client to log to journald in addition to files as well. For better or worse, this logs all events such that structured information is preserved, e.g. all additional fields next to the message are also saved as fields in the journal. By default, when viewing the logs via `journalctl`, those fields are not displayed. This makes the default output of `journalctl` for the FIrezone GUI not as useful as it could be. Fixing that is left to a later stage. Related: #8173 --- rust/Cargo.lock | 12 +++++++ rust/Cargo.toml | 1 + rust/gui-client/src-common/Cargo.toml | 1 + rust/gui-client/src-common/src/logging.rs | 35 ++++++++++++++----- .../client-apps/linux-gui-client/readme.mdx | 9 ++++- 5 files changed, 49 insertions(+), 9 deletions(-) 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