导航菜单

  • 1.vector
  • 2.milvus
  • 3.pymilvus
  • 4.rag
  • 5.rag_measure
  • ragflow
  • heapq
  • HNSW
  • cosine_similarity
  • math
  • typing
  • etcd
  • minio
  • collections
  • jieba
  • random
  • beautifulsoup4
  • chromadb
  • sentence_transformers
  • numpy
  • lxml
  • openpyxl
  • PyMuPDF
  • python-docx
  • requests
  • python-pptx
  • text_splitter
  • all-MiniLM-L6-v2
  • openai
  • llm
  • BPETokenizer
  • Flask
  • RAGAS
  • BagofWords
  • langchain
  • Pydantic
  • abc
  • faiss
  • MMR
  • scikit-learn
  • Runnable
  • PromptEngineering
  • dataclasses
  • LaTeX
  • rank_bm25
  • TF-IDF
  • asyncio
  • sqlalchemy
  • fastapi
  • Starlette
  • uvicorn
  • argparse
  • Generic
  • ssl
  • urllib
  • python-dotenv
  • RRF
  • CrossEncoder
  • Lost-in-the-middle
  • Jinja2
  • logger
  • io
  • venv
  • concurrent
  • parameter
  • SSE
  • 1. 什么是日志?为什么需要日志?
    • 1.1 为什么需要日志?
  • 2. 前置知识
    • 2.1 模块导入
    • 2.2 字符串格式化
    • 2.3 文件操作基础
    • 2.4 异常处理
  • 3. 日志级别详解
    • 3.1 日志级别从低到高:
    • 3.2 级别对应的数值:
  • 4. 最简单的使用方式
    • 4.1 基本示例
    • 4.2 设置不同的日志级别
    • 4.3 自定义日志格式
    • 4.4 将日志写入文件
  • 5. 核心概念详解
    • 5.1 Logger(记录器)
    • 5.2 Handler(处理器)
    • 5.3 Formatter(格式器)
    • 5.4 它们的关系
  • 6. 创建自定义 Logger
    • 6.1 创建简单的 Logger
    • 6.2 同时输出到控制台和文件
  • 7. 记录异常信息
    • 7.1 使用 exception() 方法
    • 7.2 使用 error() 方法记录异常
  • 8. 日志轮转
    • 8.1 按文件大小轮转
    • 8.2 按时间轮转
  • 9. 实际应用示例
  • 10. 模块级别的 Logger(最佳实践)
  • 11. 常用技巧
    • 11.1 临时调整日志级别
    • 11.2 禁用第三方库的日志
  • 12. 总结
    • 12.1 核心知识点
    • 12.2 最佳实践
    • 12.3 学习建议

1. 什么是日志?为什么需要日志? #

日志(Logging) 就是记录程序运行过程中的各种信息,就像写日记一样。当程序运行时,我们可以通过日志了解程序在做什么、发生了什么问题。

1.1 为什么需要日志? #

想象一下,你的程序在用户电脑上运行出错了,但你没有日志,就完全不知道哪里出了问题。有了日志,你就能看到:

  • 程序执行到哪一步了
  • 哪一行代码出错了
  • 错误发生时的数据是什么
  • 程序运行是否正常

日志的常见用途:

  • 调试程序:找出程序为什么出错
  • 监控运行状态:了解程序是否正常运行
  • 记录重要操作:比如用户登录、数据修改等
  • 问题追踪:当用户报告问题时,通过日志定位问题

2. 前置知识 #

在学习 Logging 模块之前,你需要了解以下 Python 基础知识:

2.1 模块导入 #

Logging 是 Python 标准库的一部分,直接导入即可使用:

# 导入 logging 模块
import logging

2.2 字符串格式化 #

日志中经常需要记录变量值,可以使用 f-string(Python 3.6+):

# 使用 f-string 格式化字符串
name = "张三"
age = 25
message = f"用户 {name} 的年龄是 {age}"

2.3 文件操作基础 #

