Tornado 日志系统完整指南:tornado.log 模块的三条日志流、格式化器与命令行配置实战
2026/9/20 13:40:54 网站建设 项目流程

Tornado 日志系统完整指南:tornado.log 模块的三条日志流、格式化器与命令行配置实战

【免费下载链接】tornadoTornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed.项目地址: https://gitcode.com/gh_mirrors/to/tornado

导读

本文以 Tornado 官方文档 docs/log.rst 为主题,系统讲解tornado.log模块的三大日志流(tornado.accesstornado.applicationtornado.general)、内置的LogFormatter格式化器,以及通过tornado.options命令行/配置文件自动启用彩色日志、文件输出与日志轮转的完整机制。读完本文,你将掌握 Tornado 应用从「开箱即用的日志」到「按需独立配置日志流」的完整实战能力,并能从源码层面理解访问日志、应用异常日志与框架内部日志的流向。


一、概述:Tornado 为什么自带一套日志封装

Tornado 是 Python 的 Web 框架与异步网络库(最初由 FriendFeed 开发),其源码模块 tornado/log.py 位于仓库根目录的tornado/包内。该模块并不另起炉灶实现一套日志框架,而是基于 Python 标准库logging之上的一层轻量封装:它预定义了三个具名 logger,并提供了一个能输出彩色、带时间戳、抗编码问题的格式化器LogFormatter

模块 docstring(tornado/log.py)明确指出 Tornado 使用三条日志流:

Logger 名称用途
tornado.accessHTTP 服务器的按请求(per-request)日志,未来也可能被其他服务器复用
tornado.application应用代码产生的错误日志,例如回调中未捕获的异常
tornado.general通用日志,包括 Tornado 自身的错误与警告

三条流可以在标准库logging模块之上独立配置——例如你希望把tornado.access日志单独输出到一份文件用于分析,其余日志仍走终端。这正是 Tornado 日志设计的第一原则:与 Python 生态完全兼容,不引入任何自定义日志语法。

在源码中,这三个 logger 以模块级单例形式创建(tornado/log.py):

access_log = logging.getLogger("tornado.access") app_log = logging.getLogger("tornado.application") gen_log = logging.getLogger("tornado.general")

Tornado 各核心模块正是通过导入这三个 logger 来打日志的,例如:

  • tornado/autoreload.py 使用gen_log输出文件变更重启、进程退出等框架级信息;
  • tornado/ioloop.py、tornado/iostream.py 使用app_log/gen_log记录回调异常、读写错误;
  • tornado/process.py 使用gen_log记录多进程 fork 与子进程状态。

二、三条日志流在 Web 应用中的真实流向

2.1tornado.access:HTTP 访问日志

当 HTTP 请求完成时,Application.log_request会被调用(tornado/web.py)。其默认实现(tornado/web.py)按响应状态码选择不同级别的tornado.access日志:

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, )

即:2xx/3xx 记INFO,4xx 记WARNING,5xx 记ERROR,每条日志包含状态码、请求摘要与毫秒级耗时。应用可以通过两种方式定制:在Application的 settings 中传入log_function回调(传参为RequestHandler对象),或继承Application重写log_request

2.2tornado.application:应用未捕获异常

当 handler 处理过程中抛出未捕获异常时,RequestHandler.log_exception(tornado/web.py)负责记录:

  • 若异常是HTTPError(如raise HTTPError(404)),则仅以gen_log.warning记录状态码与摘要,不带堆栈
  • 其他所有异常,以app_log.error记录完整堆栈(exc_info传入)。
if isinstance(value, HTTPError): ... gen_log.warning(format, *args) else: app_log.error( "Uncaught exception %s\n%r", self._request_summary(), self.request, exc_info=(typ, value, tb), )

write_error自身抛出异常,同样会走app_log.error("Uncaught exception in write_error", ...)(tornado/web.py)。此外,tornado/gen.py、tornado/concurrent.py 等异步模块在协程/Future 回调异常时也统一使用app_log.error

