Skip to content

Collect syslog and journald logs

Linux hosts expose their system logs in two places: the classic syslog stream that daemons such as rsyslog and syslog-ng write and forward over the network, and the systemd journal that journald maintains as a structured binary store. The OpenTelemetry Collector reads both — the syslog receiver listens for forwarded syslog messages, and the journald receiver shells out to journalctl to read the journal — and ships either to Osuite over OTLP.

Use the syslog receiver when a syslog daemon is already running and you want to point its output at the Collector, including centralized setups where many hosts forward to one collector. Use the journald receiver when you want system logs straight from the journal without a forwarding daemon in the path. For application log files on disk, see Collect log files.

  • The OpenTelemetry Collector (contrib distribution) — both receivers ship in opentelemetry-collector-contrib.
  • An Osuite ingest token and endpoint — see Getting Started.
  • For syslog: rsyslog or syslog-ng running on the host, with permission to add a forwarding rule.
  • For journald: the Collector must run where the journalctl binary and the journal files are available — see Run the Collector where the journal lives.

The syslog receiver opens a TCP or UDP listener, parses each incoming message as RFC 5424 or RFC 3164, and emits a structured log record. It maps the syslog priority to the record’s severity and the syslog timestamp to the record’s time automatically, so no separate severity or timestamp parser is needed.

This otel-collector.yaml listens for RFC 5424 messages over TCP on port 54527, promotes the parsed message to the log body, and exports to Osuite. The listen port is arbitrary — match it in the daemon config below.

receivers:
syslog:
tcp:
listen_address: "0.0.0.0:54527"
protocol: rfc5424
operators:
- type: move
from: attributes.message
to: body
processors:
batch: {}
resourcedetection:
detectors: [system]
exporters:
otlp/osuite:
endpoint: ingest.<region>.osuite.io:443
tls:
insecure: false
headers:
x-osuite-ingest-token: <your-ingest-token>
service:
pipelines:
logs:
receivers: [syslog]
processors: [batch, resourcedetection]
exporters: [otlp/osuite]

For UDP transport, replace the tcp block with udp:

syslog:
udp:
listen_address: "0.0.0.0:54527"
protocol: rfc5424

Add a forwarding rule in /etc/rsyslog.d/60-osuite.conf. This sends every message to the Collector over TCP in RFC 5424 format, matching protocol: rfc5424 above:

*.* action(type="omfwd"
target="127.0.0.1"
port="54527"
protocol="tcp"
template="RSYSLOG_SyslogProtocol23Format")

Reload rsyslog with systemctl restart rsyslog. The RSYSLOG_SyslogProtocol23Format template emits RFC 5424; to send the traditional BSD format instead, use RSYSLOG_TraditionalForwardFormat and set protocol: rfc3164 on the receiver.

Add a destination and log path in /etc/syslog-ng/conf.d/osuite.conf. The syslog() driver sends RFC 5424 over TCP:

destination d_osuite {
syslog("127.0.0.1" transport("tcp") port(54527));
};
log {
source(s_src);
destination(d_osuite);
};

Reload with systemctl restart syslog-ng. s_src is the default source defined in the stock syslog-ng.conf; adjust the name if your configuration differs.

  • Protocol — set protocol to rfc5424 for the modern structured format or rfc3164 for the traditional BSD format. The value must match what the daemon sends.
  • Severity — the receiver reads the syslog PRI header and sets the record’s SeverityNumber automatically, so filtering by level in the Logs Explorer works without extra operators.
  • Timestamps — RFC 3164 timestamps carry no year or timezone. Set location (for example location: America/New_York) on the receiver so they are interpreted in the host’s zone; RFC 5424 timestamps are unambiguous and need no location.
  • Sender identity — the parsed hostname, appname, and facility are kept as attributes. In centralized setups where many hosts forward to one Collector, filter by the hostname attribute to isolate a sender, since resourcedetection tags records with the Collector’s host, not the origin’s.

The journald receiver reads the systemd journal by invoking journalctl. It is useful on hosts where journald is the system of record and you would rather not run a separate syslog daemon.

The simplest and recommended path is to run the Collector directly on the host — as a systemd service or a plain binary — where journalctl is already on PATH and the journal under /var/log/journal (persistent) or /run/log/journal (volatile) is readable. The config below assumes this.

