python日志模块 --- logging

logging

Python标准库中提供了方便的日志记录模块,可以根据日志的不同等级进行过滤和分别记录。根据我们的需要快速的自定义日志消息记录。其中有三个重要的对象

  • logger:程序中执行记录日志操作的对象,调用对应的方法即可产生一条日志消息
  • handler:负责管理日志消息的处理,例如写入文件还是作为标准输出
  • fromater:定义日志消息的格式

日志的等级划分

日志的级别分为以下几个等级,每个等级使用一个整数值对其进行量化指代,是一个常量值,其中logging模块中的默认的响应级别是

日志级别Level 数值 对应方法  
CRITICAL 50 critical() 严重错误级别,出现该错误已经影响到整体运行
ERROR 40 error() 错误级别,一般用于记录程序出现错误,但不影响整体运行
WARNING 30 默认级别 warning() 警告级别,,一般用于记录程序出现潜在错误的情形
INFO 20 info() 事件级别,一般用于记录程序的运行过程
DEBUG 10 debug 调试级别,一般用于记录程序运行的详细信息
NOTSET 0    

 

 

 

 

 

 

formatter

一条日志消息记录可以由用户自定义消息格式,日志消息中提供了以下信息的字符串占位符。

占位符格式 描述
%(levelno)s 日志级别的数值
%(levelname)s 日志级别名称
%(name)s 该logger对象的名字
%(module)s 当前执行程序名
%(funcName)s 日志调用的当前函数
%(lineno)d 日志调用的当前行行号
%(asctime)s 日志的时间,默认为2003-07-08 16.49.45,789格式
%(thread)d 线程ID
%(threadName)s 线程名称
%(process)d 进程ID
%(processName) 进程名称
%(message)s 日志信息

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

定义一个日志消息格式

# 获取了一个formatter对象,该对象需要被handler使用方可生效
formatter = logging.Formatter(fmt="%(asctime)s - %(name) - %(levelname)s - %(module)s: %(message)s")

Handler

logging中两种基本类型的handler,负责处理这些日志信息,但是需要根据formatter决定日志的输出结构信息。

  • FileHandler:将日志消息写入文件
  • StreamHandler:将日志消息写入标准输出
file_handle = logging.FileHandler(filename="log.txt", mode="a+", encoding="utf-8")
stream_handler = logging.StreamHandler()

fmt = logging.Formatter(fmt="%(asctime)s - %(name) - %(levelname)s - %(module)s: %(message)s")   # formatter 
file_handle.setFormatter(fmt) # 绑定formatter

Logger

每一个logger实例就是一个日志记录器,他们有自己属性,例如:名字,日志级别,父logger等

class Logger(Filterer):
    def __init__(self, name, level=NOTSET):
        Filterer.__init__(self)
        self.name = name                # logger名称
        self.level = _checkLevel(level) # logger级别,定义了0-50整十数极其常量,    logging.WARNING = 30,也使用"WARNING"字符串,_chackLevel(level)已做处理
        self.parent = None              # 继承的父类,命名时使用.作为继承的标志。例如m.m1表示m1继承于m,m必须存在。
        self.propagate = True           # 通过下层logger的消息默认向父类传播,给父类handler判断处理
        self.handlers = []              # 处理消息handler们,每一个分别处理
        self.disabled = False           # 标记为不可用

通常我们会通过logger  =  logging.getLogger(name, level)去获取一个logger,而不是示例化,该方法是logger提供的工厂方法,再创建前会根据name实现同名单例,也就不会产生同名的两个logger。

logger产生一条日志消息

使用对应的方法将会产生一条日志消息,同时还需要绑定handler和formater才能将这个消息输出记录。同时消息的等级必须同时高于logger以及handler的等级,日志消息才会被输出。

# 用于产生对应消息方法
debug(self, msg, *args, **kwargs)
info(msg, *args, **kwargs)
warning(msg, *args, **kwargs)
critical(msg, *args, **kwargs)
error(msg, *args, **kwargs)

setLevel(level)         # 设置level级别,高于该级别才会交给handler处理
getEffectiveLevel()     # 有效level,子logger向父logger继承的特性
addFilter(filter)       # 增加一个过滤对象
addHandler(hdlr)        # 增加一个handler对象
removeHandler(hdlr)     # 移除handler

常见的使用

有了三个对象,可以结合使用

file_handle = logging.FileHandler(filename="log.txt", mode="a+", encoding="utf-8")        # 创建handler,将日志写入log.txt文件
fmt = logging.Formatter(fmt="%(asctime)s - %(name) - %(levelname)s - %(module)s: %(message)s")  # 日志格式
file_handle.setFormatter(fmt)                                                            # 添加格式
logger = logging.Logger("cmdb", level=level)    # 获取handler,设置等级
logger.addHandler(file_handle) # 添加到logger中


