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:
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 contentSYSLOG_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:
Checking logs:
Filtering by level:
Show full data (including PRIORITY
):
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
injournald
is the logger name (my_app
). - In
journalctl
, you can read these logs with:
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
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:
β Summary
systemd.journal.send()
enables quick loggingPRIORITY
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