2.3tornado.general:框架内部事件

gen_log覆盖 Tornado 自身的事件,例如:

  • tornado/http1connection.py:gen_log.info("Malformed HTTP message from %s: %s", ...)记录畸形 HTTP 报文;
  • tornado/http1connection.py:记录请求体读取超时;
  • tornado/iostream.py:gen_log.error("Reached maximum read buffer size")记录读缓冲上限。

三、LogFormatter:彩色、带时间戳、抗编码的格式化器

3.1 三大特性与默认格式

LogFormatter(tornado/log.py)是 Tornado 内置的日志格式化器,核心特性:

  1. 颜色支持:输出到支持颜色的终端时按日志级别着色;
  2. 每条日志带时间戳
  3. 对 str/bytes 编码问题健壮:字节串/非 UTF-8 内容不会导致日志管线崩溃。

默认格式与默认颜色映射(tornado/log.py):

DEFAULT_FORMAT = "%(color)s[%(levelname)1.1s %(asctime)s %(module)s:%(lineno)d]%(end_color)s %(message)s" DEFAULT_DATE_FORMAT = "%y%m%d %H:%M:%S" DEFAULT_COLORS = { logging.DEBUG: 4, # Blue logging.INFO: 2, # Green logging.WARNING: 3, # Yellow logging.ERROR: 1, # Red logging.CRITICAL: 5, # Magenta }

实际输出形如:

[I 240101 12:00:00 module:42] Hello, world [E 240101 12:00:01 web:1952] Uncaught exception ...

%(color)s%(end_color)s之间的文本会按级别着色(format方法在 tornado/log.py 中注入record.color/record.end_color)。

3.2 构造参数

构造函数签名(tornado/log.py):

LogFormatter(fmt=DEFAULT_FORMAT, datefmt=DEFAULT_DATE_FORMAT, style="%", color=True, colors=DEFAULT_COLORS)
参数含义
color是否启用颜色支持,默认True(实际是否着色还取决于终端检测结果)
fmt日志格式模板,应用于 log record 的属性字典,%(color)s~%(end_color)s之间部分按级别着色
colors日志级别到终端颜色码的映射
datefmt时间格式,用于格式化(asctime)占位符

注意:color只是"是否允许"开关,最终是否真正着色取决于_stderr_supports_color()(tornado/log.py)——它要求stderr是 TTY,且要么curses能查到终端颜色数> 0,要么在 Windows 上使用colorama包装过的stderr

3.3 颜色支持与 Windows(colorama)

Tornado 4.5 起LogFormatter增加对colorama的支持(tornado/log.py),用于不支持 ANSI 颜色码的 Windows 版本。应用如需在 Windows 获得颜色,必须先调用colorama.init()初始化。当curses不可用(典型场景即 Windows + colorama),代码会回退到硬编码 ANSI 颜色码(tornado/log.py):

self._colors[levelno] = "\033[2;3%dm" % code self._normal = "\033[0m"

自 Tornado 4.5 起,构造函数签名同时兼容logging.config.dictConfig,因此可直接在字典配置中引用。

3.4 抗编码问题的健壮性设计

format方法(tornado/log.py)做了两件关键的事:

  1. 消息统一经_safe_unicode转成 Unicode——正常情况用_unicode,遇到UnicodeDecodeError则退化为repr(s)(tornado/log.py),保证任意字节串都能成功落盘;
  2. 异常堆栈逐行做_safe_unicode后再拼接,避免某一行非 UTF-8 字节破坏整个 traceback 的换行结构;最终输出中所有换行统一缩进为"\n ",保证多行日志在文件里层次清晰。

四、enable_pretty_logging与命令行日志配置

4.1 自动启用机制

LogFormatter会在调用tornado.options.parse_command_linetornado.options.parse_config_file自动启用(除非使用--logging=none)。其背后是两条主线:

  • define_logging_options在 options 上注册全部日志相关参数(tornado/log.py);
  • options.add_parse_callback(lambda: enable_pretty_logging(options))enable_pretty_logging注册为解析回调,命令行/配置文件解析完成后自动执行。

enable_pretty_logging(tornado/log.py)的完整逻辑:

if options.logging is None or options.logging.lower() == "none": return # 显式关闭,Tornado 不碰 logging 配置 logger.setLevel(getattr(logging, options.logging.upper())) # 设置根 logger 级别 if options.log_file_prefix: # 配置了文件输出 → 按轮转模式建 Handler if rotate_mode == "size": channel = logging.handlers.RotatingFileHandler( filename=options.log_file_prefix, maxBytes=options.log_file_max_size, backupCount=options.log_file_num_backups, encoding="utf-8", ) elif rotate_mode == "time": channel = logging.handlers.TimedRotatingFileHandler( filename=options.log_file_prefix, when=options.log_rotate_when, interval=options.log_rotate_interval, backupCount=options.log_file_num_backups, encoding="utf-8", ) else: raise ValueError('The value of log_rotate_mode option should be "size" or "time"') channel.setFormatter(LogFormatter(color=False)) logger.addHandler(channel) if options.log_to_stderr or (options.log_to_stderr is None and not logger.handlers): channel = logging.StreamHandler() channel.setFormatter(LogFormatter()) # stderr 可着色 logger.addHandler(channel)

注意两个细节:

  • 文件输出必须禁用颜色LogFormatter(color=False)),避免日志文件里混入终端转义序列;
  • 若未设置log_file_prefix,且根 logger 没有任何 handler,则默认输出到stderrlog_to_stderr显式为False可强制关掉 stderr 输出。