# 使用
logger.info("一条日志消息")

# 记录栈信息
try:
  process()
except Exception as e:
  logger.error("错误信息", exc_info=True) # exc_info=True将会同时记录这个错误的traceback信息记录日志,方便查询错误信息

root-logger

当我们加载logger模块时,会自动创建一个root(一个logger),默认level为warning(30),只有消息等级高于warning的消息才能通过,使用logging.basicConfig()函数可以对root进行方便的初始化配置,它的参数有

 1 logging.basicConfig(
 2     filename=             # 如果消息写入文件,提供文件路径
 3     filemode=             # 设置读写模式,默认为"a"
 4     format=               # 格式化字符串例如:format = '%(asctime)-15s %(clientip)s %(user)-8s %(message)s'
 5 
 6     datefmt=              # 时间格式字符:"%Y-%M-%D %H:%m:%S"
 7     style=             
 8     level=                # root的level
 9     stream=               # 如果是流,指定一个流对象,例如 stream=os.stderr
10     handlers=             # 指定一个handdle
11 )

format参数中不但可以写入自带占位符,还可以写入自定义占位符,例如:format="%(key1)s",打印消息时候,指定该key的消息value即可。

 

日志轮转

在logging.handers模块中,提供了多种handler,这里常用的两种轮转日志handler类

RotatingFileHandler

logging提供了将日志写入文件中,但是服务在长期运行时会产生大量的信息,写入一个文件并不方便管理,同时也是不安全的。日志信息从始至终操作一个文件,如果在运行时出现故障造成日志文件丢失,将会造成所有日志信息的丢失,同时,随着时间的积累,我们可能需要对时间线过长的日志信息进行清理,这时候使用日志回滚(RotatingFile)

日志轮转:使用多个文件分层次的去记录日志信息。随着时间的推移,新的日志文件会将旧文件进行更迭,新日志消息的会将最早的信息清除。同时这些日志内容按照顺序被分片的放在不同文件中,这些文件个数和大小可以自定义,这样我们不再担心一个巨大的日志文件出现了。在logging模块中使用了RotatingHandler实现该功能,将处理日志文件时使用RotatingHandler来处理消息即可。

 1 import logging
 2 from logging import RotatingHandler
 3 
 4 
 5 logger = logging.getLogger(__name__)
 6 logger.setLevel(logging.INFO)
 7 
 8 formatter = logging.Formatter("'%(asctime)s - %(name)s - %(levelname)s - %(message)s", datefmt="%Y-%m-%D %H:%M:%S")
 9 
10 # 设置一个log.txt和两个回滚文件,每个文件最多储存1024字节大小的数据,当log.txt字节大于1024,会将数据写入log.txt.1文件,log.txt文件重置。
11 # log.txt.1文件再次被写入时会向log.txt.2文件写入内容,在接受从log.txt写入的新内容。
12 # log.txt.2文件写满后,继续写入将丢失最早的信息。最新的消息始终在log.txt中,之前的日志内容在log.txt.1和log.txt.2两个文件中轮转
13 file_hendler = logging.handlers.RotatingFileHandler(filename="log.txt", maxBytes=1024, backupCount=2)
14     # maxBytes 单个轮转文件最大字节数
15     # backupCount 轮转文件的个数;backupCount = 0时,log.txt可以不受maxBytes影响,一直写入数据,和普通日志文件相同
16 file_headler.setFormatter(formatter)
17 file_handler.setLevel(logging.INFO)
18 logger.addHandler(file)
19 
20 for i in range(100):
21     logger.warning("warning %d" % i)

TimedRotatingFileHandler

该handler和上面的轮转方式一样,使用多个文件来分别记录,但是分割方式是按照每天进行分割,每个日志文件记录当天的日志消息,可以设置最长的记录日期,这里设置了一个月

1 logger = logging.getLogger("abc")
2 logger.setLevel(logging.INFO)
3 formatter = logging.Formatter("'%(asctime)s - %(name)s - %(levelname)s - %(message)s", datefmt="%Y-%m-%D %H:%M:%S")
4 day_handler = TimedRotatingFileHandler("log.txt", when='D', interval=1, backupCount=30, encoding="utf-8")
5 
6 day_handler.setFormatter(formatter)
7 day_handler.setLevel(logging.INFO)

使用这种时间轮状的方式可以将日志按照每天进行分类,方便我们快速的对日志文件内容进行分析处理,也更加方便的管理。

原文地址:https://www.cnblogs.com/k5210202/p/13067831.html