日志可以写入文件,需要了解基本的文件操作:

# 打开文件(追加模式)
file = open('log.txt', 'a')
file.write('这是一条日志\n')
file.close()

2.4 异常处理 #

记录异常信息是日志的重要用途:

# 基本的异常处理
try:
    result = 1 / 0
except Exception as e:
    print(f"发生错误: {e}")

如果你还不熟悉这些内容,建议先学习 Python 基础教程。

3. 日志级别详解 #

日志级别(Log Level)用来区分日志的重要程度。就像医院的急诊分级一样,不同严重程度的问题用不同的级别。

3.1 日志级别从低到高: #

  1. DEBUG(调试):最详细的日志,用于开发调试时查看程序内部细节
  2. INFO(信息):一般信息,记录程序正常运行的状态
  3. WARNING(警告):警告信息,程序能运行但有潜在问题
  4. ERROR(错误):错误信息,程序某个功能出错了
  5. CRITICAL(严重错误):最严重的错误,程序可能崩溃

重要概念:当你设置日志级别后,只有该级别及以上的日志才会被记录。比如设置级别为 WARNING,那么只会记录 WARNING、ERROR、CRITICAL 的日志,DEBUG 和 INFO 会被忽略。

3.2 级别对应的数值: #

# 日志级别实际上是数字
DEBUG = 10
INFO = 20
WARNING = 30
ERROR = 40
CRITICAL = 50

数字越大,级别越高。设置级别时,低于该级别的日志都会被过滤掉。

4. 最简单的使用方式 #

对于初学者,最简单的方式是使用 basicConfig() 函数。这个函数可以快速配置日志系统,适合学习和简单项目。

4.1 基本示例 #

下面是一个最简单的日志示例:

# 导入 logging 模块
import logging

# 配置日志级别为 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:这是一条严重错误信息

说明:

  • root 是默认的 logger 名称(后面会详细讲解)
  • 每条日志的格式是:级别:logger名称:消息内容

4.2 设置不同的日志级别 #

你可以设置不同的日志级别,看看会发生什么:

# 导入 logging 模块
import logging

# 设置日志级别为 WARNING(只显示 WARNING 及以上级别)
logging.basicConfig(level=logging.WARNING)

# 记录不同级别的日志
logging.debug('调试信息 - 不会显示')
logging.info('普通信息 - 不会显示')
logging.warning('警告信息 - 会显示')
logging.error('错误信息 - 会显示')
logging.critical('严重错误 - 会显示')

运行结果:

WARNING:root:警告信息 - 会显示
ERROR:root:错误信息 - 会显示
CRITICAL:root:严重错误 - 会显示

可以看到,DEBUG 和 INFO 级别的日志被过滤掉了,因为它们的级别低于 WARNING。

4.3 自定义日志格式 #

默认的日志格式可能不够详细,我们可以自定义格式:

# 导入 logging 模块
import logging

# 配置日志格式和级别
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s',
    datefmt='%Y-%m-%d %H:%M:%S'
)

# 记录日志
logging.info('程序启动')
logging.warning('内存使用率较高')
logging.error('连接数据库失败')

运行结果:

2024-01-15 10:30:45 - INFO - 程序启动
2024-01-15 10:30:45 - WARNING - 内存使用率较高
2024-01-15 10:30:45 - ERROR - 连接数据库失败

格式说明:

  • %(asctime)s:时间戳
  • %(levelname)s:日志级别名称
  • %(message)s:日志消息内容
  • datefmt:时间格式

4.4 将日志写入文件 #

默认情况下,日志会输出到控制台。我们也可以将日志写入文件:

# 导入 logging 模块
import logging

# 配置日志:输出到文件,使用追加模式
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s',
    datefmt='%Y-%m-%d %H:%M:%S',
    filename='app.log',  # 日志文件名
    filemode='a'  # 'a' 表示追加模式,'w' 表示覆盖模式
)

