Python案例如何实现日志打印?

wen python案例 9

本文目录导读:

Python案例如何实现日志打印?

  1. 最基础的配置(快速上手)
  2. 配置日志格式(时间、文件名、行号)
  3. 同时输出到控制台和文件
  4. 使用配置文件(生产环境推荐)
  5. 日志轮转(防止日志文件过大)
  6. 装饰器方式自动记录函数日志
  7. 日志级别说明
  8. 总结建议

在Python中实现日志打印,推荐使用内置的logging模块,它比简单的print更强大、更灵活。

以下是几种常见的实现案例,从基础到高级:

最基础的配置(快速上手)

适合小型脚本、快速调试。

import logging
# 基础配置:设置日志级别和格式
logging.basicConfig(level=logging.INFO, 
                    format='%(asctime)s - %(levelname)s - %(message)s')
# 使用不同级别的日志
logging.debug("这是调试信息(不会打印,因为级别是INFO)")
logging.info("程序开始运行")
logging.warning("这是一个警告")
logging.error("发生了一个错误")
logging.critical("严重错误,程序可能崩溃")

输出:

2025-03-30 10:30:00,123 - INFO - 程序开始运行
2025-03-30 10:30:00,124 - WARNING - 这是一个警告
2025-03-30 10:30:00,125 - ERROR - 发生了一个错误
2025-03-30 10:30:00,126 - CRITICAL - 严重错误,程序可能崩溃

配置日志格式(时间、文件名、行号)

适合需要定位代码位置的场景。

import logging
logging.basicConfig(
    level=logging.DEBUG,
    format='%(asctime)s [%(levelname)s] %(filename)s:%(lineno)d - %(message)s',
    datefmt='%Y-%m-%d %H:%M:%S'
)
def divide(a, b):
    logging.info(f"正在计算 {a} / {b}")
    try:
        result = a / b
        logging.debug(f"计算结果: {result}")
        return result
    except ZeroDivisionError:
        logging.error("除数不能为0", exc_info=True)
        return None
divide(10, 2)
divide(10, 0)

输出(示例):

2025-03-30 10:30:00 [INFO] demo.py:9 - 正在计算 10 / 2
2025-03-30 10:30:00 [DEBUG] demo.py:11 - 计算结果: 5.0
2025-03-30 10:30:00 [INFO] demo.py:9 - 正在计算 10 / 0
2025-03-30 10:30:00 [ERROR] demo.py:14 - 除数不能为0
Traceback (most recent call last):
  File "demo.py", line 10, in divide
    result = a / b
ZeroDivisionError: division by zero

同时输出到控制台和文件

适合需要持久化日志的生产环境。

import logging
# 创建 logger
logger = logging.getLogger('my_app')
logger.setLevel(logging.DEBUG)
# 1. 文件处理器(写入文件)
file_handler = logging.FileHandler('app.log', encoding='utf-8')
file_handler.setLevel(logging.DEBUG)
# 2. 控制台处理器(打印到屏幕)
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO)
# 配置格式
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
file_handler.setFormatter(formatter)
console_handler.setFormatter(formatter)
# 添加处理器到 logger
logger.addHandler(file_handler)
logger.addHandler(console_handler)
# 使用 logger
logger.debug("调试信息 -> 仅写入文件")
logger.info("普通信息 -> 文件 + 控制台")
logger.warning("警告信息")

使用配置文件(生产环境推荐)

配置文件 logging_config.json

{
    "version": 1,
    "disable_existing_loggers": false,
    "formatters": {
        "standard": {
            "format": "%(asctime)s [%(levelname)s] %(name)s: %(message)s"
        },
        "detailed": {
            "format": "%(asctime)s [%(levelname)s] %(filename)s:%(lineno)d - %(message)s"
        }
    },
    "handlers": {
        "console": {
            "class": "logging.StreamHandler",
            "level": "INFO",
            "formatter": "standard",
            "stream": "ext://sys.stdout"
        },
        "file": {
            "class": "logging.FileHandler",
            "level": "DEBUG",
            "formatter": "detailed",
            "filename": "app.log",
            "encoding": "utf-8"
        }
    },
    "loggers": {
        "": {
            "handlers": ["console", "file"],
            "level": "DEBUG",
            "propagate": true
        },
        "my_module": {
            "handlers": ["console"],
            "level": "WARNING",
            "propagate": false
        }
    }
}

Python 代码

import logging.config
import json
# 加载配置
with open('logging_config.json', 'r', encoding='utf-8') as f:
    config = json.load(f)
logging.config.dictConfig(config)
logger = logging.getLogger(__name__)
logger.info("应用启动成功")
logger.debug("这是一条调试信息")

日志轮转(防止日志文件过大)

import logging
from logging.handlers import RotatingFileHandler
logger = logging.getLogger('rotating_logger')
logger.setLevel(logging.INFO)
# 每个文件最大 1MB,保留最近 3 个备份
handler = RotatingFileHandler(
    'app.log', 
    maxBytes=1_000_000,  # 1MB
    backupCount=3,
    encoding='utf-8'
)
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)
# 模拟大量日志
for i in range(10000):
    logger.info(f"日志第 {i} 条")

装饰器方式自动记录函数日志

import logging
from functools import wraps
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(message)s')
logger = logging.getLogger(__name__)
def log_function_call(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        logger.info(f"调用 {func.__name__}(args={args}, kwargs={kwargs})")
        try:
            result = func(*args, **kwargs)
            logger.info(f"{func.__name__} 返回: {result}")
            return result
        except Exception as e:
            logger.error(f"{func.__name__} 异常: {e}", exc_info=True)
            raise
    return wrapper
@log_function_call
def calculate(x, y):
    return x / y
calculate(10, 2)
# 2025-03-30 10:30:00 - 调用 calculate(args=(10, 2), kwargs={})
# 2025-03-30 10:30:00 - calculate 返回: 5.0

日志级别说明

级别 数值 使用场景
DEBUG 10 调试信息,开发时使用
INFO 20 正常运行信息
WARNING 30 有潜在问题但程序仍可运行
ERROR 40 发生了错误,部分功能受影响
CRITICAL 50 严重错误,程序可能无法继续运行

总结建议

  • 开发调试basicConfig(level=DEBUG) 简单配置
  • 生产环境:同时输出到控制台(INFO)和文件(DEBUG)
  • 大型项目:使用配置文件统一管理
  • 重要操作:使用装饰器统一记录函数日志
  • 日志过大:使用 RotatingFileHandler 进行轮转

推荐在项目早期就引入 logging 模块,避免后期大量替换 print

抱歉,评论功能暂时关闭!