Python's stdout buffering and systemd
Running a Python script as a Systemd service¶
This is very simple, first we need to create a systmed "unit", in /etc/systemd/system/foo.service
[Unit]
Description=Abracadabra
After=network.target
[Service]
Type=simple
User=bob
Restart=on-failure
RestartSec=3
ExecStart=/usr/bin/python3 /home/bob/test_daemon.py
[Install]
WantedBy=multi-user.target
Then, we can enable and start the service with
systemctl enable foo.service
systemctl start foo
What happens to the stdout and stderr?¶
They can be checked by calling journalctl -u foo
. Assuming test_daemon.py
was:
from datetime import datetime
from time import sleep
import sys
print("Started abracadabra")
while True:
curtime = datetime.now().strftime('%X')
print("Hi! Current time is: ", curtime)
sleep(2)
we should see
$ sudo journalctl -u foo
Okt 09 21:07:18 etna python3[13499]: Started abracadabra
Okt 09 21:07:18 etna python3[13499]: Hi! Current time is: 21:07:18
Okt 09 21:07:20 etna python3[13499]: Hi! Current time is: 21:07:20
Okt 09 21:07:22 etna python3[13499]: Hi! Current time is: 21:07:22
Okt 09 21:07:24 etna python3[13499]: Hi! Current time is: 21:07:24
Okt 09 21:07:26 etna python3[13499]: Hi! Current time is: 21:07:26
Okt 09 21:07:28 etna python3[13499]: Hi! Current time is: 21:07:28
Okt 09 21:07:30 etna python3[13499]: Hi! Current time is: 21:07:30
Redirecting stdout/stderr to a file¶
We can ask Systemd to do this by adding these lines in /etc/systemd/system/foo.service
[Service]
---snip---
ExecStart=/usr/bin/python3 /home/bob/test_daemon.py
StandardOutput=file:/var/log/foo/stdout
StandardError=file:/var/log/foo/stderr
---snip---
But nothing gets written to /var/log/foo/stdout
!¶
This is because python buffers stdout if it is not a console. If we really want to see the stdout of our daemon as soon as they are printed, we need to disable this buffering. This can be achieved by starting python with -u
flag. So, the systemd unit file changes to:
[Unit]
Description=Abracadabra
After=network.target
[Service]
Type=simple
User=bob
Restart=on-failure
RestartSec=3
ExecStart=/usr/bin/python3 /home/bob/test_daemon.py
StandardOutput=file:/var/log/foo/stdout
StandardError=file:/var/log/foo/stderr
[Install]
WantedBy=multi-user.target
That's it! Now /var/log/foo/stdout
should be populated with whatever test_daemon.py
prints.