4.2 全部日志相关命令行选项

define_logging_options(tornado/log.py)注册的参数如下,均可通过命令行--xxx=value或配置文件键值对设置:

选项类型默认值说明
--loggingstrinfoPython 日志级别,取值debug\|info\|warning\|error\|none;为none时 Tornado 不修改 logging 配置
--log_to_stderrboolNone是否输出到 stderr(可着色)。默认:未设置log_file_prefix且无其他日志配置时输出到 stderr
--log_file_prefixstrNone日志文件路径前缀。多进程运行时每个进程必须使用不同前缀(例如包含端口号),否则日志互相覆盖
--log_file_max_sizeint100 * 1000 * 1000(约 100MB)size 模式下日志文件轮转前的大小上限
--log_file_num_backupsint10保留的日志文件份数
--log_rotate_whenstrmidnightTimedRotatingFileHandler 的时间间隔类型,可选S(秒)、M(分)、H(时)、D(天)、W0-W6(星期几)
--log_rotate_intervalint1时间轮转的间隔数值
--log_rotate_modestrsize轮转模式:size(按大小)或time(按时间)

4.3 实战示例

以仓库自带示例 demos/helloworld/helloworld.py 为蓝本,最简单的方式是直接调用tornado.options.parse_command_line(),日志配置即自动生效:

python demos/helloworld/helloworld.py --logging=info

按大小轮转并写入文件:

python demos/helloworld/helloworld.py \ --log_file_prefix=/var/log/myapp/app.log \ --log_rotate_mode=size \ --log_file_max_size=104857600 \ --log_file_num_backups=10

按天轮转(每天午夜切割):

python demos/helloworld/helloworld.py \ --log_file_prefix=/var/log/myapp/app.log \ --log_rotate_mode=time \ --log_rotate_when=midnight \ --log_rotate_interval=1 \ --log_file_num_backups=30

关闭 Tornado 的日志接管、完全交给应用自己管理:

python demos/helloworld/helloworld.py --logging=none

若在代码中希望禁用,可在parse_command_line()之前设置options.logging = None(参考 tornado/options.py 的说明与 tornado/test/log_test.py 的测试用例)。

