日志输出到控制台:StreamHandler 的使用与颜色技巧
控制台输出,是咱们调试 Python 日志时最直接的反馈方式。StreamHandler 就是干这个的——它把日志消息送到标准输出或标准错误流。我早期写脚本时,经常直接 print 一堆信息,后来发现日志框架提供的灵活度完全不一样。
StreamHandler 基本用法
说白了,你只需要三行就能让日志从控制台蹦出来:
import logging
logger = logging.getLogger('console_demo')
handler = logging.StreamHandler() # 默认输出到 sys.stderr
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)
logger.setLevel(logging.DEBUG)
logger.info('嘿,控制台我能看见你!')
你看,StreamHandler 不指定流时自动走 sys.stderr。想输出到标准输出?给个 sys.stdout 参数就行:
import sys
handler = logging.StreamHandler(sys.stdout)
我用过的坑:有些同学同时加了 FileHandler 和 StreamHandler,结果控制台重复打印——一个 logger 挂了两个输出相同的 handler。我之前有个自动部署脚本,日志重复打印搞得我排查时眼花缭乱。后来养成习惯:每次添加 handler 前先检查 logger.hasHandlers(),或者直接清空再新建。
控制台颜色输出技巧
黑白日志看久了眼睛疼,尤其 ERROR 级别混在 INFO 里根本分不清。我记得有个生产故障排查,几万行日志里找一条红色报错,结果全是灰白色调……那次之后我就把颜色输出作为标配。
实现方式主要有两种:
- 用第三方库 colorama —— 跨平台,简单粗暴
- DIY 自定义 Formatter —— 零依赖,但对 ANSI 码不熟的容易踩坑
方式一:colorama + StreamHandler
我的经验是,colorama 在 Windows 上要调用 init(autoreset=True),否则颜色会“泄露”到后续输出。直接上代码:
import logging
import sys
from colorama import init, Fore, Style
init(autoreset=True) # Windows 下必须
class ColoredFormatter(logging.Formatter):
"""给不同级别上色"""
COLORS = {
'DEBUG': Fore.CYAN,
'INFO': Fore.GREEN,
'WARNING': Fore.YELLOW,
'ERROR': Fore.RED,
'CRITICAL': Fore.MAGENTA + Style.BRIGHT,
}
def format(self, record):
level_color = self.COLORS.get(record.levelname, '')
# 给整个消息加上颜色前缀
msg = super().format(record)
return f"{level_color}{msg}{Style.RESET_ALL}"
handler = logging.StreamHandler(sys.stdout)
formatter = ColoredFormatter('%(asctime)s | %(levelname)-8s | %(message)s')
handler.setFormatter(formatter)
logger = logging.getLogger('color_logger')
logger.addHandler(handler)
logger.setLevel(logging.DEBUG)
logger.debug('这个信息是青色的')
logger.info('正常的绿色信息')
logger.warning('黄色的警告要留意')
logger.error('红色的错误必须处理')
logger.critical('品红色加亮!别忽视啊')
小贴士:生产环境日志建议保留原始无颜色版本(比如文件输出),颜色只用于开发调试。我有个项目搭配了 --no-color 命令行参数来控制是否启用颜色。
方式二:纯 ANSI 转义序列(不依赖第三方)
如果你不想引入 colorama,可以用标准 ANSI 码:
class SimpleColorFormatter(logging.Formatter):
GREY = '\033[90m'
BLUE = '\033[94m'
YELLOW = '\033[93m'
RED = '\033[91m'
RESET = '\033[0m'
FORMATS = {
logging.DEBUG: BLUE,
logging.INFO: GREY,
logging.WARNING: YELLOW,
logging.ERROR: RED,
}
def format(self, record):
color = self.FORMATS.get(record.levelno, self.RESET)
record.msg = f"{color}{record.msg}{self.RESET}"
return super().format(record)
嗯,这里要注意:Windows 旧版控制台(CMD)不支持 ANSI,要么用 colorama 的 init() 去自动转换,要么直接上 Windows Terminal 或 PowerShell。我一般直接用 colorama,省心。
SteamHandler 在日志体系中的位置
下面这张图展示了 Logger、Handler、Formatter 之间的协作关系——核心就是把日志记录“格式化”后送到输出目的地:
实战配置建议
| 场景 | 推荐做法 | 为什么不推荐反例 |
|---|---|---|
| 开发测试 | StreamHandler + ColoredFormatter + DEBUG 级别 | 全用 print 替代——失去级别过滤和时间戳 |
| 生产环境控制台 | StreamHandler + 无颜色 Formatter + INFO 级别 | 保留颜色可能被日志采集系统乱码吃掉 |
| Windows + 颜色 | 必须调用 colorama.init() |
直接 ANSI 码不可靠 |
| 日志去重 | 检查 logger.handlers 避免重复 |
多次 addHandler 造成双倍输出 |
几个你可能会踩的坑
- 乱码? 我曾经因为
StreamHandler默认编码是sys.stderr.encoding,在中文 Windows 上输出乱码。加一行handler.terminator = '\n'或指定stream=open(sys.stdout.fileno(), 'w', encoding='utf-8')解决。 - 颜色字符串跑到文件里? 如果同一个 logger 同时绑定了 FileHandler 和 StreamHandler,且都用了颜色 Formatter,日志文件里会有一堆
\033[93m这种垃圾。我的经验是:颜色只给控制台专用 handler 用,文件 handler 用标准 Formatter。 - 为什么设置
logger.setLevel()后还有低级别日志输出? 注意:logger 的级别和 handler 的级别是“与”的关系。比如 logger 设了 INFO,handler 设了 DEBUG,最终还是 INFO 以上才通过。我记得有个同事调了半天,最后发现 handler 没设级别,流过了所有级别。
setup_logging(use_color=True) 函数,根据参数决定是否启用颜色。这样团队所有成员都在同一个配置下工作,避免“你机器上有颜色,我机器上没有”的尴尬。
总结
StreamHandler 是最常用的输出方式,搭配颜色能让调试效率翻倍。核心要点就三个:
- 理解 logger/handler/formatter 的分工
- 颜色要区分场景(开发 vs 生产)
- 谨慎处理 handler 重复和编码问题
你下次写日志脚本时,试试把颜色加上去——保证你会觉得比纯黑白舒服不少。