diff -r 087b52a53bea daemon.py --- a/daemon.py Thu Oct 14 12:07:08 2010 +0200 +++ b/daemon.py Tue Oct 19 11:03:48 2010 +0200 @@ -18,6 +18,8 @@ """ Daemon process behaviour. """ +print ">>>> you are working with a modified version of 'daemon'" + import os import sys import resource @@ -25,6 +27,7 @@ import signal import socket import atexit +from logging import Logger class DaemonError(Exception): @@ -187,16 +190,17 @@ `stderr` :Default: ``None`` - Each of `stdin`, `stdout`, and `stderr` is a file-like object - which will be used as the new file for the standard I/O stream - `sys.stdin`, `sys.stdout`, and `sys.stderr` respectively. The file - should therefore be open, with a minimum of mode 'r' in the case - of `stdin`, and mode 'w+' in the case of `stdout` and `stderr`. + Each of `stdin`, `stdout`, and `stderr` is a file-like object or + instance of logging.Logger which will be used as the new + destination for the standard I/O stream `sys.stdin`, `sys.stdout`, + and `sys.stderr` respectively. The file/logger should therefore be + open, with a minimum of mode 'r' in the case of `stdin`, and mode + 'w+' in the case of `stdout` and `stderr`. If the object has a `fileno()` method that returns a file - descriptor, the corresponding file will be excluded from being - closed during daemon start (that is, it will be treated as though - it were listed in `files_preserve`). + descriptor or is a logging.Logger, the corresponding file will be + excluded from being closed during daemon start (that is, it will be + treated as though it were listed in `files_preserve`). If ``None``, the corresponding system stream is re-bound to the file named by `os.devnull`. @@ -411,15 +415,24 @@ * If the item has a ``fileno()`` method, that method's return value is in the return set. + * If the item is an instance of logging.Logger the + fileno() of the stream associated with the logger + is used for file-based loggers + * Otherwise, the item is in the return set verbatim. """ files_preserve = self.files_preserve if files_preserve is None: files_preserve = [] - files_preserve.extend( - item for item in [self.stdin, self.stdout, self.stderr] - if hasattr(item, 'fileno')) + for item in [self.stdin, self.stdout, self.stderr]: + if hasattr(item, 'fileno'): + files_preserve.append(item) + elif isinstance(item, Logger): + for handler in item.handlers: + if hasattr(handler, 'stream') and \ + hasattr(handler.stream, 'fileno'): + files_preserve.append(handler.stream) exclude_descriptors = set() for item in files_preserve: if item is None: @@ -463,6 +476,28 @@ return signal_handler_map +class FileLikeLogger: + "wraps a logging.Logger into a file like object" + + def __init__(self, logger): + self.logger = logger + + def write(self, str): + str = str.rstrip() #get rid of all tailing newlines and white space + if str: #don't log emtpy lines + for line in str.split('\n'): + self.logger.critical(line) #critical to log at any logLevel + + def flush(self): + for handler in self.logger.handlers: + handler.flush() + + def close(self): + for handler in self.logger.handlers: + handler.close() + + + def change_working_directory(directory): """ Change the working directory of this process. """ @@ -720,18 +755,30 @@ """ Redirect a system stream to a specified file. `system_stream` is a standard system stream such as - ``sys.stdout``. `target_stream` is an open file object that - should replace the corresponding system stream object. + ``sys.stdout``. `target_stream` is an open file object + or instance of logging.Logger that should replace the corresponding + system stream object. + + In the case of a logger, the logger is wrapped in an instance + of a file like object before redirecting. If `target_stream` is ``None``, defaults to opening the operating system's null device and using its file descriptor. """ - if target_stream is None: + streamName = system_stream.name + itsALogger = isinstance(target_stream, Logger) + if target_stream is None or itsALogger: target_fd = os.open(os.devnull, os.O_RDWR) else: target_fd = target_stream.fileno() - os.dup2(target_fd, system_stream.fileno()) + os.dup2(target_fd, system_stream.fileno()) # this also applies for loggers + if itsALogger: + fileLikeObj = FileLikeLogger(target_stream) + if streamName == "": + sys.stdout = fileLikeObj + elif streamName == "": + sys.stderr = fileLikeObj def make_default_signal_map(): diff -r 087b52a53bea test.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test.py Tue Oct 19 11:03:48 2010 +0200 @@ -0,0 +1,29 @@ +#!/usr/bin/env python + +import daemon +import logging +import logging.handlers + + +def getRotFileLogger(name, filePath, logLevel=logging.DEBUG, format=None): + format = format or '%(message)s' + my_logger = logging.getLogger(name) + my_logger.setLevel(logLevel) + handler = logging.handlers.RotatingFileHandler( + filePath, maxBytes=2000, backupCount=2) + formatter = logging.Formatter(format) + handler.setFormatter(formatter) + my_logger.addHandler(handler) + return my_logger + + +if __name__ == '__main__': + + context = daemon.DaemonContext() + #context.stdout = open('stdout.file', 'w+') + #context.stderr = open('stderr.file', 'w+') + context.stdout = getRotFileLogger('stdout', 'stdout.log') + context.stderr = getRotFileLogger('stderr', 'stderr.log') + with context: + print "hello!" + raise (Exception("bummer!"))