Forwarding Linux Syslog to BunnyLogs

Stream every log line from your Linux system — kernel messages, service output, auth events — into your BunnyLogs feed in real time.

Two approaches

Modern Linux systems log through systemd's journal (journald) and, on most distributions, also through rsyslog which reads from the journal. Pick the approach that fits your setup:

ApproachBest for
journald watcher Any systemd-based system — Python script using the bunnylogs package
rsyslog omhttp Systems already using rsyslog; native module, no scripting
Option 1 — journald watcher (Python)

This script reads from journalctl -f -o json and forwards every new journal entry to BunnyLogs via the bunnylogs package. Each journal source (nginx, sshd, kernel, …) appears as a separate program in your log stream. Works on any systemd-based distribution (Ubuntu, Debian, Fedora, Arch, etc.).

Install the package

pip install bunnylogs

Save the script as /usr/local/bin/bunnylogs-syslog.py:

#!/usr/bin/env python3
"""Forward systemd journal entries to BunnyLogs."""
import json
import logging
import subprocess

from bunnylogs import BunnyLogsHandler

UUID = "<uuid>"   # replace with your logspace UUID

PRIORITY_MAP = {
    0: logging.CRITICAL,  # emergency
    1: logging.CRITICAL,  # alert
    2: logging.CRITICAL,  # critical
    3: logging.ERROR,     # error
    4: logging.WARNING,   # warning
    5: logging.INFO,      # notice
    6: logging.INFO,      # informational
    7: logging.DEBUG,     # debug
}

_loggers: dict[str, logging.Logger] = {}


def get_logger(program: str) -> logging.Logger:
    if program not in _loggers:
        logger = logging.getLogger(program)
        logger.setLevel(logging.DEBUG)
        logger.addHandler(BunnyLogsHandler(UUID))
        logger.propagate = False
        _loggers[program] = logger
    return _loggers[program]


def main() -> None:
    proc = subprocess.Popen(
        ["journalctl", "--follow", "--output=json", "--lines=0"],
        stdout=subprocess.PIPE,
        text=True,
    )
    for line in proc.stdout:
        try:
            entry = json.loads(line)
        except json.JSONDecodeError:
            continue
        message = entry.get("MESSAGE", "")
        if not message or not isinstance(message, str):
            continue  # skip binary/empty journal fields
        priority = int(entry.get("PRIORITY", 6))
        level = PRIORITY_MAP.get(priority, logging.INFO)
        program = (
            entry.get("SYSLOG_IDENTIFIER")
            or entry.get("_COMM")
            or "syslog"
        )
        get_logger(program).log(level, message)


if __name__ == "__main__":
    main()
chmod +x /usr/local/bin/bunnylogs-syslog.py

Test it manually first (you should see new log entries appear in BunnyLogs as they arrive):

python3 /usr/local/bin/bunnylogs-syslog.py
Run as a systemd service

Create /etc/systemd/system/bunnylogs-syslog.service:

[Unit]
Description=BunnyLogs syslog forwarder
After=network-online.target systemd-journald.service
Wants=network-online.target

[Service]
ExecStart=/usr/bin/python3 /usr/local/bin/bunnylogs-syslog.py
Restart=on-failure
RestartSec=10
StandardOutput=null
StandardError=journal

[Install]
WantedBy=multi-user.target
systemctl daemon-reload
systemctl enable --now bunnylogs-syslog
systemctl status bunnylogs-syslog

Option 2 — rsyslog omhttp

rsyslog's omhttp output module POSTs log messages to an HTTP endpoint natively — no scripting required. This is the right choice if rsyslog is already your primary log daemon.

Install the omhttp module

# Debian / Ubuntu
apt install rsyslog-omhttp

# RHEL / Fedora / CentOS
dnf install rsyslog-omhttp

Create /etc/rsyslog.d/bunnylogs.conf

# Load the HTTP output module
module(load="omhttp")

# Template: JSON body that matches BunnyLogs field names
template(name="BunnyLogsJSON" type="list") {
  constant(value="{")
  constant(value="\"message\":")     property(name="msg"         format="json")
  constant(value=",\"program\":")    property(name="programname" format="json")
  constant(value="}")
}

# Forward all messages of warning severity and above
*.warning action(
    type           = "omhttp"
    server         = "bunnylogs.com"
    serverport     = "443"
    usehttps       = "on"
    restpath       = "/live/<uuid>"
    template       = "BunnyLogsJSON"
    httpcontenttype = "application/json"
    action.resumeRetryCount = "3"
)

Change *.warning to *.* to forward all severity levels, or use a facility selector like auth,authpriv.* to forward only authentication events.

Reload rsyslog

rsyslogd -N1                      # syntax check
systemctl restart rsyslog

Verify

# Generate a test message — it should appear in BunnyLogs within seconds
logger -p user.warning "BunnyLogs test message from rsyslog"

Filtering

Both approaches forward every journal entry by default. Common ways to narrow the feed:

  • journald watcher — pass extra journalctl flags when spawning the subprocess, e.g. --identifier=nginx to follow only nginx messages, or --priority=warning to skip INFO and DEBUG.
  • rsyslog — change the selector (kern.*, auth.*, *.err, etc.) or add an if filter inside the action block to match on the program name.
Tips
  • Use the program field (populated from SYSLOG_IDENTIFIER / programname) to filter per service in the BunnyLogs dashboard — you'll see sshd, nginx, kernel, etc. as distinct sources.
  • The journald watcher uses --lines=0 so it only picks up new entries after it starts, avoiding a flood of historical messages on first run.
  • On a busy server, consider forwarding only warning and above to stay within your monthly ingest cap.
  • If the BunnyLogs endpoint is temporarily unreachable, the journald watcher reconnects automatically (systemd Restart=on-failure); rsyslog retries via action.resumeRetryCount.
  • To forward logs from multiple servers into the same logspace, run the same service on each machine and use a different program name (e.g. set SYSLOG_IDENTIFIER or override program in the script) to distinguish them.