程序员文章、书籍推荐和程序员创业信息与资源分享平台

网站首页 > 技术文章 正文

Python的logging功能:程序身边的日志管家

hfteth 2025-05-16 13:29:45 技术文章 12 ℃

对话实录

小白:(满脸焦虑,疯狂翻着代码)专家救命!我的Python程序运行起来状况百出,用print调试时日志在终端窗口都刷没了,根本找不到问题出在哪,快教教我怎么办!
专家:(淡定一笑,敲了敲电脑)别慌!logging模块就是你的救星!它能像飞机黑匣子一样,精准记录程序运行的每一步,比print强大百倍,还能灵活控制输出。今天就带你玩转这个神器!

一、logging 基础:从 “记流水账” 开始

1.logging 的初体验

logging是 Python 内置的日志记录模块,默认只记录警告及以上级别的信息。简单一行代码,就能让程序 “开口说话”:

import logging
logging.warning('程序出现潜在风险!')

运行后,控制台会输出:

WARNING:root:程序出现潜在风险!

核心概念

  • 日志级别:从低到高分为DEBUG(调试信息)、INFO(运行信息)、WARNING(警告)、ERROR(错误)、CRITICAL(严重错误),级别越高越重要。
  • 默认配置:不做额外设置时,日志输出到控制台,格式为级别:logger名称:日志内容。

2.调整日志 “音量”

通过basicConfig函数,能自由设置日志级别。比如想看到程序所有 “悄悄话”(DEBUG级信息),可以这样做:

import logging

# 设置日志级别为DEBUG,显示所有DEBUG及以上级别的信息
logging.basicConfig(level=logging.DEBUG)

logging.debug('这是一段调试信息,帮助定位问题')
logging.info('程序正在按计划执行')
logging.warning('注意!可能存在性能瓶颈')
logging.error('程序出现错误,部分功能失效')
logging.critical('系统崩溃,紧急修复!')

输出:

DEBUG:root:这是一段调试信息,帮助定位问题
INFO:root:程序正在按计划执行
WARNING:root:注意!可能存在性能瓶颈
ERROR:root:程序出现错误,部分功能失效
CRITICAL:root:系统崩溃,紧急修复!

二、进阶操作:定制专属日志系统

1.让日志 “住进” 文件,打造个性化日志 “模板”

不想让日志在控制台一闪而过?可以日志加上执行时间,函数名、行号等 “标签”,并把它们保存到文件,方便复盘分析

import logging

logging.basicConfig(
    level=logging.DEBUG,
    # 指定日志文件路径,文件会自动创建
    filename='app.log',
    # 'a'表示追加模式,新日志不会覆盖旧日志
    filemode='a',
    # 自定义日志格式,包含时间、级别、内容
    format='%(asctime)s - %(name)s - %(levelname)s - %(funcName)s - %(lineno)d - %(message)s'

)
def divide(a, b):
    try:
        result = a / b
        logging.info('除法运算成功')
        return result
    except ZeroDivisionError:
        logging.error('除数不能为0', exc_info=True)
				#增加exc_info=True:在日志中记录完整的异常堆栈信息,方便排查问题。

divide(10, 2)
divide(10, 0)

在app.log中输出

2025-04-20 09:44:54,987 - root - INFO - divide - 125 - 除法运算成功
2025-04-20 09:44:54,987 - root - ERROR - divide - 128 - 除数不能为0
Traceback (most recent call last):
  File "/Desktop/test.py", line 124, in divide
    result = a / b
ZeroDivisionError: division by zero

格式说明

  • %(asctime)s:日志记录时间(如2025-04-20 09:42:20,284)
  • %(name)s:日志记录用户(如root)
  • %(levelname)s:日志级别(DEBUG/INFO等)
  • %(funcName)s:函数名
  • %(lineno)s:代码行号
  • %(message)s:具体日志内容

2.多模块日志 “分区管理”

大型项目中,不同模块的日志容易 “打架”?通过使用FileHandler将不同模块的Logger分开管理:

import logging


def module_logging(module_name):
	# 创建名为'module_name'的Logger
    logger = logging.getLogger(module_name)
	#设置级别为debug
    logger.setLevel(logging.DEBUG)

    # 创建文件处理器,专门记录module_name的日志到一个log文件
    file_handler = logging.FileHandler(f'{module_name}.log')
    file_handler.setLevel(logging.DEBUG)
	#创建日志格式
    formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
    file_handler.setFormatter(formatter)
    # 将文件处理器添加到Logger
    logger.addHandler(file_handler)

    return logger

#定义两个module
module1_logger = module_logging('module1')
module2_logger = module_logging('module2')

#分别打印不同module的日志到对应日志文件
module1_logger.info('module1 的日志信息')
module2_logger.error('module2 的错误日志信息')

同时使用StreamHandler将error日志输出到控制器,这样我们就可以在程序执行窗口看到错误日志了。上面的代码改造为增加1个控制台处理器:

import logging


def module_logging(module_name):
    logger = logging.getLogger(module_name)
    logger.setLevel(logging.DEBUG)
    
    file_handler = logging.FileHandler(f'{module_name}.log')
    file_handler.setLevel(logging.DEBUG)
    formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
    file_handler.setFormatter(formatter)
    logger.addHandler(file_handler)
    
    # 创建控制台处理器,只显示ERROR及以上级别的信息
    console_handler = logging.StreamHandler()
    console_handler.setLevel(logging.ERROR)
    logger.addHandler(console_handler)
    
    return logger


