Send Command Output to the systemd Journal with systemd-cat

A cron job runs fine but leaves no trace anywhere, and piping it through systemd-cat is the fastest way to fix that. This guide captures a command's standard output and standard error in the journal, or adds a shell pipeline's output to it, gives entries a searchable identifier, sets a default priority, and checks the command's real exit status. Allow about ten minutes. Examples use systemd 255.4-1ubuntu8.17.

1. Check the installed command

Confirm the binary and version before relying on option details:

$ command -v systemd-cat
/usr/bin/systemd-cat
$ systemd-cat --version
systemd 255 (255.4-1ubuntu8.17)

The exact feature string after the version varies with the package build; the systemd release is what matters, since this guide follows the local systemd-cat(1) manual.

Checkpoint: run systemd-cat --help if a command copied from another host uses an unfamiliar option. The installed command supports --identifier, --priority, --stderr-priority, and --level-prefix.

2. Send one command to the journal

Pass the program and its arguments after systemd-cat. Both output streams get connected to journald:

$ systemd-cat --identifier=backup-check /usr/bin/sh -c 'printf "backup started\n"; printf "warning from stderr\n" >&2'

The command normally prints nothing in your terminal, because its standard output and standard error are being sent to the journal instead. The identifier makes those entries easy to find:

$ journalctl --no-pager --identifier=backup-check
Sep 27 05:45:00 host backup-check[1234]: backup started
Sep 27 05:45:00 host backup-check[1234]: warning from stderr

The timestamp, host, process ID and message order will differ on your machine. If your account cannot see all journal entries, journalctl may show a notice or omit messages written by other users. That is a read-access issue, not proof systemd-cat failed; reading another user's or a system service's logs may need membership of an appropriate journal group or elevated read access.

3. Preserve and check the child exit status

systemd-cat returns the status of the command it runs. Capture it immediately if a script needs to decide whether the operation succeeded:

$ systemd-cat --identifier=backup-check /usr/bin/sh -c 'printf "check failed\n"; exit 7'
$ status=$?
$ printf 'child exit status: %s\n' "$status"
child exit status: 7

Tip: a non-zero status stays non-zero even though the message was accepted by the journal. Do not replace the status with a later journalctl command, or with a test that merely confirms a log entry exists. A useful shell wrapper records the status and returns it to its caller:

systemd-cat --identifier=nightly-task /path/to/task --mode check
status=$?
if [ "$status" -ne 0 ]; then
    printf 'nightly-task failed with status %s\n' "$status" >&2
fi
exit "$status"

4. Add a pipeline safely

With no command arguments, systemd-cat reads standard input, which is useful when an existing tool already produces the stream you want to record:

$ /path/to/check --verbose 2>&1 | systemd-cat --identifier=check-output --priority=notice
$ journalctl --no-pager --identifier=check-output

The 2>&1 part is deliberate. A pipeline normally sends only the left command's standard output into systemd-cat; its standard error would still go to the terminal. Merge the streams before the pipe when you want both in the journal.

The direct command form from step 2 is usually preferable when it fits: it captures both output streams without a separate pipeline process, and the child's exit status is available directly. Save the pipeline form for output that genuinely comes from a preceding process.

5. Choose identifiers and priorities

Use a short identifier that describes the producer rather than the host. Without --identifier, systemd-cat writes no identification string at all. Set the default priority with a name or a number from 0 to 7:

$ printf 'configuration checked\n' | systemd-cat --identifier=config-check --priority=notice
$ journalctl --no-pager --identifier=config-check -n 1
Sep 27 05:46:00 host config-check[1234]: configuration checked

The named levels are emerg, alert, crit, err, warning, notice, info, and debug. The default is info. This is a default, not a filter: every line is still logged unless another journald policy affects visibility or storage.

Use --stderr-priority=err when a command's standard error should have a different default from standard output:

$ systemd-cat --identifier=import-job --priority=info --stderr-priority=err /path/to/import-job

When the two defaults differ, systemd-cat uses separate channels, and stdout and stderr are not strictly ordered relative to each other. If strict ordering matters more than distinct priorities, leave both streams at the same default.

6. Understand line prefixes and common traps

Level-prefix parsing is on by default. A line beginning with a prefix such as <5> is logged at that syslog priority instead of the default, useful when the producer deliberately emits syslog-style levels, but surprising when angle-bracketed text is just ordinary output. Disable it explicitly when the input is not under your control:

$ printf '<5>literal-looking text\n' | systemd-cat --identifier=raw-output --level-prefix=no

Do not put untrusted text straight into the shell command. Quote identifiers and arguments that contain spaces or shell characters, and prefer a direct command invocation over sh -c, which adds another layer of shell parsing you only need for an intentionally compound command.

Warning: journal logging is not a replacement for a command's output file, audit trail, or durable application record. Retention, forwarding and visibility are controlled by journald configuration and permissions outside this command. Never log passwords, tokens, private keys or sensitive input just because a diagnostic command happens to print it.

Done means