python logging模块

logging模块的使用方式


logging模块提供了两种记录日志的方式:

  • 第一种是logging提供的模块级别函数
  • 第二种是使用Logging日志系统的四大组件

其实,logging所提供的模块级别的日志记录函数也是对logging日志系统相关类的封装而已。


一、使用logging提供的模块级别函数

# coding: utf-8

"""
使用模块级别的函数记录日志
配置logging.basicConfig函数,把日志输出到文件
"""

import logging
import os

BASE_DIR = os.path.dirname(os.path.abspath(__file__))
LOG_FILE_PATH = os.path.join(BASE_DIR, "my.log")

LOG_FORMAT = "%(asctime)s - %(levelname)s - %(message)s"

# 不提供filename则默认输出到控制台
logging.basicConfig(filename=LOG_FILE_PATH, level=logging.DEBUG, format=LOG_FORMAT)

logging.debug("This is a debug log.")
logging.info("This is a info log.")
logging.warning("This is a warning log.")
logging.error("This is a error log.")
logging.critical("This is a critical log.")
View Code

 1、logging.basicConfig(**kwargs)函数说明

参数名称描述
filename 指定日志输出目标文件的文件名,指定该设置后日志信息就不会被输出到控制台了
filemode 指定日志文件的打开模式,默认为'a'。需要注意的是,该选项要在filename指定时才有效
format 指定日志格式字符串,即指定日志输出时所包含的字段信息以及它们的顺序。logging模块定义的格式字段下面会列出。
datefmt 指定日期/时间格式。需要注意的是,该选项要在format中包含时间字段%(asctime)s时才有效
level 指定日志器的日志级别
stream 指定日志输出目标stream,如sys.stdout、sys.stderr以及网络stream。需要说明的是,stream和filename不能同时提供,否则会引发 ValueError异常
style Python 3.2中新添加的配置项。指定format格式字符串的风格,可取值为'%'、'{'和'$',默认为'%'
handlers Python 3.3中新添加的配置项。该选项如果被指定,它应该是一个创建了多个Handler的可迭代对象,这些handler将会被添加到root logger。需要说明的是:filename、stream和handlers这三个配置项只能有一个存在,不能同时出现2个或3个,否则会引发ValueError异常。


2、logging模块定义的格式字符串 

字段/属性名称使用格式描述
asctime %(asctime)s 日志事件发生的时间--人类可读时间,如:2003-07-08 16:49:45,896
created %(created)f 日志事件发生的时间--时间戳,就是当时调用time.time()函数返回的值
relativeCreated %(relativeCreated)d 日志事件发生的时间相对于logging模块加载时间的相对毫秒数(目前还不知道干嘛用的)
msecs %(msecs)d 日志事件发生事件的毫秒部分
levelname %(levelname)s 该日志记录的文字形式的日志级别('DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL')
levelno %(levelno)s 该日志记录的数字形式的日志级别(10, 20, 30, 40, 50)
name %(name)s 所使用的日志器名称,默认是'root',因为默认使用的是 rootLogger
message %(message)s 日志记录的文本内容,通过 msg % args计算得到的
pathname %(pathname)s 调用日志记录函数的源码文件的全路径
filename %(filename)s pathname的文件名部分,包含文件后缀
module %(module)s filename的名称部分,不包含后缀
lineno %(lineno)d 调用日志记录函数的源代码所在的行号
funcName %(funcName)s 调用日志记录函数的函数名
process %(process)d 进程ID
processName %(processName)s 进程名称,Python 3.1新增
thread %(thread)d 线程ID
threadName %(threadName)s 线程名称

logging.info("name: %s" % "Mikle")和logging.info("name: %s", "Mikle")这两种是一样的,因为message是通过msg % args计算得到的。

3、logging日志级别

日志等级(level)描述
DEBUG 最详细的日志信息,典型应用场景是 问题诊断
INFO 信息详细程度仅次于DEBUG,通常只记录关键节点信息,用于确认一切都是按照我们预期的那样进行工作
WARNING 当某些不期望的事情发生时记录的信息(如,磁盘可用空间较低),但是此时应用程序还是正常运行的
ERROR 由于一个更严重的问题导致某些功能不能正常运行时记录的信息
CRITICAL 当发生严重错误,导致应用程序不能继续运行时记录的信息

二、logging四大组件

组件名称对应类名功能描述
日志器 Logger 提供了应用程序可一直使用的接口
处理器 Handler 将logger创建的日志记录发送到合适的目的输出
过滤器 Filter 提供了更细粒度的控制工具来决定输出哪条日志记录,丢弃哪条日志记录
格式器 Formatter 决定日志记录的最终输出格式

logging组件记录日志流程

  1. 定义日志器对象logger
  2. 设置日志器对象的日志等级,log等级总开关
  3. 定义处理器对象handler,一个logger可以对应多个处理器对象
  4. 定义格式器对象Formatter
  5. 把格式器对象添加到处理器对象
  6. 把处理器对象添加到日志器对象
# coding: utf-8