# 记录日志(这些日志会写入 app.log 文件,不会显示在控制台)
logging.info('程序启动')
logging.warning('这是一个警告')
logging.error('这是一个错误')

运行后,会在当前目录生成 app.log 文件,里面包含所有日志记录。

5. 核心概念详解 #

要深入理解 Logging 模块,需要了解几个核心概念。这些概念就像搭积木一样,组合起来就构成了完整的日志系统。

5.1 Logger(记录器) #

Logger 是日志系统的入口,我们通过它来记录日志。可以把它想象成一个"日志记录员"。

特点:

  • 每个 Logger 都有一个名称
  • 可以设置日志级别
  • 可以有多个 Handler(处理器)

5.2 Handler(处理器) #

Handler 决定日志输出到哪里。就像邮递员一样,负责把日志"送"到目的地。

常见的 Handler:

  • StreamHandler:输出到控制台(终端)
  • FileHandler:输出到文件
  • RotatingFileHandler:输出到文件,支持文件大小轮转

5.3 Formatter(格式器) #

Formatter 决定日志的显示格式。就像给日志"化妆",让它更美观、更易读。

5.4 它们的关系 #

Logger(记录器)
    ↓
Handler(处理器)→ Formatter(格式器)
    ↓
输出到控制台/文件

工作流程:

  1. 你调用 logger.info('消息')
  2. Logger 检查级别,如果通过就创建日志记录
  3. Logger 把记录交给 Handler
  4. Handler 用 Formatter 格式化日志
  5. Handler 输出到目标位置(控制台或文件)

6. 创建自定义 Logger #

在实际项目中,我们通常不使用默认的 root logger,而是创建自己的 Logger。这样可以更好地管理不同模块的日志。

6.1 创建简单的 Logger #

# 导入 logging 模块
import logging

# 创建一个名为 'my_app' 的 logger
logger = logging.getLogger('my_app')

# 设置日志级别
logger.setLevel(logging.DEBUG)

# 创建控制台处理器
console_handler = logging.StreamHandler()

# 设置处理器的日志级别
console_handler.setLevel(logging.INFO)

# 创建格式器
formatter = logging.Formatter(
    '%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    datefmt='%Y-%m-%d %H:%M:%S'
)

# 将格式器添加到处理器
console_handler.setFormatter(formatter)

# 将处理器添加到 logger
logger.addHandler(console_handler)

# 使用 logger 记录日志
logger.debug('这是调试信息')
logger.info('程序启动成功')
logger.warning('内存不足')
logger.error('发生错误')

运行结果:

2024-01-15 10:30:45 - my_app - INFO - 程序启动成功
2024-01-15 10:30:45 - my_app - WARNING - 内存不足
2024-01-15 10:30:45 - my_app - ERROR - 发生错误

说明:

  • getLogger('my_app') 创建一个名为 'my_app' 的 logger
  • setLevel() 设置 logger 的级别
  • StreamHandler() 创建控制台处理器
  • Formatter() 创建格式器
  • addHandler() 将处理器添加到 logger

6.2 同时输出到控制台和文件 #

在实际项目中,我们通常希望日志既显示在控制台,又保存到文件:

# 导入 logging 模块
import logging

# 创建 logger
logger = logging.getLogger('my_app')
logger.setLevel(logging.DEBUG)

# 创建格式器
formatter = logging.Formatter(
    '%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    datefmt='%Y-%m-%d %H:%M:%S'
)

# 创建控制台处理器
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO)  # 控制台只显示 INFO 及以上级别
console_handler.setFormatter(formatter)

# 创建文件处理器
file_handler = logging.FileHandler('app.log', mode='a', encoding='utf-8')
file_handler.setLevel(logging.DEBUG)  # 文件记录所有 DEBUG 及以上级别
file_handler.setFormatter(formatter)

# 将处理器添加到 logger
logger.addHandler(console_handler)
logger.addHandler(file_handler)