module1_logger = module_logging('module1')
module2_logger = module_logging('module2')

module1_logger.info('module1 的日志信息')
module2_logger.error('module2 的错误日志信息')

3.按日期滚动日志文件

使用TimedRotatingFileHandler可以实现按日期滚动日志文件,每天生成一个新的日志文件。

import logging
from logging.handlers import TimedRotatingFileHandler

logger = logging.getLogger('rotating_logger')
logger.setLevel(logging.DEBUG)

# 创建按天滚动的文件处理器
file_handler = TimedRotatingFileHandler('app.log', when='D', interval = 1, backupCount = 7)
file_handler.setLevel(logging.DEBUG)

formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
file_handler.setFormatter(formatter)

logger.addHandler(file_handler)

for i in range(10):
    logger.debug(f'循环中日志 {i}')

TimedRotatingFileHandler('app.log', when='D', interval = 1, backupCount = 7) 创建了一个按天滚动的文件处理器。when='D' 表示按天滚动,interval = 1 表示每天滚动一次,backupCount = 7 表示最多保留 7 个旧的日志文件,超过 7 个旧文件会被删除。

4.按文件大小滚动日志文件

使用RotatingFileHandler可以实现按文件大小滚动日志文件。

import logging
from logging.handlers import RotatingFileHandler

logger = logging.getLogger('size_rotating_logger')
logger.setLevel(logging.DEBUG)

# 创建按文件大小滚动的文件处理器,最大1024字节,最多保留3个备份文件
file_handler = RotatingFileHandler('app.log', maxBytes = 1024, backupCount = 3)
file_handler.setLevel(logging.DEBUG)

formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
file_handler.setFormatter(formatter)

logger.addHandler(file_handler)

for i in range(1000):
    logger.debug(f'循环中日志 {i}')

RotatingFileHandler('app.log', maxBytes = 1024, backupCount = 3) 创建了一个按文件大小滚动的文件处理器,当 app.log文件大小达到1024字节时,会创建新的备份文件,最多保留3个备份文件。

三、实战案例:logging的“十八般武艺”

案例 1:Web应用的 “健康监控”

在 Flask 应用中,用logging记录用户请求和错误:

from flask import Flask
import logging

app = Flask(__name__)

logging.basicConfig(
    level=logging.INFO,
    filename='flask_app.log',
    format='%(asctime)s - %(levelname)s - %(message)s'
)

@app.route('/')
def index():
    logging.info('用户访问首页')
    return 'Hello, World!'

@app.errorhandler(404)
def page_not_found(error):
    logging.error(f'404错误:{error}')
    return '页面未找到', 404

if __name__ == '__main__':
    app.run(debug=True)

通过分析flask_app.log,能快速定位高频访问页面和错误接口!

案例 2:自动化脚本的 “任务日记”

在脚本中记录任务执行的每一步:

import logging
import time

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

def backup_data():
    logging.info('开始执行数据备份任务')
    time.sleep(2)
    try:
        # 模拟备份操作
        with open('backup.txt', 'w') as f:
            f.write('备份成功')
        logging.info('数据备份完成')
    except Exception as e:
        logging.error(f'备份失败:{e}')

backup_data()

即使脚本半夜运行,第二天也能通过日志了解任务执行过程!

案例 3:数据库操作的 “安全卫士”

在操作数据库时,记录关键操作和异常:

import logging
import sqlite3

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

def query_database():
    try:
        conn = sqlite3.connect('example.db')
        cursor = conn.cursor()
        cursor.execute('SELECT * FROM users')
        result = cursor.fetchall()
        logging.info(f'查询成功,结果:{result}')
        conn.close()
    except sqlite3.Error as e:
        logging.error(f'数据库查询失败:{e}')

query_database()

一旦数据库出问题,日志立刻 “报警”!

闭坑指南:避开 logging 的 “雷区”

日志级别设置混乱

  • 错误操作:设置过高的日志级别(如生产环境用CRITICAL),导致关键信息被 “屏蔽”。
  • 正确姿势:开发阶段用DEBUG全面排查,生产环境切换为INFO或WARNING,聚焦核心问题。

日志文件 “无限膨胀”

  • 错误操作:未设置日志文件大小限制,导致文件越来越大,撑爆硬盘。
  • 正确姿势:使用RotatingFileHandler按大小或时间切割日志文件:

多模块日志 “串台”

  • 错误操作:不同模块共用同一个Logger,日志混杂难以区分。
  • 正确姿势:每个模块创建独立Logger,通过logger.name标识来源:

日志格式 “乱码”

  • 错误操作:写入文件时未指定编码,导致中文等字符乱码。
  • 正确姿势:在basicConfig中添加encoding='utf-8':
logging.basicConfig(
    level=logging.INFO,
    filename='app.log',
    format='%(asctime)s - %(levelname)s - %(message)s',
    encoding='utf-8'
)

五、专家总结:logging让代码 “有迹可循”

掌握logging,就像给程序安上了 “记录仪” 和 “指南针”!无论是调试Bug、监控运行状态,还是复盘历史操作,它都能轻松搞定。从现在开始,抛弃混乱的print,用logging让代码更专业、更健壮!

Tags:

最近发表
标签列表