我需要创建一个系统,将嵌入式系统中生成的日志消息远程记录在服务器上并存储在轮换日志文件中。由于网络通信的限制,日志消息必须通过HTTP协议传输。服务器已经运行了Flask http://flask.pocoo.org/基于 HTTP 服务器,因此我想将日志系统与基于 Flask 的 Web 应用程序集成。
蟒蛇logging https://docs.python.org/2/library/logging.html模块提供专用HTTPhandler https://docs.python.org/2.7/library/logging.handlers.html#httphandler为此目的的类。基于logging
文档,我已经创建了第一个实现。
服务器部分:
from flask import Flask
from flask import Response
from flask import request
import logging
import logging.handlers
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'logging server'
@app.route('/log', methods=['POST'])
def handle_log():
if request.method == 'POST':
rd=request.form.to_dict()
record = logging.makeLogRecord(rd)
log1.handle(record)
return "OK"
log1=logging.getLogger('MTEST')
log1.setLevel(logging.DEBUG)
fh=logging.handlers.RotatingFileHandler('logs','a',maxBytes=10000000,backupCount=10)
formatter = logging.Formatter('%(asctime)s %(name)-15s %(levelname)-8s %(message)s')
rfh.setFormatter(formatter)
log1.addHandler(rfh)
log1.error("First error generated locally")
app.run()
客户端部分:
import logging, logging.handlers
myLogger = logging.getLogger('MTEST')
myLogger.setLevel(logging.DEBUG)
httpHandler = logging.handlers.HTTPHandler('localhost:5000',url='/log',method="POST")
myLogger.addHandler(httpHandler)
myLogger.info('Small info message')
myLogger.debug('Small debug message')
myLogger.erro('Small error message')
但是,通过该实现,服务器报告了以下错误(我复制了四个几乎相同的错误消息之一),并且仅记录了本地生成的错误消息。
Traceback (most recent call last):
File "/usr/lib/python2.7/logging/handlers.py", line 76, in emit
if self.shouldRollover(record):
File "/usr/lib/python2.7/logging/handlers.py", line 156, in shouldRollover
msg = "%s\n" % self.format(record)
File "/usr/lib/python2.7/logging/__init__.py", line 741, in format
return fmt.format(record)
File "/usr/lib/python2.7/logging/__init__.py", line 465, in format
record.message = record.getMessage()
File "/usr/lib/python2.7/logging/__init__.py", line 329, in getMessage
msg = msg % self.args
TypeError: not all arguments converted during string formatting
我添加了传输到服务器的表单数据的打印以及创建的 LogRecord 对象的属性。看来,args
属性作为空元组传输并且不被创建。
为了解决这个问题,我修改了handle_log
常规:
@app.route('/log', methods=['POST'])
def handle_log():
if request.method == 'POST':
rd=request.form.to_dict()
rd['args']=""
record = logging.makeLogRecord(rd)
log1.handle(record)
return "OK"
日志记录仍然无法正常工作,但服务器产生的错误已更改:
Logged from file test1.py, line 9
127.0.0.1 - - [10/Jul/2018 23:52:51] "POST /log HTTP/1.0" 200 -
Traceback (most recent call last):
File "/usr/lib/python2.7/logging/handlers.py", line 76, in emit
if self.shouldRollover(record):
File "/usr/lib/python2.7/logging/handlers.py", line 156, in shouldRollover
msg = "%s\n" % self.format(record)
File "/usr/lib/python2.7/logging/__init__.py", line 741, in format
return fmt.format(record)
File "/usr/lib/python2.7/logging/__init__.py", line 467, in format
record.asctime = self.formatTime(record, self.datefmt)
File "/usr/lib/python2.7/logging/__init__.py", line 423, in formatTime
ct = self.converter(record.created)
TypeError: a float is required
为了检查出了什么问题,我添加了打印创建的 LogRecord 对象的过滤器:
from flask import Flask
from flask import Response
from flask import request
import logging
import logging.handlers
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'logging server'
@app.route('/log', methods=['POST'])
def handle_log():
if request.method == 'POST':
rd=request.form.to_dict()
rd['args']=""
record = logging.makeLogRecord(rd)
log1.handle(record)
return "OK"
class myflt(logging.Filter):
def filter(self,rec):
print(rec.__dict__)
return 1
log1=logging.getLogger('MTEST')
log1.setLevel(logging.DEBUG)
rfh=logging.handlers.RotatingFileHandler('logs','a',maxBytes=10000000,backupCount=10)
formatter = logging.Formatter('%(asctime)s %(name)-15s %(levelname)-8s %(message)s')
rfh.setFormatter(formatter)
log1.addHandler(rfh)
log1.addFilter(myflt())
log1.error("First error generated locally")
app.run()
之后,我可以比较为本地生成的消息创建的 LogRecord 对象:
{'threadName': 'MainThread', 'name': 'MTEST', 'thread': 139678016341824, 'created': 1531259894.112536, 'process': 5595, 'processName': 'MainProcess', 'args': (), 'module': 'srv1', 'filename': 'srv1.py', 'levelno': 40, 'exc_text': None, 'pathname': 'srv1.py', 'lineno': 37, 'msg': 'First error generated locally', 'exc_info': None, 'funcName': '<module>', 'relativeCreated': 44.27504539489746, 'levelname': 'ERROR', 'msecs': 112.53595352172852}
对于远程生成的消息:
{'relativeCreated': u'12.3879909515', 'process': u'5597', 'module': u'test1', 'funcName': u'<module>', 'filename': u'test1.py', 'levelno': u'10', 'processName': u'MainProcess', 'lineno': u'9', 'msg': u'Small debug message', 'args': '', 'exc_text': u'None', 'name': u'MTEST', 'thread': u'140221336438592', 'created': u'1531259898.51', 'threadName': u'MainThread', 'msecs': u'511.312007904', 'pathname': u'test1.py', 'exc_info': u'None', 'levelname': u'DEBUG'}
看来,日志记录的传输通过HTTPHandler
将所有数字转换为字符串,因此makeLogRecord
函数无法正确重新创建LogRecord
服务器中的对象。
通过以下方式传输日志消息的正确方法是什么HTTPHandler
以便可以通过例如以下方式正确处理它们:RotatingFileHandler
在远程机器上?