Introduction to python Library - python daemon: a tool to implement python background program

Keywords: Python Unix Linux shell

brief introduction

Python daemon implements Unix daemons. Reference resources: PEP 3143

This library implements the good behavior daemons specification of PEP 3143 "standard daemons library".

The DaemonContext instance stores the behavior of the program and the configured process environment.

quick get start


import time

with daemon.DaemonContext():
    f = open("/tmp/test.log",'w')
    while True:
        f.write('''
        Library to implement a well-behaved Unix daemon process.

This library implements the well-behaved daemon specification of PEP 3143, "Standard daemon process library".

A well-behaved Unix daemon process is tricky to get right, but the required steps are much the same for every daemon program. A DaemonContext instance holds the behaviour and configured process environment for the program; use the instance as a context manager to enter a daemon state.
''')
        f.write("{0}\n".format(time.ctime(time.time())))
        time.sleep(1)

Implementation:


$ python3 daemon1.py 

$ tail -f /tmp/test.log 
This library implements the well-behaved daemon specification of PEP 3143, "Standard daemon process library".

A well-behaved Unix daemon process is tricky to get right, but the required steps are much the same for every daemon program. A DaemonContext instance holds the behaviour and configured process environment for the program; use the instance as a context manager to enter a daemon state.
Thu Feb  8 14:21:43 2018

$ ps afx | grep -i daemon1
 8646 pts/2    S+     0:00          |           \_ grep --color=auto -i daemon1
 8640 ?        S      0:00          \_ python3 daemon1.py
$ kill -9 8640

To stop the above process, you can find the process number through ps, and then kill.

Note that the above code has no problem in python2, but in Python, you need to modify the way the library file runner.py opens the file.


# vi /usr/local/lib/python3.5/dist-packages/daemon/runner.py 
# 118 -120
        self.daemon_context = DaemonContext()
        self.daemon_context.stdin = open(app.stdin_path, 'wb+',buffering=0)
        self.daemon_context.stdout = open(app.stdout_path,  'wb+',buffering=0)
        self.daemon_context.stderr = open(
                app.stderr_path, 'wb+', buffering=0)

More practical examples


import time
import logging
import logging.handlers
from daemon import runner

class App():
    
    def __init__(self):
        self.stdin_path = '/dev/null'
        self.stdout_path = '/dev/tty'
        self.stderr_path = '/dev/tty'
        self.pidfile_path =  '/tmp/foo.pid'
        self.pidfile_timeout = 5
        
    def run(self):
        
        logs = logging.getLogger('MyLogger')
        logs.setLevel(logging.DEBUG)
        fh = logging.handlers.RotatingFileHandler(
            '/tmp/test.log',maxBytes=10000000,backupCount=5)
        fh.setLevel(logging.DEBUG)
        formatter = logging.Formatter(u'%(asctime)s [%(levelname)s] %(message)s')
        fh.setFormatter(formatter)
        logs.addHandler(fh)  
        
        while True:
            for i in range(10):
                logs.info("Beginning Scan {0}! \n".format(i))
            time.sleep(1)

app = App()
daemon_runner = runner.DaemonRunner(app)
daemon_runner.do_action()

Implementation:


$ python2 daemon2.py 
usage: daemon2.py start|stop|restart

$ python3 daemon2.py  start
andrew@andrew-MS-7A71:~/code/python-chinese-library/libraries/daemon$ 
andrew@andrew-MS-7A71:~/code/python-chinese-library/libraries/daemon$ Traceback (most recent call last):
  File "/usr/local/lib/python3.5/dist-packages/lockfile/pidlockfile.py", line 77, in acquire
    write_pid_to_pidfile(self.path)
  File "/usr/local/lib/python3.5/dist-packages/lockfile/pidlockfile.py", line 161, in write_pid_to_pidfile
    pidfile_fd = os.open(pidfile_path, open_flags, open_mode)
FileExistsError: [Errno 17] File exists: '/tmp/foo.pid'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "daemon2.py", line 39, in <module>
    daemon_runner.do_action()
  File "/usr/local/lib/python3.5/dist-packages/daemon/runner.py", line 274, in do_action
    func(self)
  File "/usr/local/lib/python3.5/dist-packages/daemon/runner.py", line 182, in _start
    self.daemon_context.open()
  File "/usr/local/lib/python3.5/dist-packages/daemon/daemon.py", line 389, in open
    self.pidfile.__enter__()
  File "/usr/local/lib/python3.5/dist-packages/lockfile/__init__.py", line 197, in __enter__
    self.acquire()
  File "/usr/local/lib/python3.5/dist-packages/daemon/pidfile.py", line 60, in acquire
    super(TimeoutPIDLockFile, self).acquire(timeout, *args, **kwargs)
  File "/usr/local/lib/python3.5/dist-packages/lockfile/pidlockfile.py", line 85, in acquire
    self.path)
lockfile.LockTimeout: Timeout waiting to acquire lock for /tmp/foo.pid

andrew@andrew-MS-7A71:~/code/python-chinese-library/libraries/daemon$ python3 daemon2.py  stop
Terminating on signal 15
andrew@andrew-MS-7A71:~/code/python-chinese-library/libraries/daemon$ python3 daemon2.py  stop
Traceback (most recent call last):
  File "daemon2.py", line 39, in <module>
    daemon_runner.do_action()
  File "/usr/local/lib/python3.5/dist-packages/daemon/runner.py", line 274, in do_action
    func(self)
  File "/usr/local/lib/python3.5/dist-packages/daemon/runner.py", line 224, in _stop
    raise error
daemon.runner.DaemonRunnerStopFailureError: PID file '/tmp/foo.pid' not locked

Note that the above error is caused by repeated start or stop when the process does not exist.

Reference material

A way of old style writing background process


import time
import logging
import logging.handlers


logs = logging.getLogger('MyLogger')
logs.setLevel(logging.DEBUG)
fh = logging.handlers.RotatingFileHandler(
    '/tmp/test.log',maxBytes=10000000,backupCount=5)
fh.setLevel(logging.DEBUG)
formatter = logging.Formatter(u'%(asctime)s [%(levelname)s] %(message)s')
fh.setFormatter(formatter)
logs.addHandler(fh)  


while True:
    for i in range(10):
        logs.info("Beginning Scan {0}! \n".format(i))
    time.sleep(1)      

In this way, it can't be executed in the background, but you can use the nohup and &. Add the following start and stop scripts for this purpose, which is not troublesome in fact:

For shell scripts, see: http://t.cn/R8scWAe


$ sh startup.sh 
================================================================================================================
Starting older.py(PID=15315)...[Success]
================================================================================================================
$ sh startup.sh 
================================================================================================================
older.py already started(PID=15315)
================================================================================================================
$ sh shutdown.sh 
================================================================================================================
Stopping older.py(PID=15315)...[Success]
================================================================================================================
$ sh shutdown.sh 
================================================================================================================
older.py is not running
================================================================================================================

Other

supervisord linux can control the linux process, of course, it can also be backstage.

How do you create a daemon in Python?

You can also register with Linux system D as a background process.

Posted by TCovert on Mon, 30 Dec 2019 08:27:19 -0800