#!/usr/bin/env python import os import sys from logging import Logger import daemon 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 openFilesFromLoggers(loggers): "returns the open files used by file-based handlers of the specified loggers" openFiles = [] for logger in loggers: for handler in logger.handlers: if hasattr(handler, 'stream') and \ hasattr(handler.stream, 'fileno'): openFiles.append(handler.stream) return openFiles class LoggingDaemonContext(daemon.DaemonContext): def _addLoggerFiles(self): "adds all files related to loggers_preserve to files_preserve" for logger in [self.stdout_logger, self.stderr_logger]: if logger: self.loggers_preserve.append(logger) loggerFiles = openFilesFromLoggers(self.loggers_preserve) self.files_preserve.extend(loggerFiles) def __init__( self, chroot_directory=None, working_directory='/', umask=0, uid=None, gid=None, prevent_core=True, detach_process=None, files_preserve=[], # changed default loggers_preserve=[], # new pidfile=None, stdout_logger = None, # new stderr_logger = None, # new #stdin, omitted! #stdout, omitted! #sterr, omitted! signal_map=None, ): self.stdout_logger = stdout_logger self.stderr_logger = stderr_logger self.loggers_preserve = loggers_preserve devnull_in = open(os.devnull, 'r+') devnull_out = open(os.devnull, 'w+') files_preserve.extend([devnull_in, devnull_out]) daemon.DaemonContext.__init__(self, chroot_directory = chroot_directory, working_directory = working_directory, umask = umask, uid = uid, gid = gid, prevent_core = prevent_core, detach_process = detach_process, files_preserve = files_preserve, pidfile = pidfile, stdin = devnull_in, stdout = devnull_out, stderr = devnull_out, signal_map = signal_map) def open(self): self._addLoggerFiles() daemon.DaemonContext.open(self) if self.stdout_logger: fileLikeObj = FileLikeLogger(self.stdout_logger) sys.stdout = fileLikeObj if self.stderr_logger: fileLikeObj = FileLikeLogger(self.stderr_logger) sys.stderr = fileLikeObj #--------------------------------------------------------------- if __name__ == '__main__': # since this test uses chroot, it should be called as superuser (sudo..) import logging import logging.handlers import random import time import urllib2 #-- setting up a rotating file logger ------- 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 #-- some utilities --- def rmTestFiles(*fList): for f in fList: rmTestFile(f) def rmTestFile(fileName): if os.path.isfile(fileName): os.remove(fileName) def rmTestDir(dirName): if os.path.isdir(dirName): os.rmdir(dirName) #-- clean up the test directory and file structure ---- rmTestFile('jail/beacon.txt') rmTestFile('jail/jailTestFile.txt') rmTestDir('jail') rmTestFiles('test.log', 'stdout.log', 'stderr.log', 'test.file') os.mkdir(os.path.join(os.getcwd(), 'jail')) open('jail/beacon.txt', 'w').write('I should be found') #-- set up loggers and files for the daemon testLogger = getRotFileLogger('test', 'test.log') stdoutLogger = getRotFileLogger('stdout', 'stdout.log') stderrLogger = getRotFileLogger('stderr', 'stderr.log') testFile = open('test.file', 'w') #-- test that all work before opening the DaemonContext testLogger.info('testLogger: before opening context') stdoutLogger.info('stdoutLogger: before opening context') stderrLogger.info('stderrLogger: before opening context') testFile.write('testFile: hello to a file before context\n') #-- get and configure the DaemonContext context = LoggingDaemonContext() context.files_preserve=[testFile] context.loggers_preserve=[testLogger] context.stdout_logger = stdoutLogger context.stderr_logger = stderrLogger context.chroot_directory = os.path.join(os.getcwd(), 'jail') #-- test whether it all works with context: # should appear in stdout.log print "stdout: hello!" # should appear in test.log testLogger.info('testLogger: hello, just testing') # should appear in test.file testFile.write('testFile: hello to a file\n') #testing chroot print "If chroot works, I should see beacon.txt: %s" % os.listdir('.') open('jailTestFile.txt', 'w').write('this was written in the jail\n') # do these things need access to devices???: print "time is: %s" % time.time() print "this is a random number: %s" % random.randint(1, 100) # the above seems to work without errors # but the following needs access to devices that are not available in the jail print urllib2.urlopen('http://www.python.org/').read(20) # should appear in stderr.file raise (Exception("stderrLogger: bummer!"))