多进程注意:--log_file_prefix在多进程场景(例如 tornado/process.py 的fork_processes)下必须每个进程不同,官方建议把端口号拼进前缀。

五、程序化独立配置:摆脱命令行,自由掌控

如果不想依赖命令行解析,也可在代码中直接用标准库logging配置 Tornado 的任意日志流。例如将访问日志单独送入文件、应用日志保留在 stderr:

import logging # 独立配置 tornado.access access = logging.getLogger("tornado.access") access.setLevel(logging.INFO) fh = logging.FileHandler("access.log", encoding="utf-8") fh.setFormatter(logging.Formatter("%(asctime)s %(message)s")) access.addHandler(fh) # 其余日志(tornado.application / tornado.general / 根 logger)保持默认 logging.basicConfig(level=logging.INFO)

define_logging_options也可用于自定义的OptionParser实例(tornado/log.py 注明:默认 options 实例已自动包含这些选项,仅当自建OptionParser时才需要显式调用):

from tornado.options import OptionParser from tornado.log import define_logging_options, enable_pretty_logging parser = OptionParser() define_logging_options(parser) enable_pretty_logging(parser) # 或调用 parser.parse_command_line() 自动触发

此时LogFormatter亦可直接复用在任何 handler 上:

from tornado.log import LogFormatter handler = logging.StreamHandler() handler.setFormatter(LogFormatter(color=True)) logging.getLogger().addHandler(handler)

六、从源码与测试看行为保证

仓库 tornado/test/log_test.py 是验证上述行为的最佳参考:

  • LogFormatterTest.test_basic_logging等用例验证单条日志输出格式与正则[E 240101 12:00:00 log_test:N]完全一致(tornado/test/log_test.py);
  • test_bytes_logging/test_utf8_logging/test_bytes_exception_logging验证了字节串消息、非 UTF-8 消息、字节型异常堆栈均不会导致日志崩溃,且异常堆栈中的换行不会被转义(tornado/test/log_test.py);
  • EnablePrettyLoggingTest.test_log_file验证设置log_file_prefix后文件内容格式为[E ...] hellotest_log_file_with_timed_rotating验证time轮转模式,test_wrong_rotate_mode_value验证非法log_rotate_mode会抛ValueError(tornado/test/log_test.py);
  • LoggingOptionTest通过子进程验证:默认不解析时不启用日志、调用parse_command_line()info级日志可见、--logging=none(含大小写None)可关闭、命令行参数可覆盖代码中设置的options.logging(tornado/test/log_test.py)。

七、常见问题与最佳实践小结

  1. --logging=none的含义:不是"不记日志",而是"Tornado 不接管 logging 配置",此时根 logger 级别与 handler 完全由你掌控。
  2. 颜色丢失:日志重定向到文件/管道时_stderr_supports_color()返回False,属预期行为;如需在文件中保留颜色必须自行设置。
  3. 编码崩溃:消息可能包含任意 bytes 时,优先使用LogFormatter,其_safe_unicode/repr回退机制保证日志管线永不因编码异常中断。
  4. 多进程日志:多进程场景务必让每个进程的log_file_prefix唯一(如包含端口号),否则多个进程写同一文件会互相覆盖。
  5. 访问日志定制:需要自定义访问日志格式时,优先在Applicationsettings 传入log_function,或在子类中重写log_request(tornado/web.py)。
  6. 异常分级:HTTP 4xx 异常(HTTPError)走gen_log.warning且无堆栈,非 HTTP 异常走app_log.error带完整堆栈——监控告警可据此分流。

延伸阅读(仓库内相关文档)

  • tornado.options 命令行解析说明:日志选项是如何注册与解析的
  • tornado.web 的 log_function / log_request 说明:访问日志钩子的应用层入口
  • 异步与协程指南:理解tornado.application日志对应的异步回调异常场景

【免费下载链接】tornadoTornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed.项目地址: https://gitcode.com/gh_mirrors/to/tornado

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询