19. python logging

 


19. python logging

19.1 logging模块的日志级别

  1. 日志级别
    • 日志级别用于区分日志的严重程度。以下是一些常见的日志级别(按严重程度递增):
      • DEBUG:用于记录调试信息,通常不需要在生产环境中启用。
      • INFO:用于记录程序的一般信息或状态。
      • WARNING:用于记录潜在的问题或错误。
      • ERROR:用于记录错误或异常。
      • CRITICAL:用于记录严重的错误或异常。
    • 默认情况下,logging 模块的日志级别被设置为 WARNING。这意味着只有级别为 WARNING 或更高级别的日志会被记录。

19.2 logging模块级别函数

logging.basicConfig()

logging.basicConfig() 适用于简单的日志记录需求,例如:

  • 快速记录日志到 标准输出文件
  • 控制 日志级别(DEBUG、INFO、WARNING等)
  • 指定 基本的日志格式
  • 适用于小型脚本一次性执行的任务

logging.basicConfig()函数常用参数

logging.basicConfig() 函数的关键字参数如下:

参数名 作用
level 设置日志级别(如 logging.DEBUGlogging.INFO 等)。
filename 指定日志输出到文件的文件名。
filemode 设置日志文件的模式(如 'w' 覆盖,'a' 追加)。
format 指定日志格式化字符串,如 '%(asctime)s - %(levelname)s - %(message)s'
datefmt 指定日期时间格式,例如 '%Y-%m-%d %H:%M:%S'
handlers 指定日志处理器列表,替代 filenamestream
stream 指定日志输出的流,如 sys.stdoutsys.stderr(与 filename 互斥)。
encoding 指定日志文件的编码,仅在 filename 存在时生效(如 'utf-8')。

format 参数用于指定日志消息的格式,它可以包含多个格式化占位符,以下是常用的格式说明符:

格式符 含义
%(asctime)s 当前时间,默认格式 YYYY-MM-DD HH:MM:SS,sss(毫秒)
%(levelname)s 日志级别名称(如 DEBUGINFOWARNING 等)
%(name)s 记录器的名称(即 logger 对象的 name 属性)
%(message)s 实际的日志消息
%(pathname)s 日志记录发生的源文件的完整路径
%(filename)s 日志记录发生的文件名(不含路径)
%(module)s 日志记录所在的模块名(filename 去掉扩展名)
%(funcName)s 日志记录所在的函数名
%(lineno)d 日志记录发生的行号
%(thread)d 线程 ID
%(threadName)s 线程名称
%(process)d 进程 ID
%(processName)s 进程名称
%(msecs)d 当前时间的毫秒部分
%(relativeCreated)d 相对程序启动时间(毫秒)
%(levelno)d 日志级别的数值(如 DEBUG=10INFO=20

简单示例:

import logging

logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s - %(module)s - %(lineno)d',
    datefmt='%Y/%m/%d %H:%M:%S',
    filename='app.log',
    filemode='w',
    encoding='utf-8'
)

logging.info("这是一个日志消息")

此代码会将日志消息写入 app.log 文件,采用 utf-8 编码,格式包含时间、级别和消息内容。

推荐的替代方案:使用 logging 进行更灵活的配置

对于运维工程师,推荐使用自定义日志配置,实现:

  • 同时输出到 文件控制台
  • 不同级别 记录到 不同文件
  • 日志切割
  • 更详细的日志格式

19.3 logging模块的类(自定义日志配置)

日志库采用模块化方法,并提供几类组件:记录器、处理器、过滤器和格式化器。

  1. 日志记录器(Logger)

    • 日志记录器是日志系统的入口点。可以通过 logging.getLogger(name) 方法创建日志记录器。如果未提供名称,则会返回默认记录器。
    • getLogger() 返回对具有指定名称的记录器实例的引用(如果已提供),或者如果没有则返回 root 。名称是以句点分隔的层次结构。多次调用 getLogger()具有相同的名称将返回对同一记录器对象的引用。在分层列表中较低的记录器是列表中较高的记录器的子项。

    记录器对象上使用最广泛的方法分为两类:配置和消息发送。

    这些是最常见的配置方法:

    • Logger.setLevel() 指定记录器将处理的最低严重性日志消息,其中 debug 是最低内置严重性级别, critical 是最高内置严重性级别。 例如,如果严重性级别为 INFO ,则记录器将仅处理 INFO 、 WARNING 、 ERROR 和 CRITICAL 消息,并将忽略 DEBUG 消息。
    • Logger.addHandler()Logger.removeHandler() 从记录器对象中添加和删除处理器对象。
    • Logger.addFilter()Logger.removeFilter() 可以添加或移除记录器对象中的过滤器。
  2. 日志处理器(Handler)

    • 处理器决定了日志的输出目标(如控制台、文件、网络等)。常用的处理器包括:
      • StreamHandler:将日志发送到流(如 sys.stdoutsys.stderr)。
      • FileHandler:将日志写入文件。
      • RotatingFileHandler:将日志写入文件,并在文件大小超过指定值时自动切割(支持日志回滚)。
      • TimedRotatingFileHandler:根据时间间隔切割日志文件(支持日志回滚)。

    Handler对象负责将适当的日志消息(基于日志消息的严重性)分派给处理器的指定目标。 Logger 对象可以使用 addHandler() 方法向自己添加零个或多个处理器对象。作为示例场景,应用程序可能希望将所有日志消息发送到日志文件,将错误或更高的所有日志消息发送到标准输出,以及将所有关键消息发送至一个邮件地址。 此方案需要三个单独的处理器,其中每个处理器负责将特定严重性的消息发送到特定位置。

    • 处理器常用方法:
      • setLevel()方法,就像在日志记录器对象中一样,指定将被分派到适当目标的最低严重性。 为什么有两个 setLevel() 方法? 在日志记录器中设置的级别确定要传递给其处理器的消息的严重性。 每个处理器中设置的级别则确定该处理器将发送哪些消息。
      • setFormatter()选择一个该处理器使用的 Formatter 对象。
      • addFilter()removeFilter()分别在处理器上配置和取消配置过滤器对象。
  3. 日志过滤器(Filter):

    • 日志记录可能不需要全部保存,如只保存某个级别的日志,或只保存包含某个关键子的日志等,这时可以使用过滤器限定要保存的部分。过滤器是由Filter类实例化的对象。记录器和处理器都可以使用过滤器来设置比日志级别更细粒度、更复杂的相关过滤功能。
  4. 日志格式化器(Formatter)

    • 格式器用于指定日志的输出格式。通过 logging.Formatter 创建格式器,并可以设置时间格式、日志级别、日志消息等内容。
    • fmt指定消息格式化字符串,datefmt指定日期格式化字符串,style设置样式指示符。
      • 如果 style'%',则消息格式字符串将使用 %(<dictionary key>)s 样式的字符串替换;
      • 如果样式为 '{',则将假定消息格式字符串与 str.format() 兼容(使用关键字参数);

