tornado源代码分析
打开site-packages/tornado/log.py,最开头的注释文档说明了tornado的日志模块是直接和logging模块集成的
"""Logging support for Tornado. Tornado uses three logger streams: * ``tornado.access``: Per-request logging for Tornado's HTTP servers (and potentially other servers in the future) * ``tornado.application``: Logging of errors from application code (i.e. uncaught exceptions from callbacks) * ``tornado.general``: General-purpose logging, including any errors or warnings from Tornado itself. These streams may be configured independently using the standard library's `logging` module. For example, you may wish to send ``tornado.access`` logs to a separate file for analysis. """
其中一共用到了三种被命名的logger
access_log = logging.getLogger("tornado.access") app_log = logging.getLogger("tornado.application") gen_log = logging.getLogger("tornado.general")
access_log 用于记录每一个访问请求
app_log 用于记录程序运行过程中,所有未被处理的异常
gen_log 用于记录tornado自己运行过程中报的错误和警告
自定义访问日志格式
先看下tornado的源代码,access_log是在tornado.web.RequestHandler中调用的,打开site-packages/tornado/web.py, 日志是在请求完成时调用log_request完成的
def log_request(self, handler): """Writes a completed HTTP request to the logs. By default writes to the python root logger. To change this behavior either subclass Application and override this method, or pass a function in the application settings dictionary as ``log_function``. """ if "log_function" in self.settings: self.settings["log_function"](handler) return if handler.get_status() < 400: log_method = access_log.info elif handler.get_status() < 500: log_method = access_log.warning else: log_method = access_log.error request_time = 1000.0 * handler.request.request_time() log_method("%d %s %.2fms", handler.get_status(), handler._request_summary(), request_time)
其中的日志格式是由handler._request_summary()来决定的
def _request_summary(self): return "%s %s (%s)" % (self.request.method, self.request.uri, self.request.remote_ip)
因此我们只需要继承tornado.web.RequestHandler,然后重写_request_summary就可以配置自己的access_log格式了,下面是我自己写的一个BaseHandler
class BaseHandler(tornado.web.RequestHandler): def __init__(self, application, request, **kwargs): super(BaseHandler, self).__init__(application, request) self._operator_name = 'NO_LOGIN' def _request_summary(self): if self.request.method == 'post': return "%s %s (%s@%s)" % (self.request.method, self.request.uri, self._operator_name, self.request.remote_ip) return "%s %s %s(%s@%s)" % (self.request.method, self.request.uri, self.request.body.decode(), self._operator_name, self.request.remote_ip)
BaseHandler重写了_request_summary,并给日志增加了操作者信息和远端IP信息
使用配置档初始化
一个logging.yaml例子,这里只配置了tornado.access,其中的logs文件夹需要手动建立
version: 1 loggers: root: level: DEBUG handlers: [console] tornado: level: DEBUG handlers: [console,log] propagate: no tornado.access: level: DEBUG handlers: [console, access] propagate: no log: level: DEBUG handlers: [console,log] propagate: no formatters: simple: format: '%(asctime)s - %(name)s - %(levelname)s - %(message)s' timedRotating: format: '%(asctime)s %(name)-12s %(levelname)-8s - %(message)s' handlers: console: class: logging.StreamHandler level: DEBUG formatter: simple access: class: logging.handlers.TimedRotatingFileHandler level: DEBUG formatter: simple filename: 'logs/access.log' when: 'midnight' interval: 1 backupCount: 180 log: class: logging.handlers.TimedRotatingFileHandler level: DEBUG formatter: timedRotating filename: 'logs/log.log' when: 'midnight' interval: 1 backupCount: 180 encoding: 'utf8'
tornado初始化之前记得初始化log配置
log_config = yaml.load(open('logging.yaml', 'r')) logging.config.dictConfig(log_config)