Python Logging 模块

一文速通 logging 输出配置 + 颜色输出。

最基础示例

import logging

# 五种日志级别
logging.debug("This is a debug message")
logging.info("This is an info message")
logging.warning("This is a warning message")
logging.error("This is an error message")
logging.critical("This is a critical message")

输出格式

单纯打印一条语句,对于日志来说信息量太少了。如果希望在打印的同时附上时间、代码位置(文件名、具体语句行、类名等),并且加上各种符号以便分隔,使用 basicConfig中的 format 参数:

logging.basicConfig(
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
)

输出效果如下:

2025-06-22 01:13:14,505 - root - ERROR - This is an error message

对于时间,还可以通过 datefmt 进行特定格式化:

logging.basicConfig(
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    datefmt='%Y-%m-%d %H:%M:%S',
)

输出效果如下:

2025-06-22 01:16:18 - root - CRITICAL - This is a critical message

代码里的这些 asctime, name 都是哪来的?分别代表什么?可以在 模块 logging 文档中的 LogRecord 部分查看。


以下是一个比较通用的 format:

logging.basicConfig(
    format='%(asctime)s | %(levelname)-8s | %(module)s:%(lineno)d | %(message)s',
    datefmt='%Y-%m-%d %H:%M:%S'
)

效果如下,包含了时间、日志级别、模块名及具体行号等信息:

2025-06-22 01:27:28 | WARNING  | logging_test:10 | This is a warning message

输出到日志文件

令日志信息只输出到指定文件。默认情况下,该文件是追加的,每次重新运行程序都会在文件中追加内容而不会覆盖上次的日志内容。

logging.basicConfig(
    filename="mylog.log",
    encoding="utf-8"
)

logging.info("This is an info message")

如果想在每次运行程序时覆盖上次的日志内容,添加 filemode="w" 参数:

logging.basicConfig(
    filename="mylog.log",
    encoding="utf-8",
    filemode="w",
)

控制台打印日志同时输出到文件

logging 通过 handler 来管理不同的输出。因此输出到文件和输出到控制台各需要一个 handler 管理。

默认情况下,在basicConfig 中配置的参数是针对一个 Handler 的配置。因此,如果需要多个 handler,可以显式传入 handler 列表:

logging.basicConfig(
    level=logging.INFO, 
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    # 以上参数是两个 handler 共用的
    handlers=[
        logging.FileHandler("app.log"),      # 输出到文件
        logging.StreamHandler(sys.stdout)    # 输出到控制台
    ]
)

logging.info("这条日志会同时写入文件和控制台")

如果不同的 handler 有不同的配置,如文件保留 DEBUG 及以上的所有日志信息,而控制台只输出 INFO 及以上的所有日志信息,并各自采用各自的格式,则可以单独配置:

# 配置根 Logger
logger = logging.getLogger()
logger.setLevel(logging.DEBUG)

# 文件 Handler(所有级别)
file_handler = logging.FileHandler("app.log")
file_formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
file_handler.setFormatter(file_formatter)

# 控制台 Handler(仅 INFO 及以上)
console_handler = logging.StreamHandler(sys.stdout)
console_handler.setLevel(logging.INFO)
console_handler.setFormatter(logging.Formatter('%(levelname)s - %(message)s'))

然后将它们加入到根 Logger 即可:

logger.addHandler(file_handler)
logger.addHandler(console_handler)

控制台日志颜色

方便起见,我们使用 colorama 库:pip install colorama。它可以支持不同操作系统、不同 shell 的彩色输出。

使用很简单,只需要简单地导入颜色变量,然后在字符串前 + 上它即可:

from colorama import Fore, init

init(autoreset=True) # 必须,否则后续其他输出也会沿用颜色

print(Fore.RED + "这是红色")
print(Fore.GREEN + "这是绿色")
print(Fore.BLUE + "这是蓝色")
print(Fore.YELLOW + "这是黄色")
print(Fore.CYAN + "这是青色")

效果如下:

image-20250622014226519

要与 logging 一起使用,需要做个简单封装再使用:

# 自己定义一个 Formatter 子类
class ColoredFormatter(logging.Formatter):
    # 不同日志等级对应不同颜色
    COLORS = {
        'WARNING': Fore.YELLOW,
        'ERROR': Fore.RED,
        'CRITICAL': Fore.RED + Fore.BLACK
    }
    def format(self, record):
        msg = super().format(record)
        return self.COLORS.get(record.levelname, Fore.WHITE) + msg

console_handler = logging.StreamHandler(sys.stdout)
console_handler.setLevel(logging.INFO)
# 使用自己定义的 ColoredFormatter 对象
console_handler.setFormatter(ColoredFormatter('%(levelname)s - %(message)s'))

logging.error("Error 信息")