How to Route Command and Pipeline Logs into the Journal in Linux Using systemd-cat

Standalone shell scripts, ad-hoc maintenance jobs, and legacy pipeline commands frequently emit unmanaged standard output and standard error streams that can get lost or require cumbersome file redirection. The systemd-cat utility solves this challenge by serving as an interactive ingestion filter between arbitrary POSIX processes and the systemd journal. By piping output through or executing binaries directly under systemd-cat, administrators can seamlessly attach syslog identifiers, priority levels, and facility tags to process streams, converting unstructured console prints into structured, indexed, and filterable system logs.

Understanding How systemd-cat Interacts with journald

Standard Linux utilities write textual messages to file descriptors 1 (stdout) and 2 (stderr). In modern systemd installations, native service daemons automatically direct these descriptors to the journal socket via unit configuration directives. However, interactive terminal commands, automated shell scripts, and periodic cron jobs run outside native unit files and bypass this structured logging mechanism unless routed explicitly.

The systemd-cat tool opens a streaming socket connection directly to systemd-journald.service and forwards incoming lines as individual journal log records. When invoked, it reads standard input and writes each newline-terminated string to the journal buffer, appending timestamp metadata, caller credentials, and user-specified tags. This allows system administrators to monitor custom automation scripts using standard journalctl querying and filtering flags alongside core operating system daemons.

Piping Command Output Directly into the Journal

The simplest method to route command streams to systemd-journald involves piping the output of a pipeline into systemd-cat. By default, systemd-cat assigns the default priority level info and labels the record with a generic identifier corresponding to the invoking shell or parent binary.

To pipe output from a routine file synchronisation or maintenance command into the journal, execute:

rsync -avz /var/www/html/ backup.local:/backups/www/ 2>&1 | systemd-cat

In this invocation, both standard output and standard error are combined via 2>&1 and delivered into the stdin stream of systemd-cat. The resulting output appears inside the journal with standard timestamps and process identification.

Assigning Custom Log Identifiers and Syslog Tags

When running custom automation scripts across production servers, locating generic entries in journalctl can prove difficult. The -t (or --identifier=) parameter assigns a custom string identifier that attaches directly to the SYSLOG_IDENTIFIER field in the journal database.

To tag custom backup logs with a distinct identifier, run:

echo "Database cleanup completed successfully" | systemd-cat -t "db-maintenance"

You can verify and query these specific entries immediately using journalctl with the matching -t tag:

journalctl -t db-maintenance -n 20 --no-pager

This isolates your script events from broader system events without requiring separate text files in /var/log/.

Setting Explicit Syslog Priority Levels

Unstructured scripts frequently encounter varying operational states ranging from routine informational notices to critical database failures. The -p (or --priority=) option allows you to designate an explicit RFC 5424 syslog priority level for the ingested stream.

The utility accepts standard numerical priorities (0 to 7) as well as text labels, including:

  • emerg (0): System is unusable.
  • alert (1): Immediate action required.
  • crit (2): Critical conditions.
  • err (3): Error conditions.
  • warning (4): Warning conditions.
  • notice (5): Normal but significant condition.
  • info (6): Informational messages.
  • debug (7): Debug-level messages.

To record a critical alerting event from a disk health audit, execute:

echo "Critical: Root volume SMART threshold exceeded" | systemd-cat -t "storage-monitor" -p crit

Administrators filtering the journal for high-priority failures will immediately catch this message when inspecting errors:

journalctl -p crit..err -t storage-monitor

Wrapping Executables and Background Scripts with systemd-cat

Rather than piping output downstream through a Unix pipe, systemd-cat can directly spawn and wrap an executable command. When passed an executable path and arguments, systemd-cat binds both stdout and stderr of the spawned process directly to the journal stream before running it.

To wrap an administrative Python or Bash script directly, use the command-spawning syntax:

systemd-cat -t "nightly-sync" -p notice /opt/scripts/backup-storage.sh --full

When launched in this manner, standard output emitted by the script is recorded at the designated notice priority, and standard error messages are automatically tagged and preserved. This approach eliminates the need to handle subshell redirection, making it an ideal wrapper for automated task dispatchers.

Integrating systemd-cat with Cron Jobs

Traditional cron implementations dispatch email notifications via local mail transfer agents (MTAs) whenever a scheduled task produces output on stdout or stderr. In modern environments where local MTAs like Postfix or Sendmail are intentionally omitted, cron output is often discarded or dumped into obscure system mailboxes.

By inserting systemd-cat into your crontab definitions, you ensure all cron job execution details are captured directly within the unified journal infrastructure. Open your user or system crontab:

crontab -e

Define the cron entry using systemd-cat as the command prefix:

0 3 * * * systemd-cat -t "cron-cert-renew" /usr/local/bin/renew-certificates.sh

Every time cron fires at 03:00, execution messages, certificate checks, and unexpected errors are streamed directly to journald under the cron-cert-renew identifier. You can inspect the cron run history at any time without configuring an email server:

journalctl -t cron-cert-renew --since "yesterday"

Differentiating Standard Output and Standard Error Streams

When wrapping a binary that outputs both diagnostic progress and error messages, you may want standard output and standard error captured with distinct priorities rather than sharing a single blanket level. The --stderr-priority= flag sets an independent priority level specifically for file descriptor 2.

To record informational messages as info while escalating error streams to err, execute:

systemd-cat -t "app-deploy" --priority=info --stderr-priority=err /opt/deploy/run-migration.sh

With this configuration, normal progress statements are logged cleanly without triggering alert filters, while unexpected stack traces or missing dependency errors immediately qualify under journalctl -p err inspection criteria.

Inspecting JSON and Structured Output in journald

Logs delivered through systemd-cat are enriched with core operating system metadata, including the executing user ID (UID), effective group ID (GID), process ID (PID), and precise microsecond timestamps. To inspect the full structured object recorded by journald for your custom entries, output the journal records in JSON format:

journalctl -t app-deploy -n 1 -o json-pretty

The resulting output reveals how systemd-cat attaches the properties:

{
    "__CURSOR" : "s=39b75...",
    "__REALTIME_TIMESTAMP" : "1730956800123456",
    "_PID" : "41258",
    "_UID" : "1000",
    "_COMM" : "systemd-cat",
    "_EXE" : "/usr/bin/systemd-cat",
    "_CMDLINE" : "systemd-cat -t app-deploy /opt/deploy/run-migration.sh",
    "SYSLOG_IDENTIFIER" : "app-deploy",
    "PRIORITY" : "6",
    "MESSAGE" : "Database migrations applied successfully."
}

This deep integration enables centralized log aggregators like Fluentbit, Logstash, or Vector to ingest and parse custom shell executions using the exact same schema applied to production daemon services.

Get the best tech tips delivered straight to your inbox.

Join thousands of readers mastering Apple, Google, Microsoft, and Linux.