I would like to create a python daemon, completely detaching from the terminal or parent process, and yet retaining any log handlers through the python logging module. There is a wonderful example at cookbook-278731 of a well-behaved daemon, and see PEP 3143.
Borrowing from these examples, here is a simple daemonize function that will retain thread-safe logging handlers that were setup through the logging module. This is also available via github.
# vim: set tabstop=4 shiftwidth=4 autoindent smartindent: ''' Daemon (python daemonize function) Detach process through double-fork (to avoid zombie process), close all file descriptors, and set working directory to root (to avoid umount problems) ''' import os, resource import logging # target environment UMASK = 0 WORKINGDIR = '/' MAXFD = 1024 if (hasattr(os, "devnull")): REDIRECT_TO = os.devnull else: REDIRECT_TO = "/dev/null" def daemonize(): '''Detach this process and run it as a daemon''' try: pid = os.fork() #first fork except OSError, e: raise Exception, "%s [%d]" % (e.strerror, e.errno) if (pid == 0): #first child os.setsid() try: pid = os.fork() #second fork except OSError, e: raise Exception, "%s [%d]" % (e.strerror, e.errno) if (pid == 0): #second child os.chdir(WORKINGDIR) os.umask(UMASK) else: os._exit(0) else: os._exit(0) #close all file descriptors except from non-console logging handlers maxfd = resource.getrlimit(resource.RLIMIT_NOFILE)[1] if (maxfd == resource.RLIM_INFINITY): maxfd = MAXFD filenos = [] for handler in logging.root.handlers: if hasattr(handler, 'stream') and hasattr(handler.stream, 'fileno') and handler.stream.fileno() > 2: filenos.append( handler.stream.fileno() ) for fd in range(0, maxfd): try: if fd not in filenos: os.close(fd) except OSError: pass #redirect stdin, stdout, stderr to null os.open(REDIRECT_TO, os.O_RDWR) os.dup2(0, 1) os.dup2(0, 2) return(0)
Simply call the daemonize() function within your application, e.g.,
import logging import Daemon logging.info('daemonize?') Daemon.daemonize(): logging.info('daemonized! we are now safely detached')
From a shell, console logging will only appear before daemonize() was called, e.g.,
# python test/daemon.py INFO:daemon.py:4 -- daemonize? #
And the logfile output:
# cat test.log 2011-09-27 13:26:02,712 INFO:daemon.py:4 -- daemonize? 2011-09-27 13:26:02,717 INFO:daemon.py:6 -- daemonized! we are now safely detached #