Skip to main content
โšก Calmops

Syslog Protocol: Centralized Logging Setup and Configuration

Introduction

Syslog is the standard protocol for system and application logging on Linux and Unix systems. It defines a message format, severity levels, and transport mechanisms that allow logs from hundreds of servers to flow to a central location for analysis, alerting, and compliance.

Prerequisites: Linux system with rsyslog installed (default on Ubuntu/Debian/RHEL), basic shell knowledge.

Syslog Message Format

A syslog message has this structure (RFC 5424):

<priority>version timestamp hostname app-name procid msgid structured-data message

Example:
<34>1 2026-03-30T10:00:00Z web01 nginx 1234 - - GET /api/users 200 45ms

Priority = Facility ร— 8 + Severity

Facilities (what generated the message):

Code Facility Common Use
0 kern Kernel messages
1 user User-level messages
3 daemon System daemons
4 auth Authentication/security
16-23 local0-local7 Custom applications

Severities (how important):

Code Name Meaning
0 emerg System unusable
1 alert Immediate action required
2 crit Critical conditions
3 err Error conditions
4 warning Warning conditions
5 notice Normal but significant
6 info Informational
7 debug Debug messages
# Priority examples:
# kern.emerg    = 0*8 + 0 = 0   (kernel emergency)
# auth.warning  = 4*8 + 4 = 36  (auth warning)
# local0.info   = 16*8 + 6 = 134 (app info)

rsyslog: The Standard Syslog Daemon

rsyslog is the default syslog daemon on most Linux distributions.

Basic Configuration

# /etc/rsyslog.conf

# Load modules
module(load="imuxsock")   # local Unix socket (most apps use this)
module(load="imklog")     # kernel log messages
module(load="imjournal")  # systemd journal (modern systems)

# Log rules: facility.severity  destination
# * = all, none = exclude

# All emergency messages to all users
*.emerg                         :omusrmsg:*

# Auth messages to auth.log
auth,authpriv.*                 /var/log/auth.log

# All except auth to syslog
*.*;auth,authpriv.none          /var/log/syslog

# Kernel messages
kern.*                          /var/log/kern.log

# Cron
cron.*                          /var/log/cron.log

# Debug (exclude mail, auth, cron)
*.debug;\
    mail,authpriv,cron.none     /var/log/debug

Receiving Logs from Remote Hosts

# /etc/rsyslog.conf โ€” on the central log server

# Enable TCP input (reliable, recommended)
module(load="imtcp")
input(type="imtcp" port="514")

# Enable UDP input (legacy, less reliable)
module(load="imudp")
input(type="imudp" port="514")

# Store logs by hostname
$template RemoteLogs,"/var/log/remote/%HOSTNAME%/%PROGRAMNAME%.log"
*.* ?RemoteLogs

# Or store all remote logs in one file
*.* /var/log/remote/all.log
# Open firewall for syslog
sudo ufw allow 514/tcp
sudo ufw allow 514/udp

# Restart rsyslog
sudo systemctl restart rsyslog

# Verify it's listening
ss -tlnp | grep 514

Forwarding Logs to a Central Server

# /etc/rsyslog.conf โ€” on each client server

# Forward all logs via TCP (@@) to central server
*.* @@logserver.example.com:514

# Forward only errors and above
*.err @@logserver.example.com:514

# Forward specific facility
auth.* @@logserver.example.com:514

# UDP forwarding (single @)
*.* @logserver.example.com:514

# With queue for reliability (don't lose logs if server is down)
*.* action(type="omfwd"
    target="logserver.example.com"
    port="514"
    protocol="tcp"
    action.resumeRetryCount="100"
    queue.type="linkedList"
    queue.size="10000"
    queue.filename="fwdRule1"
    queue.saveOnShutdown="on")

TLS-Encrypted Syslog

Plain syslog sends logs in cleartext. Use TLS for production:

Generate Certificates

# On the log server โ€” create CA and server cert
mkdir -p /etc/rsyslog.d/certs && cd /etc/rsyslog.d/certs

# Create CA
openssl genrsa -out ca.key 4096
openssl req -new -x509 -days 3650 -key ca.key -out ca.crt \
    -subj "/CN=Syslog CA/O=MyOrg"

# Create server certificate
openssl genrsa -out server.key 4096
openssl req -new -key server.key -out server.csr \
    -subj "/CN=logserver.example.com/O=MyOrg"
openssl x509 -req -days 3650 -in server.csr \
    -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt

# Set permissions
chmod 600 server.key ca.key
chmod 644 server.crt ca.crt

TLS Server Configuration

# /etc/rsyslog.conf โ€” log server

# Load TLS module
module(load="imtcp"
    StreamDriver.Name="gtls"
    StreamDriver.Mode="1"
    StreamDriver.AuthMode="anon")

# TLS certificates
global(
    DefaultNetstreamDriver="gtls"
    DefaultNetstreamDriverCAFile="/etc/rsyslog.d/certs/ca.crt"
    DefaultNetstreamDriverCertFile="/etc/rsyslog.d/certs/server.crt"
    DefaultNetstreamDriverKeyFile="/etc/rsyslog.d/certs/server.key"
)