示例:将日志消息同时输出到屏幕和文件

#!/usr/bin/python3
#_*_coding:utf-8_*_
import logging

#创建记录器,并其命名为Test
logger = logging.getLogger('Test')
#默认日志级别为WARNING,这里改为DEBUG
logger.setLevel(logging.DEBUG)
#创建FileHandler处理器,将日志消息输出到文件中,并设置特定的消息日志和日志格式
logfile = logging.FileHandler(filename='mylog.log',mode='w')
formatter1 = logging.Formatter(fmt='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s',datefmt='%a, %b %Y %H:%M:%S')
logfile.setFormatter(formatter1)
#创建StreamHandler处理器,将WARNING或更高级别的日志消息输出到控制台,设置特定消息格式
console = logging.StreamHandler()
console.setLevel(logging.WARNING)
formatter2 = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
console.setFormatter(formatter2)
#将处理器添加到记录器
logger.addHandler(logfile)
logger.addHandler(console)
#测试,输出不同的日志消息
logger.debug('这是debug')
logger.info('这是info')
logger.warning('这是warning')
logger.error('这是error')
logger.critical('这是critical')

示例:使用日志文件记录异常处理信息

#!/usr/bin/python3
#_*_coding:utf-8_*_
import logging

#创建记录器
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
#创建格式化器
logfile = logging.FileHandler(filename='mylog.log',mode='w')
formatter = logging.Formatter(fmt='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s',datefmt='%a, %b %Y %H:%M:%S')
logfile.setFormatter(formatter)

#将处理器添加到记录器
logger.addHandler(logfile)

#测试异常处理函数
def func(num1,num2):
    try:
        x = num1 * num2
        y = num1 / num2
        return x,y
    except Exception:
        #输出日志消息,exc_info参数确定是否将异常信息添加到日志消息中
        logger.error('出现异常!',exc_info=True)
        logger.info('已记录日志!')

if __name__ == '__main__':
    func(2,0)   #除以0触发异常

——>>输出:

(test-venv) root@jiaxing:~/test-venv# cat mylog.log 
Sun, Mar 2025 00:35:23 test.py[line:24] ERROR 出现异常!
Traceback (most recent call last):
  File "/root/test-venv/test.py", line 20, in func
    y = num1 / num2
ZeroDivisionError: division by zero
Sun, Mar 2025 00:35:23 test.py[line:25] INFO 已记录日志!

示例:按日志文件大小切割(RotatingFileHandler)

import logging
from logging.handlers import RotatingFileHandler

# 配置日志
logger = logging.getLogger("MyLogger")
logger.setLevel(logging.DEBUG)

# 创建一个 RotatingFileHandler,最大 5MB,每次超出后最多保留 3 个备份
handler = RotatingFileHandler("app.log", maxBytes=5*1024*1024, backupCount=3, encoding="utf-8")
formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")
handler.setFormatter(formatter)

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

# 测试写入日志
for i in range(10000):
    logger.info(f"这是第 {i} 条日志")

参数解析:

  • maxBytes=5*1024*1024:日志文件大小达到 5MB 后切割
  • backupCount=3:最多保留 3 个历史日志文件(app.log.1, app.log.2, app.log.3
  • encoding="utf-8":支持中文日志

示例:按时间切割日志文件(TimedRotatingFileHandler)

import logging
from logging.handlers import TimedRotatingFileHandler

# 配置日志
logger = logging.getLogger("MyTimedLogger")
logger.setLevel(logging.DEBUG)

# 创建一个 TimedRotatingFileHandler,每天切割一次日志,最多保留 7 天
handler = TimedRotatingFileHandler("timed_app.log", when="D", interval=1, backupCount=7, encoding="utf-8")
formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")
handler.setFormatter(formatter)

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

# 测试写入日志
for i in range(100):
    logger.info(f"这是第 {i} 条定时日志")

参数解析:

  • when="D":按天切割(支持 "S" 秒,"M" 分,"H" 小时,"D" 天,"W0-W6" 每周几)
  • interval=1:间隔 1 天切割一次
  • backupCount=7:最多保留 7 个历史日志文件
  • encoding="utf-8":支持中文日志
posted @   逃离这世界~  阅读(6)  评论(0编辑  收藏  举报
(评论功能已被禁用)
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
点击右上角即可分享
微信分享提示

目录导航