If you must run the Collector in a container, either build a custom image that installs journalctl and bind-mount the host journal read-only, or mount the host root and point journalctl at it through a chroot:

docker run -v /:/host:ro --user 0 otel/opentelemetry-collector-contrib

Then set root_path: /host on the receiver so it runs journalctl against the host’s journal.

By default the receiver places the entire journal entry as a structured map in the record body. This otel-collector.yaml promotes the message to the body, lifts the systemd unit to an attribute, maps the journal priority to severity, and exports to Osuite:

receivers:
journald:
start_at: end
priority: info
processors:
batch: {}
resourcedetection:
detectors: [system]
exporters:
otlp/osuite:
endpoint: ingest.<region>.osuite.io:443
tls:
insecure: false
headers:
x-osuite-ingest-token: <your-ingest-token>
service:
pipelines:
logs:
receivers: [journald]
processors: [batch, resourcedetection]
exporters: [otlp/osuite]

Reading the whole journal is often too much. Narrow it at the receiver so only the units and severities you care about are collected:

journald:
start_at: end
units:
- ssh
- nginx
- kubelet
priority: info
  • units — collect only these systemd units. Omit to read every unit.
  • priority — drop anything below this syslog priority. info keeps info and more severe; use warning or err to reduce volume further.
  • matches — for finer control, filter on any journal field (for example _SYSTEMD_UNIT or _TRANSPORT); each list entry is a set of ANDed conditions.
  • start_atend reads only new entries (default); beginning backfills the existing journal on first start.

The receiver does not map severity on its own — a fresh record keeps SeverityNumber unset even though the journal carries a PRIORITY field. Add operators to promote the message and map the numeric syslog priority (0 = emergency through 7 = debug) to OpenTelemetry severity:

journald:
start_at: end
priority: info
operators:
- type: move
from: body._SYSTEMD_UNIT
to: attributes["systemd.unit"]
- type: severity_parser
parse_from: body.PRIORITY
mapping:
fatal:
- "0"
- "1"
- "2"
error: "3"
warn:
- "4"
- "5"
info: "6"
debug: "7"
- type: move
from: body.MESSAGE
to: body

The severity_parser and the unit move run while the body is still the full journal map, so they read body.PRIORITY and body._SYSTEMD_UNIT. The final move replaces the body with just MESSAGE; do it last, after everything else you want has been lifted out. Other journal fields such as _PID or SYSLOG_IDENTIFIER are available in the body map and can be moved to attributes the same way before that final step.

What you should see

Within a minute, system log records appear in the Logs Explorer. Syslog records carry hostname, appname, and facility attributes with severity already set from the PRI header; journald records carry systemd.unit and the severity mapped from PRIORITY. Filtering by severity returns the expected levels.

  • No syslog records — confirm the daemon is forwarding to the Collector’s listen_address and port, that the transport matches (tcp vs udp), and that protocol matches the format the daemon sends. A firewall between daemon and Collector will silently drop the traffic.
  • Garbled syslog messages — the protocol setting does not match the sender. RFC 5424 output parsed as rfc3164 (or the reverse) produces malformed records.
  • journalctl: command not found or the receiver fails to start — the Collector is running in an environment without the journalctl binary; run it on the host or use the container workaround above.
  • Empty journald output — the journal may be volatile only. Confirm the process can read /var/log/journal or /run/log/journal, and that priority is not filtering everything out.
  • Severity always unset for journald — the severity_parser operator is missing or parse_from does not point at body.PRIORITY.

No data in Osuite after a few minutes? Work through these checks.

  • Endpoint — confirm the exporter targets exactly ingest.<region>.osuite.io:443 for your region, over TLS.
  • Token — confirm the ingest header carries a valid <your-ingest-token> and has not been rotated.
  • Pipeline — confirm the signal you expect (traces, logs, or metrics) is wired into an active pipeline with the otlp/osuite exporter attached.
  • Export errors — check the application or Collector logs for OTLP export failures (auth, DNS, TLS, connection refused).
  • Network — confirm the host has outbound access to the ingest endpoint on port 443.
  • Timing — allow up to a minute for the first data to appear before assuming a failure.

Still stuck? Ask the Investigation Agent or contact support.