# 使用 logger 记录日志
logger.debug('调试信息 - 只在文件中可见')
logger.info('程序启动 - 控制台和文件都可见')
logger.warning('警告信息 - 控制台和文件都可见')
logger.error('错误信息 - 控制台和文件都可见')

说明:

  • 控制台处理器级别设为 INFO,所以 DEBUG 信息不会在控制台显示
  • 文件处理器级别设为 DEBUG,所以所有日志都会写入文件
  • 这样可以在控制台看到重要信息,同时在文件中保存详细日志

7. 记录异常信息 #

记录异常信息是日志的重要用途。当程序出错时,我们需要记录完整的错误信息,包括错误类型、错误消息和堆栈跟踪。

7.1 使用 exception() 方法 #

最简单的方式是使用 logger.exception(),它会自动记录异常信息:

# 导入 logging 模块
import logging

# 配置日志
logging.basicConfig(
    level=logging.DEBUG,
    format='%(asctime)s - %(levelname)s - %(message)s',
    datefmt='%Y-%m-%d %H:%M:%S'
)

# 创建 logger
logger = logging.getLogger(__name__)

# 模拟一个会出错的函数
def divide(a, b):
    # 尝试除法运算
    result = a / b
    return result

# 使用 try-except 捕获异常并记录
try:
    # 尝试执行可能出错的代码
    result = divide(10, 0)
except Exception:
    # 记录异常信息(会自动包含堆栈跟踪)
    logger.exception('除法运算出错')

运行结果:

2024-01-15 10:30:45 - ERROR - 除法运算出错
Traceback (most recent call last):
  File "test.py", line 15, in <module>
    result = divide(10, 0)
  File "test.py", line 8, in divide
    result = a / b
ZeroDivisionError: division by zero

7.2 使用 error() 方法记录异常 #

也可以使用 logger.error() 配合 exc_info=True 参数:

# 导入 logging 模块
import logging

# 配置日志
logging.basicConfig(
    level=logging.DEBUG,
    format='%(asctime)s - %(levelname)s - %(message)s',
    datefmt='%Y-%m-%d %H:%M:%S'
)

# 创建 logger
logger = logging.getLogger(__name__)

# 模拟一个会出错的函数
def process_data(data):
    # 尝试访问不存在的键
    value = data['key']

# 使用 try-except 捕获异常
try:
    # 尝试处理数据
    process_data({})
except Exception as e:
    # 记录异常信息
    logger.error(f'处理数据时出错: {e}', exc_info=True)

说明:

  • logger.exception() 等价于 logger.error(..., exc_info=True)
  • exc_info=True 会记录完整的异常堆栈信息
  • 异常信息对于调试非常重要,一定要记录

8. 日志轮转 #

当日志文件变得很大时,会影响程序性能。日志轮转(Log Rotation)可以自动创建新的日志文件,避免单个文件过大。

8.1 按文件大小轮转 #

当日志文件达到指定大小时,自动创建新文件:

# 导入 logging 模块和轮转处理器
import logging
from logging.handlers import RotatingFileHandler

# 创建 logger
logger = logging.getLogger('my_app')
logger.setLevel(logging.DEBUG)

# 创建格式器
formatter = logging.Formatter(
    '%(asctime)s - %(levelname)s - %(message)s',
    datefmt='%Y-%m-%d %H:%M:%S'
)

# 创建轮转文件处理器
# maxBytes: 文件最大大小(1MB)
# backupCount: 保留的备份文件数量(5个)
rotating_handler = RotatingFileHandler(
    'app.log',
    maxBytes=1024 * 1024,  # 1MB
    backupCount=5,
    encoding='utf-8'
)
rotating_handler.setLevel(logging.DEBUG)
rotating_handler.setFormatter(formatter)

# 添加处理器
logger.addHandler(rotating_handler)

# 记录日志
for i in range(100):
    logger.info(f'这是第 {i} 条日志信息')

说明:

  • 当日志文件达到 1MB 时,会自动重命名为 app.log.1
  • 原来的 app.log.1 会变成 app.log.2,依此类推
  • 最多保留 5 个备份文件(app.log.1 到 app.log.5)
  • 超过 5 个的旧文件会被删除

