3

The Logging Cookbook describes a method for using Python logging functionality with remote log server.

Unfortunately, this implementation requires exposing of the server, which process the received data with unPickle function, which is a severe security risk.

I'd like to use the msgpack library to pass the logging message instead of the pickle module. Is there any ready to use solution based on logging and msgpack available? Are there any special precautions to consider, when implementing it myself?

Update I have managed to modify the original example from the Logging Cookbook, and obtained something which seems to work.

The log server:

import logging
import logging.handlers
import SocketServer
import struct
import msgpack

class LogRecordStreamHandler(SocketServer.StreamRequestHandler):
    """Handler for a streaming logging request.

    This basically logs the record using whatever logging policy is
    configured locally.
    """

    def handle(self):
        """
        Handle multiple requests - each expected to be a 4-byte length,
        followed by the LogRecord in pickle format. Logs the record
        according to whatever policy is configured locally.
        """
        import msgpack as p
        unp=p.Unpacker()
        while True:
            r=self.request.recv(1000)
            if len(r)==0:
                break
            unp.feed(r)
            for obj in unp:
                record = logging.makeLogRecord(obj)
                self.handleLogRecord(record)


    def handleLogRecord(self, record):
        # if a name is specified, we use the named logger rather than the one
        # implied by the record.
        if self.server.logname is not None:
            name = self.server.logname
        else:
            name = record.name
        logger = logging.getLogger(name)
        # N.B. EVERY record gets logged. This is because Logger.handle
        # is normally called AFTER logger-level filtering. If you want
        # to do filtering, do it at the client end to save wasting
        # cycles and network bandwidth!
        logger.handle(record)

class LogRecordSocketReceiver(SocketServer.ThreadingTCPServer):
    """
    Simple TCP socket-based logging receiver suitable for testing.
    """

    allow_reuse_address = 1

    def __init__(self, host='localhost',
                 port=65432,
                 handler=LogRecordStreamHandler):
        SocketServer.ThreadingTCPServer.__init__(self, (host, port), handler)
        self.abort = 0
        self.timeout = 1
        self.logname = None

    def serve_until_stopped(self):
        import select
        abort = 0
        while not abort:
            rd, wr, ex = select.select([self.socket.fileno()],
                                       [], [],
                                       self.timeout)
            if rd:
                self.handle_request()
            abort = self.abort

def main():
    logging.basicConfig(
        format='%(relativeCreated)5d %(name)-15s %(levelname)-8s %(message)s')
    tcpserver = LogRecordSocketReceiver()
    print('About to start TCP server...')
    tcpserver.serve_until_stopped()

if __name__ == '__main__':
    main()

Sample client:

import logging, logging.handlers
import msgpack
import test1
#import auxiliary_module

class MsgpackHandler(logging.handlers.SocketHandler):
  def __init__(self, host, port):
     logging.handlers.SocketHandler.__init__(self,host,port)
  def makePickle(self,record):
     #Use msgpack instead of pickle, for increased safety
     return msgpack.packb(record.__dict__)
# create logger with 'spam_application'
logger = logging.getLogger('my_log')
logger.setLevel(logging.DEBUG)
# create file handler which logs even debug messages
#fh = logging.FileHandler('spam.log')
fh=MsgpackHandler('localhost',65432)
fh.setLevel(logging.DEBUG)
# create formatter and add it to the handler
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
fh.setFormatter(formatter)
# add the handlers to the logger
logger.addHandler(fh)

logger.info('this is sample info message')
logger.error('this is sample error message')
4

0 回答 0