"""
1)将所有级别的所有日志都写入磁盘文件中
2)all.log文件中记录所有的日志信息,日志格式为:日期和时间 - 日志级别 - 日志信息
3)error.log文件中单独记录error及以上级别的日志信息,日志格式为:日期和时间 - 日志级别 - 文件名[:行号] - 日志信息
4)要求all.log在每天凌晨进行日志切割(TimedRotatingFileHandler)
"""

import logging
import logging.handlers

logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)

# 1、TimedRotatingFileHandler
rf_handler = logging.handlers.TimedRotatingFileHandler('all.log', when='midnight', interval=1, backupCount=7)
fmt = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
rf_handler.setFormatter(fmt)

# 2、FileHandler
f_handler = logging.FileHandler('error.log')
f_handler.setLevel(logging.ERROR)
fmt2 = logging.Formatter("%(asctime)s - %(levelname)s - %(filename)s[:%(lineno)d] - %(message)s")
f_handler.setFormatter(fmt2)

logger.addHandler(rf_handler)
logger.addHandler(f_handler)

logger.debug('debug message')
logger.info('info message')
logger.warning('warning message')
logger.error('error message')
logger.critical('critical message')
View Code
以下代码可以直接使用,把代码保存为log.py,然后import log就可以直接使用了。
#! /usr/bin/env python
# -*- coding: utf-8 -*-
"""
log模块第一次导入的时候,是作为一个文件被查找到的。查找成功后,文件会跑一个生成log实例的逻辑,然后把它加入到全局sys.modules字典里面。
以后所有模块的`import log`动作都会绕开文件查找的过程,直接在sys.modules里面找这个模块。
"""
"""
可以直接使用
import log

log.info()
log.error()
...
"""

import os
import sys
import logging
import logging.handlers

# 当前目录下会生成对应的log文件
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
ACCESS_LOG_PATH = os.path.join(BASE_DIR, "access.log")
ERROR_LOG_PATH = os.path.join(BASE_DIR, "error.log")

class Log(object):                                                                                                                                                   [0/708]

    ACCESS_LOG_PATH = ACCESS_LOG_PATH
    ERROR_LOG_PATH = ERROR_LOG_PATH
    FMT = (
        '[%(levelname)s][%(name)s:%(process)d][%(asctime)s]' +
        ': %(message)s')

    def __init__(self, name):
        logger = logging.getLogger(name)

        fmt = logging.Formatter(self.FMT)

        trf_handler = logging.handlers.TimedRotatingFileHandler(
            self.ACCESS_LOG_PATH, 'D', 1, 7)
        trf_handler.setLevel(logging.INFO)
        trf_handler.setFormatter(fmt)

        rf_handler = logging.handlers.RotatingFileHandler(
            self.ERROR_LOG_PATH,
            maxBytes=1024*1024, backupCount=5)
        rf_handler.setLevel(logging.ERROR)
        rf_handler.setFormatter(fmt)

        logger.addHandler(trf_handler)
        logger.addHandler(rf_handler)

        self.logger = logger

    def __call__(self):
        return self.logger


sys.modules[__name__] = Log(__name__)()
View Code

 RotatingFileHandler

日志回滚

比如日志文件是chat.log,当chat.log达到指定的大小之后,RotatingFileHandler自动把文件改名为chat.log.1。不过,如果chat.log.1已经存在,会先把chat.log.1重命名为chat.log.2。最后重新创建 chat.log,继续输出日志信息。【这样保证了chat.log里面是最新的日志】
(原文链接:https://blog.csdn.net/chpllp/article/details/72649506)

RotatingFileHandler参数说明

def __init__(self, filename, mode='a', maxBytes=0, backupCount=0, encoding=None, delay=0)

参数描述
filename log文件名
mode 写入日志文件的模式,默认'a',追加
maxBytes

日志文件大小,即大于多少时切割日志。如果是0,则不切割。

比如50M,maxBytes=1024*1024*50

backupCount 文件备份数量,比如日志文件是chat.log,当chat.log达到指定大小后,RotatingFileHandler自动把文件改名为chat.log.1,如果backupCount=5,则文件名一直到chat.log.5
encoding 写入日志编码,默认None
delay 写入log文件的延迟时间,默认0

TimedRotatingFileHandler

按时间切割日志

TimedRotatingFileHandler参数说明

def __init__(self, filename, when='h', interval=1, backupCount=0, encoding=None, delay=False, utc=False)

参数描述
filename log文件名
when

切割日志的时间

"S": 每秒

"M": 每分钟

"H": 每小时

"D": 每天(每天的0:00)

"MIDNIGHT": 与"D"相同

"W{0~6}": 每个星期,W0 表示周日

when参数忽略大小写,即用大小写都行,因为参数会先upper()转换成大写。用"M"和"m"是相同的。

interval

间隔,间隔多少个when执行一次。如果when="D", interval=2,则每隔两天切割一次。默认1。

backupCount 备份数量,备份的log文件后缀会跟上时间
encoding 写入日志编码,默认None
delay 写入log文件的延迟时间,默认0
utc  当when是"D"或"W{0~6}"时,是否以UTC的时间来切割,默认False,表示本地时间。

RotatingFileHandler存在的问题 

https://www.jianshu.com/p/1772306cb3db?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

参考文档

https://www.cnblogs.com/yyds/p/6901864.html

原文地址:https://www.cnblogs.com/shengmading/p/14737482.html