8.2 按时间轮转 #

每天自动创建新的日志文件:

# 导入 logging 模块和时间轮转处理器
import logging
from logging.handlers import TimedRotatingFileHandler

# 创建 logger
logger = logging.getLogger('my_app')
logger.setLevel(logging.DEBUG)

# 创建格式器
formatter = logging.Formatter(
    '%(asctime)s - %(levelname)s - %(message)s',
    datefmt='%Y-%m-%d %H:%M:%S'
)

# 创建时间轮转文件处理器
# when: 轮转时间单位('midnight' 表示每天午夜)
# interval: 时间间隔(1 天)
# backupCount: 保留的备份文件数量(7 天)
timed_handler = TimedRotatingFileHandler(
    'app.log',
    when='midnight',
    interval=1,
    backupCount=7,
    encoding='utf-8'
)
timed_handler.setLevel(logging.DEBUG)
timed_handler.setFormatter(formatter)

# 添加处理器
logger.addHandler(timed_handler)

# 记录日志
logger.info('程序启动')
logger.warning('这是一条警告')

说明:

  • when='midnight' 表示每天午夜轮转
  • interval=1 表示每 1 个时间单位轮转一次
  • backupCount=7 表示保留 7 天的日志文件
  • 轮转后的文件会自动添加日期后缀,如 app.log.2024-01-15

9. 实际应用示例 #

下面是一个完整的、可以在实际项目中使用的日志配置示例:

# 导入必要的模块
import logging
import sys
from logging.handlers import RotatingFileHandler

def setup_logger(name, log_file='app.log', level=logging.INFO):
    """
    设置并返回一个配置好的 logger

    参数:
        name: logger 的名称(通常使用 __name__)
        log_file: 日志文件名
        level: 日志级别
    返回:
        配置好的 logger 对象
    """
    # 创建 logger
    logger = logging.getLogger(name)
    logger.setLevel(level)

    # 避免重复添加处理器(如果 logger 已经有处理器,直接返回)
    if logger.handlers:
        return logger

    # 创建格式器
    # 格式包含:时间、logger名称、级别、文件名和行号、消息
    formatter = logging.Formatter(
        '%(asctime)s - %(name)s - %(levelname)s - [%(filename)s:%(lineno)d] - %(message)s',
        datefmt='%Y-%m-%d %H:%M:%S'
    )

    # 创建控制台处理器
    console_handler = logging.StreamHandler(sys.stdout)
    console_handler.setFormatter(formatter)
    console_handler.setLevel(logging.INFO)  # 控制台只显示 INFO 及以上级别

    # 创建文件处理器(带轮转功能)
    # maxBytes: 10MB
    # backupCount: 保留 5 个备份文件
    file_handler = RotatingFileHandler(
        log_file,
        maxBytes=10 * 1024 * 1024,  # 10MB
        backupCount=5,
        encoding='utf-8'
    )
    file_handler.setFormatter(formatter)
    file_handler.setLevel(logging.DEBUG)  # 文件记录所有 DEBUG 及以上级别

    # 添加处理器到 logger
    logger.addHandler(console_handler)
    logger.addHandler(file_handler)

    return logger

# 使用示例
if __name__ == '__main__':
    # 创建 logger
    logger = setup_logger(__name__)

    # 记录不同级别的日志
    logger.debug('这是调试信息(只在文件中可见)')
    logger.info('程序启动成功')
    logger.warning('内存使用率较高')
    logger.error('连接数据库失败')

    # 记录异常信息
    try:
        # 模拟一个会出错的代码
        result = 1 / 0
    except Exception:
        logger.exception('发生除零错误')

    # 记录带变量的日志
    user_id = 123
    username = '张三'
    logger.info(f'用户 {username} (ID: {user_id}) 登录成功')

