Logging Cookbook描述了一种在远程日志服务器上使用Python日志记录功能的方法。
不幸的是,此实现需要公开服务器,该服务器使用 unPickle 函数处理接收的数据,该函数是severe security risk。
我想使用msgpack库传递日志消息而不是pickle模块。是否有基于日志记录和 msgpack 的现成可用解决方案? 在自己实施时是否有任何特殊的预防措施需要考虑?
更新 我已经设法修改了Logging Cookbook中的原始示例,并获得了似乎有用的东西。
日志服务器:
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()
示例客户端:
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')