1---2title: 'Building a python daemon process'3date: '2012-06-18'4published_at: '2012-06-18T13:50:00.003+10:00'5tags: ['daemon', 'init scripts', 'logging', 'programming', 'python']6author: 'Gavin Jackson'7excerpt: 'This blog post is a quick introduction to building a python daemon process. This sample builds a true Unix daemon process, with full logging and an optional init script (which has been tested on SLES...'8updated_at: '2012-06-18T13:51:54.342+10:00'9legacy_url: 'http://www.gavinj.net/2012/06/building-python-daemon-process.html'10---11121314This blog post is a quick introduction to building a python daemon process. This sample builds a true Unix daemon process, with full logging and an optional init script (which has been tested on SLES 11 SP2 but should work on other Redhat based distributions).1516A Unix daemon has some fundamental characteristics (it's a lot more than just a background process), these are listed in *PEP 3143 Standard Daemon Process Library *[http://www.python.org/dev/peps/pep-3143/#correct-daemon-behaviour](http://www.python.org/dev/peps/pep-3143/#correct-daemon-behaviour).1718Cron may be a suitable alternative to this approach, however a daemon allows you to have a single, long lived process that can maintain state, allows you to send process signals to, a guarantee that you only have one instance in execution (which can be a gotcha with poorly written cron scripts that don't implement lock files), by modifying the INIT INFO section of the init script you can also control where in the system boot up the daemon commences execution.1920### Step 1 - Required Libraries2122Install the Python Daemon package from ([http://pypi.python.org/pypi/python-daemon/](http://pypi.python.org/pypi/python-daemon/)) - note that you will also need an earlier version of the python lockfile (version 0.8) to work ([http://pypi.python.org/pypi/lockfile/0.9.1](http://pypi.python.org/pypi/lockfile/0.9.1)).2324### Step 2 - Sample Code2526`# To kick off the script, run the following from the python directory:`2728```29# PYTHONPATH=`pwd` python testdaemon.py start30```3132```33#standard python libs34import logging35```3637```38import time3940#third party libs41```42`from daemon import runner`4344`class App():`4546```4748 def __init__(self):49 self.stdin_path = '/dev/null'50 self.stdout_path = '/dev/tty'51 self.stderr_path = '/dev/tty'52```53****`self.pidfile_path = '/var/run/testdaemon/testdaemon.pid'`5455```56 self.pidfile_timeout = 557```5859```60 def run(self):6162 while True:63 #Main code goes here ...64```6566```67 #Note that logger level needs to be set to logging.DEBUG before this shows up in the logs68```6970`logger.debug("Debug message")`7172```73 logger.info("Info message")74```7576```77 logger.warn("Warning message")78```7980`logger.error("Error message")`8182```83 time.sleep(10)84```8586```87app = App()88logger = logging.getLogger("DaemonLog")89logger.setLevel(logging.INFO)90formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")91```92****`handler = logging.FileHandler("/var/log/testdaemon/testdaemon.log")`9394```95handler.setFormatter(formatter)96logger.addHandler(handler)97```9899```100daemon_runner = runner.DaemonRunner(app)101```102103```104#This ensures that the logger file handle does not get closed during daemonization105daemon_runner.daemon_context.files_preserve=[handler.stream]106daemon_runner.do_action()107```108109Save this script under **/usr/share/testdaemon/testdaemon.py**110111You need to ensure that the **/var/log/testdaemon** and** /var/run/testdaemon** folders exist and the user running the daemon process has write permissions.112113### **Step 3 - Init Script**114115You can run the script by running **python testdaemon.py start**116117This will spawn the daemon process. You can stop it by running **daemon.py stop**118119The following init script will allow you to start and stop the daemon on system startup and shutdown respectively:120121```122#! /bin/bash123```124125`# Copyright (c) 1996-2012 My Company.`126127```128# All rights reserved.129#130131# Author: Bob Bobson, 2012132```133`#`134135```136# Please send feedback to bob@bob.com137#138139# /etc/init.d/testdaemon140#141142### BEGIN INIT INFO143# Provides: testdaemon144# Required-Start:145# Should-Start:146# Required-Stop:147# Should-Stop:148# Default-Start: 3 5149# Default-Stop: 0 1 2 6150# Short-Description: Test daemon process151# Description: Runs up the test daemon process152### END INIT INFO153```154155```156# Activate the python virtual environment157```158****`. /path_to_virtualenv/activate`159160```161case "$1" in162 start)163 echo "Starting server"164165 # Start the daemon166```167`python``/usr/share/testdaemon/testdaemon.py``start`168169```170 ;;171 stop)172 echo "Stopping server"173174 # Stop the daemon175```176177`python``/usr/share/testdaemon/testdaemon.py``stop`178179`;;`180181```182 restart)183 echo "Restarting server"184```185186```187 python /usr/share/testdaemon/testdaemon.py restart188```189190`;;`191192```193 *)194195 # Refuse to do other stuff196 echo "Usage: /etc/init.d/testdaemon.sh {start|stop|restart}"197 exit 1198 ;;199esac200```201202```203exit 0204```205206Note that this is using python virtualenv to use an isolated python environment (please refer to my previous blog entry [here](http://www.gavinj.net/2012/05/python-virtualenv-is-your-friend.html)).207208Save file in **/etc/init.d/testdaemon**209210**chmod u+x ****/etc/init.d/testdaemon**211212Enable using **chkconfig testdaemon on**213214Check that it is enabled **chkconfig --list**215216...217218testdaemon 0:off 1:off 2:off **3:on** 4:off **5:on** 6:off219220...221222Kick it off by running **service testdaemon start**223224You will now see it running in the background:225226```227ps -aux|grep testdaemon228root 15225 0.0 0.0 64304 5948 ? S 13:10 0:00 python /usr/share/testdaemon/testdaemon.py start229```230231### Step 4 - Example Usage232233```234(pyenv-zenkai)rama:/usr/share/testdaemon # python ./testdaemon.py start235(pyenv-zenkai)rama:/usr/share/testdaemon # started with pid 14565236```237238```239(pyenv-zenkai)rama:/usr/share/testdaemon # cat /var/log/testdaemon/testdaemon.log2402012-06-18 12:57:22,512 - DaemonLog - INFO - Info message2412012-06-18 12:57:22,512 - DaemonLog - WARNING - Warning message2422012-06-18 12:57:22,512 - DaemonLog - ERROR - Error message2432012-06-18 12:58:19,770 - DaemonLog - INFO - Info message2442012-06-18 12:58:19,771 - DaemonLog - WARNING - Warning message2452012-06-18 12:58:19,771 - DaemonLog - ERROR - Error message2462012-06-18 12:58:29,779 - DaemonLog - INFO - Info message2472012-06-18 12:58:29,779 - DaemonLog - WARNING - Warning message2482012-06-18 12:58:29,780 - DaemonLog - ERROR - Error message249```250251...252253```254(pyenv-zenkai)rama:/usr/share/testdaemon # python ./testdaemon.py stop255Terminating on signal 15256(pyenv-zenkai)rama:/usr/share/testdaemon #257```258259260