这个示例的特点:

  • 同时输出到控制台和文件
  • 控制台显示重要信息,文件保存详细日志
  • 支持日志文件轮转,避免文件过大
  • 包含文件名和行号,方便定位问题
  • 可以记录异常信息

10. 模块级别的 Logger(最佳实践) #

在实际项目中,建议在每个模块中创建自己的 logger,使用模块名作为 logger 名称:

# 导入 logging 模块
import logging

# 在每个模块开头创建 logger(使用 __name__ 作为名称)
logger = logging.getLogger(__name__)

# 定义用户类
class User:
    def __init__(self, name):
        # 记录对象创建
        logger.info(f'创建用户对象: {name}')
        self.name = name

    def login(self):
        # 记录用户登录
        logger.info(f'用户 {self.name} 登录')
        return True

# 使用示例
if __name__ == '__main__':
    # 配置日志(在实际项目中,这通常在程序入口处配置一次)
    logging.basicConfig(
        level=logging.INFO,
        format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
        datefmt='%Y-%m-%d %H:%M:%S'
    )

    # 创建用户对象
    user = User('张三')
    user.login()

运行结果:

2024-01-15 10:30:45 - __main__ - INFO - 创建用户对象: 张三
2024-01-15 10:30:45 - __main__ - INFO - 用户 张三 登录

说明:

  • 使用 __name__ 作为 logger 名称,可以自动识别是哪个模块的日志
  • 这样在日志中就能清楚地看到每条日志来自哪个模块
  • 这是 Python 社区推荐的最佳实践

11. 常用技巧 #

11.1 临时调整日志级别 #

有时候我们需要临时提高或降低日志级别:

# 导入 logging 模块
import logging

# 创建 logger
logger = logging.getLogger(__name__)

# 配置日志
logging.basicConfig(level=logging.INFO)

# 保存原始日志级别
original_level = logger.level

# 临时提高日志级别(只显示 WARNING 及以上)
logger.setLevel(logging.WARNING)
logger.debug('这条调试信息不会显示')
logger.info('这条普通信息不会显示')
logger.warning('这条警告信息会显示')

# 恢复原始日志级别
logger.setLevel(original_level)
logger.info('现在普通信息又会显示了')

11.2 禁用第三方库的日志 #

有时候第三方库会产生很多日志,我们可以提高它们的日志级别:

# 导入 logging 模块
import logging

# 禁用 requests 库的 DEBUG 和 INFO 日志(只显示 WARNING 及以上)
logging.getLogger('requests').setLevel(logging.WARNING)

# 禁用 urllib3 库的日志
logging.getLogger('urllib3').setLevel(logging.WARNING)

12. 总结 #

Logging 模块是 Python 开发中必不可少的工具。通过本教程,你应该已经掌握了:

12.1 核心知识点 #

  1. 日志级别:DEBUG < INFO < WARNING < ERROR < CRITICAL
  2. 基本使用:basicConfig() 快速配置
  3. 核心概念:Logger、Handler、Formatter
  4. 自定义配置:创建自己的 logger 和 handler
  5. 异常记录:使用 logger.exception() 记录异常
  6. 日志轮转:避免日志文件过大

12.2 最佳实践 #

  1. 使用模块级别的 logger:logger = logging.getLogger(__name__)
  2. 同时输出到控制台和文件:控制台显示重要信息,文件保存详细日志
  3. 合理设置日志级别:开发时用 DEBUG,生产环境用 INFO 或 WARNING
  4. 记录异常信息:使用 logger.exception() 记录完整的错误堆栈
  5. 使用日志轮转:避免日志文件过大影响性能

12.3 学习建议 #

  • 从简单的 basicConfig() 开始学习
  • 逐步理解 Logger、Handler、Formatter 的关系
  • 在实际项目中练习使用
  • 根据项目需求选择合适的日志级别和格式

记住:好的日志记录习惯会让你的调试工作事半功倍!

← 上一节 llm 下一节 Lost-in-the-middle →

访问验证

请输入访问令牌

Token不正确,请重新输入