tracing-systemd 0.2.1

A tracing-subscriber Layer that pretty-prints span chains to stdout and/or the systemd journal
Documentation

tracing-systemd

crates.io docs.rs MSRV

A tracing-subscriber layer that prints span chains to stdout in a format that's easy to read locally and easy to ingest into the systemd journal when run under a unit.

INFO [1] my_app::request(method: "GET", path: "/api")::handler(): served in 4ms
WARN [1] my_app::request(method: "POST", path: "/api")::handler(): retrying: {attempt: 2}

Usage

[dependencies]
tracing            = "0.1"
tracing-subscriber = "0.3"
tracing-systemd    = "0.2"
use tracing::info;
use tracing_subscriber::prelude::*;
use tracing_systemd::SystemdLayer;

fn main() {
    tracing_subscriber::registry()
        .with(SystemdLayer::stdout().with_target(true).with_thread_ids(true))
        .init();

    info!("hello, world");
}

See examples/ for more.

Features

Feature Default Effect
colors yes ANSI color output via nu-ansi-term.
journald no Re-exports tracing-journald under tracing_systemd::journald.
json no Pulls in serde_json (reserved for a future JSON output mode).

To turn off color output:

tracing-systemd = { version = "0.2", default-features = false }

Logging to the journal

There are two ways to get logs into journald, depending on how the binary is started.

If your binary runs under a systemd unit, its stdout/stderr is already piped to the journal. The default SystemdLayer::stdout() emits the <3><7> syslog priority prefix that journalctl uses to assign levels, so you don't need anything else:

# use tracing_subscriber::prelude::*;
# use tracing_systemd::SystemdLayer;
tracing_subscriber::registry()
    .with(SystemdLayer::stdout())
    .init();

If you want structured fields in the journal, or you're running outside a unit, enable the journald feature and add the dedicated layer alongside the stdout one:

use tracing_subscriber::prelude::*;
use tracing_systemd::SystemdLayer;

let journald = tracing_systemd::journald::layer_with_identifier("my-app").ok();

tracing_subscriber::registry()
    .with(SystemdLayer::stdout())
    .with(journald)
    .init();

Option<Layer> implements Layer<S>, so a None (when journald isn't reachable) just becomes a no-op without an if let.

Filter entries with journalctl -t my-app.

Customization

All separators, brackets, and prefixes are overridable on the builder. They take anything that's Into<Cow<'static, str>>, so both &'static str and String work.

use tracing_systemd::{SystemdLayer, ColorMode, ColorTheme, TimestampFormat};
use nu_ansi_term::{Color, Style};

let layer = SystemdLayer::stdout()
    .with_target(true)
    .with_thread_ids(true)
    .with_timestamp_format(TimestampFormat::UnixSeconds)
    .with_function_bracket_left("[")
    .with_function_bracket_right("]")
    .with_arguments_equality("=")
    .with_color_mode(ColorMode::Auto)
    .with_color_theme(ColorTheme {
        info: Style::new().fg(Color::Cyan).bold(),
        ..ColorTheme::default()
    });

For tests, redirect output to any io::Write:

use std::sync::{Arc, Mutex};
use tracing_systemd::{Output, SystemdLayer};

let buf = Arc::new(Mutex::new(Vec::<u8>::new()));
let layer = SystemdLayer::stdout()
    .with_output(Output::writer(MyShared(buf.clone())));

Migrating from 0.1

0.1 0.2
SystemdLayer::new() SystemdLayer::stdout()
separate_spans_with(s) with_span_separator(s)
separate_message_with(s) with_message_separator(s)
level_separator(s) with_level_separator(s)
function_bracket_left(s) with_function_bracket_left(s)
function_bracket_right(s) with_function_bracket_right(s)
arguments_equality(s) with_arguments_equality(s)
arguments_separator(s) with_arguments_separator(s)
thread_id_prefix(s) with_thread_id_prefix(s)
thread_id_suffix(s) with_thread_id_suffix(s)
use_level_prefix(b) with_level_prefix(b)
use_color(true/false) with_color_mode(ColorMode::Always / Never)
use_sd_journal(true) Add tracing_systemd::journald::layer()? as a separate layer (needs journald feature).

with_target and with_thread_ids are unchanged. with_timestamps, with_timestamp_format, with_color_theme, and with_output are new.

Other things that changed:

  • The default for color is now ColorMode::Auto (respects NO_COLOR and TTY status). Pass ColorMode::Always for the old behavior.
  • The colored feature was renamed to colors.
  • The sd-journal feature is gone; use journald, which goes through tracing-journald (pure Rust, no libsystemd-dev).
  • The runtime use_sd_journal(false) toggle is gone. Pick the layer at construction time instead.

MSRV

Rust 1.85 (edition 2024). MSRV bumps are minor version bumps.

License

MIT