Skip to content

Logging to journald from Python

When creating system tools, daemons, or services running on systemd based systems, it's a good idea to log events directly to journald. This way, logs are sent to the central system journal.

In this article, I'll show how to log to journald in Python both using the systemd module and the standard logging library with journald support.


Installing the systemd-python module

To use the direct journald API, install the library:

pip install systemd-python

Method 1: systemd.journal.send()

The simplest way to log. Example:

from systemd import journal

journal.send("Something happened",
             SYSLOG_IDENTIFIER="my_app",
             PRIORITY=5)

Parameters:

  • MESSAGE - the first argument, log message content
  • SYSLOG_IDENTIFIER - log source identifier (e.g., app name or unit file)
  • PRIORITY - importance level (number from 0 to 7)

PRIORITY levels:

Number Name Description
0 emerg Critical - system is unusable
1 alert Requires immediate action
2 crit Critical error
3 err Error
4 warning Warning
5 notice Important information
6 info Informational
7 debug Debugging

Note: PRIORITY should be a number, e.g., PRIORITY=3, not a string like "err". Using a string may not work.


Reading logs in journalctl πŸ”

Example logging:

journal.send("Something went wrong",
             SYSLOG_IDENTIFIER="my_app",
             PRIORITY=3)

Checking logs:

journalctl -t my_app

Filtering by level:

journalctl -t my_app -p err

Show full data (including PRIORITY):

journalctl -t my_app -o verbose

Method 2: standard logging with JournalHandler

For larger Python applications, it's recommended to use the built-in logging module with the JournalHandler. This approach:

  • enables centralized logging management across the app,
  • allows flexible addition of various logging backends (file, stdout, journald, etc.),
  • is compatible with frameworks (Django, Flask, Celery, FastAPI, etc.),
  • allows external configuration (e.g., via logging.conf or YAML)

Example usage:

import logging
from systemd.journal import JournalHandler

logger = logging.getLogger("my_app")
logger.addHandler(JournalHandler())
logger.setLevel(logging.DEBUG)

logger.debug("App debug")
logger.info("Info")
logger.warning("Warning!")
logger.error("Error occurred")
logger.critical("Critical failure")
  • By default, SYSLOG_IDENTIFIER in journald is the logger name (my_app).
  • In journalctl, you can read these logs with:
journalctl -t my_app

Logging levels πŸ”Ž

JournalHandler automatically maps logging levels to syslog (PRIORITY) levels:

logging level journald PRIORITY Syslog name
DEBUG 7 debug
INFO 6 info
WARNING 4 warning
ERROR 3 err
CRITICAL 2 crit

πŸ›‘ There is no support for PRIORITY=1 (alert) or 0 (emerg). If you need these levels, use journal.send()


Why use logging + JournalHandler in larger applications?

  • βœ… Centralized logging logic - configure once, works everywhere
  • βœ… Flexibility - you can add extra handlers (files, syslog, email)
  • βœ… Framework compatibility - many Python libraries and frameworks use logging
  • βœ… External configuration - load settings from a file

If your app is a simple CLI script or a single daemon, you can use journal.send() directly. But if you're building something biggerβ€”definitely use logging + JournalHandler.


Troubleshooting (no logs) πŸ› οΈ

1. No logs in journalctl -p emerg?

Check if:

journal.send("Test", PRIORITY=0)  # βœ… correct
journal.send("Test", PRIORITY="emerg")  # ❌ will NOT work

2. Use -t instead of -p for testing

journalctl -t my_app -o verbose

This will show logs regardless of level

3. Run as root (if needed)

Some levels may be skipped in a sandbox or with limited permissions:

sudo python3 my_app.py

βœ… Summary

  • systemd.journal.send() enables quick logging
  • PRIORITY is a number from 0 to 7
  • standard logging can be hooked to journald
  • journalctl allows filtering logs by identifier (-t) and level (-p)
  • for debugging, use -o verbose