# Listen on TLS port
input(type="imtcp" port="6514")

TLS Client Configuration

# /etc/rsyslog.conf โ€” client

# Copy ca.crt from server to client
# scp logserver:/etc/rsyslog.d/certs/ca.crt /etc/rsyslog.d/certs/

global(
    DefaultNetstreamDriver="gtls"
    DefaultNetstreamDriverCAFile="/etc/rsyslog.d/certs/ca.crt"
)

# Forward with TLS
*.* action(type="omfwd"
    target="logserver.example.com"
    port="6514"
    protocol="tcp"
    StreamDriver="gtls"
    StreamDriverMode="1"
    StreamDriverAuthMode="anon")

Forwarding systemd Journal to Syslog

Modern Linux systems use systemd’s journal. Forward it to syslog:

# /etc/systemd/journald.conf
[Journal]
ForwardToSyslog=yes
MaxLevelSyslog=debug   # forward all levels

# Restart journald
sudo systemctl restart systemd-journald

# Verify forwarding
journalctl -f &
logger "test message"  # should appear in both journal and syslog

Structured Logging with rsyslog

Parse JSON logs from applications:

# /etc/rsyslog.d/json-app.conf

# Template to parse JSON from app logs
template(name="json-template" type="list") {
    property(name="timereported" dateFormat="rfc3339")
    constant(value=" ")
    property(name="hostname")
    constant(value=" ")
    property(name="$!msg")
    constant(value="\n")
}

# Parse JSON messages from myapp
if $programname == 'myapp' then {
    action(type="mmjsonparse")
    action(type="omfile"
        file="/var/log/myapp/structured.log"
        template="json-template")
    stop
}

Sending Logs from Applications

Python

import logging
import logging.handlers

# Send to local syslog
handler = logging.handlers.SysLogHandler(address='/dev/log')
handler.setFormatter(logging.Formatter('%(name)s: %(message)s'))

logger = logging.getLogger('myapp')
logger.addHandler(handler)
logger.setLevel(logging.INFO)

logger.info('Application started')
logger.error('Database connection failed', extra={'db_host': 'db01'})

Go

import "log/syslog"

w, err := syslog.New(syslog.LOG_INFO|syslog.LOG_LOCAL0, "myapp")
if err != nil {
    panic(err)
}
defer w.Close()

w.Info("Application started")
w.Err("Database connection failed")

Shell

# logger command sends to syslog
logger "Backup completed successfully"
logger -p local0.err "Backup failed: disk full"
logger -t myapp -p daemon.warning "Config file missing"

# With structured data
logger -p local0.info -t myapp "user_login user=alice ip=192.168.1.1"

Log Rotation

# /etc/logrotate.d/rsyslog
/var/log/syslog
/var/log/remote/*/*.log
{
    rotate 7
    daily
    missingok
    notifempty
    delaycompress
    compress
    postrotate
        /usr/lib/rsyslog/rsyslog-rotate
    endscript
}

Integrating with Modern Log Stacks

Forward to Elasticsearch (ELK)

# /etc/rsyslog.d/elasticsearch.conf
module(load="omelasticsearch")

template(name="es-template" type="list") {
    constant(value="{")
    constant(value="\"@timestamp\":\"")
    property(name="timereported" dateFormat="rfc3339")
    constant(value="\",\"host\":\"")
    property(name="hostname")
    constant(value="\",\"message\":\"")
    property(name="msg" format="json")
    constant(value="\",\"severity\":\"")
    property(name="syslogseverity-text")
    constant(value="\"}")
}

*.* action(type="omelasticsearch"
    server="elasticsearch.example.com"
    serverport="9200"
    template="es-template"
    searchIndex="syslog-%timereported:1:10:date-rfc3339%"
    bulkmode="on"
    queue.type="linkedList"
    queue.size="5000")

Forward to Grafana Loki

# Use promtail to forward syslog to Loki
# /etc/promtail/config.yml
scrape_configs:
  - job_name: syslog
    syslog:
      listen_address: 0.0.0.0:1514
      labels:
        job: syslog
    relabel_configs:
      - source_labels: [__syslog_message_hostname]
        target_label: host
      - source_labels: [__syslog_message_app_name]
        target_label: app

# Forward rsyslog to promtail
# /etc/rsyslog.d/loki.conf
*.* action(type="omfwd" target="127.0.0.1" port="1514" protocol="tcp"
    template="RSYSLOG_SyslogProtocol23Format")

Troubleshooting

# Test rsyslog config syntax
rsyslogd -N1

# Send a test message
logger -p local0.info "test message from $(hostname)"

# Watch syslog in real-time
tail -f /var/log/syslog

# Check rsyslog is running
systemctl status rsyslog

# Check what rsyslog is listening on
ss -tlnp | grep rsyslog

# Debug rsyslog (verbose output)
rsyslogd -dn 2>&1 | head -50

Resources

Comments