python模块--logging模块

logging的优势就在于可以控制日志的级别,把不需要的信息进行过滤,且可以决定它输出到什么地方、如何输出,还可以通过控制等级把特定等级的信息输出到特定的位置等。

logging一共分为四个部分:💗常用哦

  • 📘 Loggers:日志收集器,可供程序直接调用的接口,app通过调用提供的api来记录日志

  • 📔 Handlers:日志处理器, 决定将日志记录分配至正确的目的地

  • 📙 Filters:日志过滤器,对日志信息进行过滤, 提供更细粒度的日志是否输出的判断

  • 📒 Formatters:日志格式器,制定最终记录打印的格式布局

日志等级:

  1. 用作记录日志,默认分为六种日志级别(括号为级别对应的数值)

Level

说明

NOTSET(0)

DEBUG(10)

输出详细的运行信息,主要用于调试,追踪问题时使用

INFO(20)

输出正常的运行的信息,一切按预期进行的情况

WARNING(30)

一些意想不到的或即将会发生的情况,比如警告:内存空间不足,但不影响程序运行

ERROR(40)

由于某些问题,程序的一些功能会受到影响,还可以继续运行

CRITICAL(50)

一个严重的错误,表明程序本身可能无法继续运行

这些等级的日志中低包含高,

比如INFO,会收集INFO及以上等级的日志,DEBUG等级的日志将不进行收集。下面我们来输出这5个等级的日志:

日志收集器: getLogger():💗

import logging

# 获取默认的日志收集器root
my_log = logging.getLogger()
# 设置默认的日志收集器等级
my_log.setLevel("DEBUG")  # 日志将全部被收集

a = 100
logging.debug(a)
logging.info('这是INFO等级的信息')
logging.warning('这是WARNING等级的信息')
logging.error('这是ERROR等级的信息')
logging.critical('这是CRITICAL等级的信息')

DEBUG:root:100
INFO:root:这是INFO等级的信息
WARNING:root:这是WARNING等级的信息
ERROR:root:这是ERROR等级的信息
CRITICAL:root:这是CRITICAL等级的信息

日志处理器:Handlers(): 💗

  • logging模块中包含的类

  • 用来自定义日志对象的规则(比如:设置日志输出格式、等级等)

  • 常用3个子类:StreamHandler【送往控制台】、FileHandler【文件】、TimedRotatingFileHandler

  • Handlers(处理器)的作用就是将logger发过来的信息进行准确地分配,送往正确的地方。

StreamHandler() 控制台输出日志。

它决定了每个日志收集器的行为,是创建收集器之后需要配置的重点区域。

每个Handler同样有一个日志级别,一个logger可以拥有多个handler也就是说logger可以根据不同的日志级别将日志传递给不同的handler。当然也可以相同的级别传递给多个handler,这就根据需求来灵活的配置了。

输出到控制台
sh = logging.StreamHandler()
sh.setLevel("ERROR")  # 设置处理器的输出等级
my_logger.addHandler(sh)  # 将处理器绑定到日志收集器上
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
import logging

# 创建一个logger日志对象
logger = logging.getLogger('test_logger')
logger.setLevel(logging.DEBUG)  # 设置默认的日志级别

# 创建日志格式对象
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')

# 创建StreamHandler对象【控制台输出】
sh = logging.StreamHandler()
# StreamHandler对象自定义日志级别
sh.setLevel(logging.DEBUG)
# StreamHandler对象自定义日志格式
sh.setFormatter(formatter)

logger.addHandler(sh)  # logger日志对象加载StreamHandler对象
# 日志输出
logger.info('newdream')

FileHandler()日志输出到文件

输出到文件中
fh = logging.FileHandler("logs.logs", encoding="utf8")fh.setLevel("INFO")my_logger = logging.getLogger('my_logger')  # 获取默认的日志收集器root
import logging

#创建一个logger日志对象
logger = logging.getLogger('test_logger')
logger.setLevel(logging.DEBUG)    #设置默认的日志级别

#创建日志格式对象
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')

#创建FileHandler对象
fh = logging.FileHandler('test.log')
#FileHandler对象自定义日志级别
fh.setLevel(logging.DEBUG)
#FileHandler对象自定义日志格式
fh.setFormatter(formatter)

logger.addHandler(fh)    #logger日志对象加载FileHandler对象
#日志输出
logger.info('newdream')

上面代码执行后,控制台不会输出日志信息,而会在当前文件夹新建一个test.log日志文件进行日志记录。

TimedRotatingFileHandler日志滚动

FileHandler 存储日志,文件的大小会随着时间推移而不断增大,最终有一天它会占满你所有的磁盘空间。【解决文件大小过大的问题】

RotatingFileHandler 滚动时刻

日志文件的大小达到一定值,当达到指定值的时候,RotatingFileHandler会将日志文件 重命名存档,然后打开一个新的日志文件。

TimedRotatingFileHandler它的构造函数定义如下:

是当某个时刻到来时就进行滚动,同 RotatingFileHandler 一样,当滚动时机来临时,TimedRotatingFileHandler 会将日志文件重命名存档,然后打开一个新的日志文件。

TimedRotatingFileHandler(filename [,when [,interval [,backupCount]]])
内置函数解释:
filename:     日志文件名;

when:         是一个字符串,用于描述滚动周期的基本单位,字符串的值及意义如下:
    S - Seconds
    M - Minutes
    H - Hours
    D - Days
    midnight - roll over at midnight
    W{0-6} - roll over on a certain day;  0 - Monday


interval: 滚动周期,单位由when指定,比如:when='D',interval=1,表示每天产生一个日志件;


backupCount:  
    表示日志文件的保留个数;
    默认的0是不会自动删除掉日志。
    若设为5,则在文件的创建过程中库会判断是否有超过这个5,若超过,则会从最先创建的开始删除。
两个比较重要的 成员变量 suffix 和 extMatch
suffix是指日志文件名的后缀,suffix中通常带有  格式化的时间字符串
filename和suffix由 “.” 连接构成文件名,比如
    filename="test", suffix="%Y-%m-%d.log",生成的文件名为test.2020-08-01.log。
extMatch是一个编译好的正则表达式,用于匹配日志文件名的后缀,它必须和suffix是匹配的,
如果suffix和extMatch匹配不上的话,过期的日志是不会被删除的。
suffix=“%Y-%m-%d.log”, extMatch的只能是re.compile(r"^\d{4}-\d{2}-\d{2}.log$")。

默认情况下,在TimedRotatingFileHandler对象初始化时,suffxi和extMatch会根据when的值进行初始化:

如果对日志文件名没有特殊要求的话,可以不用设置suffix和extMatch,如果需要,一定要让它们匹配上。

S:            suffix="%Y-%m-%d_%H-%M-%S",extMatch=r"\^d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2}";
M:            suffix="%Y-%m-%d_%H-%M",extMatch=r"^\d{4}-\d{2}-\d{2}_\d{2}-\d{2}";
H:            suffix="%Y-%m-%d_%H",extMatch=r"^\d{4}-\d{2}-\d{2}_\d{2}";
D:            suffxi="%Y-%m-%d",extMatch=r"^\d{4}-\d{2}-\d{2}";
MIDNIGHT:     "%Y-%m-%d",extMatch=r"^\d{4}-\d{2}-\d{2}";
W:            "%Y-%m-%d",extMatch=r"^\d{4}-\d{2}-\d{2}";
整体代码:
import logging
from logging import handlers

#创建一个logger日志对象
logger = logging.getLogger('test_logger')
logger.setLevel(logging.DEBUG)    #设置默认的日志级别

#创建日志格式对象
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')

#创建TimedRotatingFileHandler对象
rh = handlers.TimedRotatingFileHandler("test.log", when='S', interval=1, backupCount=5)
#TimedRotatingFileHandler对象自定义日志级别
rh.setLevel(logging.DEBUG)
#TimedRotatingFileHandler对象自定义日志级别
rh.suffix = "%Y_%m_%d_%H_%M_%S.log"
#TimedRotatingFileHandler对象自定义日志格式
rh.setFormatter(formatter)

logger.addHandler(rh)    #logger日志对象加载TimedRotatingFileHandler对象
#日志输出
logger.info('newdream')

代码执行后,控制台不会输出日志信息,而会在当前文件夹新建一个 test_年_月_日_时_分_秒.log 日志文件进行日志记录。

日志格式器:

对日志进行格式化,因为常规的日志输出并不直观美观,通过美化日志的输出格式,可以让我们阅读起来更加舒服。

format常用格式如下: %(name)s: 打印收集器名称 %(levelno)s: 打印日志级别的数值 %(levelname)s: 打印日志级别名称 %(pathname)s: 打印当前执行程序的路径,其实就是sys.argv[0] %(filename)s: 打印当前执行程序名 %(funcName)s: 打印日志的当前函数 %(lineno)d: 打印日志的当前行号 %(asctime)s: 打印日志的时间 %(thread)d: 打印线程ID %(threadName)s: 打印线程名称 %(process)d: 打印进程ID %(message)s: 打印日志信息

import logging# 这是logger1my_logger = logging.getLogger('A.C,B')my_logger.setLevel('INFO')# 创建一个处理器sh = logging.StreamHandler()sh.setLevel("ERROR")my_logger.addHandler(sh) # 将处理器绑定到日志收集器上# 设置一个格式,并设置到处理器上formatter = logging.Formatter( '%(asctime)s - [%(filename)s --> line: %(lineno)d] - %(levelname)s: %(message)s ')sh.setFormatter(formatter)my_logger.debug('这是logger1-DEBUG等级的信息')my_logger.info('这是logger1-INFO等级的信息')my_logger.warning('这是logger1-WARNING等级的信息')my_logger.error('这是logger1-ERROR等级的信息')my_logger.critical('这是logger1-CRITICAL等级的信息')

2023-01-18 16:23:29,555 - [test_logs3.py --> line: 20] - ERROR: 这是logger1-ERROR等级的信息 2023-01-18 16:23:29,555 - [test_logs3.py --> line: 21] - CRITICAL: 这是logger1-CRITICAL等级的信息

日志过滤器:logging.Filter(name='A')

Filters可以实现比level更复杂的过滤功能,限制只有满足过滤规则的日志才会被输出。比如我们定义了

filter = logging.Filter('A.B'),并将这个Filter添加到了一个Handler上,则使用该Handler的Logger中只有名字带A.B前缀的Logger才能输出其日志。下面是一个简单实例:

filter方法用于具体控制传递的record记录是否能通过过滤,如果该方法返回值为0表示不能通过过滤,非0表示可以通过过滤。

import logging# 这是logger1my_logger = logging.getLogger('A.C,B')my_logger.setLevel('INFO')# 这是logger2my_logger2 = logging.getLogger('A.B')my_logger2.setLevel('INFO')# 创建一个处理器,两个logger都使用这个处理器sh = logging.StreamHandler()sh.setLevel("ERROR")my_logger.addHandler(sh)my_logger2.addHandler(sh)# 创建一个过滤器绑到处理器上my_filter = logging.Filter(name='A') # 只有日志有 A. 的才会被收集sh.addFilter(my_filter) # 把过滤器添加到处理器上# sh2.removeFilter(my_filter) # 移除过滤器my_logger.debug('这是logger1-DEBUG等级的信息')my_logger.info('这是logger1-INFO等级的信息')my_logger.warning('这是logger1-WARNING等级的信息')my_logger.error('这是logger1-ERROR等级的信息')my_logger.critical('这是logger1-CRITICAL等级的信息')

这是logger1-ERROR等级的信息这是logger1-CRITICAL等级的信息这是logger2-ERROR等级的信息这是logger2-CRITICAL等级的信息

日志过滤器:Filters

Filters可以实现比level更复杂的过滤功能,限制只有满足过滤规则的日志才会被输出。

比如我们定义了filter = logging.Filter('A.B'),并将这个Filter添加到了一个Handler上,则使用该Handler的Logger中只有名字带A.B前缀的Logger才能输出其日志。下面是一个简单实例:

filter方法用于具体控制传递的record记录是否能通过过滤,如果该方法返回值为0表示不能通过过滤,非0表示可以通过过滤。

import logging
# 这是logger1
my_logger = logging.getLogger('A.C,B')
my_logger.setLevel('INFO')

# 这是logger2
my_logger2 = logging.getLogger('A.B')
my_logger2.setLevel('INFO')

# 创建一个处理器,两个logger都使用这个处理器
sh = logging.StreamHandler()
sh.setLevel("ERROR")
my_logger.addHandler(sh)
my_logger2.addHandler(sh)

# 创建一个过滤器绑到处理器上
my_filter = logging.Filter(name='A')  # 只有日志有  A. 的才会被收集
sh.addFilter(my_filter)    # 把过滤器添加到处理器上
# sh2.removeFilter(my_filter)   # 移除过滤器

my_logger.debug('这是logger1-DEBUG等级的信息')
my_logger.info('这是logger1-INFO等级的信息')
my_logger.warning('这是logger1-WARNING等级的信息')
my_logger.error('这是logger1-ERROR等级的信息')
my_logger.critical('这是logger1-CRITICAL等级的信息')

日志格式器:Formatters💗

对日志进行格式化,因为常规的日志输出并不直观美观,通过美化日志的输出格式,可以让我们阅读起来更加舒服。

format常用格式如下:
    %(name)s:          打印收集器名称
    %(levelno)s:       打印日志级别的数值
    %(levelname)s:     打印日志级别名称
    %(pathname)s:      打印当前执行程序的路径,其实就是sys.argv[0]
    %(filename)s:      打印当前执行程序名
    %(funcName)s:      打印日志的当前函数
    %(lineno)d:        打印日志的当前行号
    %(asctime)s:       打印日志的时间
    %(thread)d:        打印线程ID
    %(threadName)s:    打印线程名称
    %(process)d:       打印进程ID
    %(message)s:       打印日志信息
import logging
# 这是logger1
my_logger = logging.getLogger('A.C,B')
my_logger.setLevel('INFO')

# 创建一个处理器
sh = logging.StreamHandler()
sh.setLevel("ERROR")
my_logger.addHandler(sh)  # 将处理器绑定到日志收集器上

# 设置一个格式,并设置到处理器上
formatter = logging.Formatter(
        '%(asctime)s - [%(filename)s --> line: %(lineno)d] - %(levelname)s: %(message)s '
)
sh.setFormatter(formatter)

my_logger.debug('这是logger1-DEBUG等级的信息')
my_logger.info('这是logger1-INFO等级的信息')
my_logger.warning('这是logger1-WARNING等级的信息')
my_logger.error('这是logger1-ERROR等级的信息')
my_logger.critical('这是logger1-CRITICAL等级的信息')

2023-01-18 16:23:29,555 - [test_logs3.py --> line: 20] - ERROR: 这是logger1-ERROR等级的信息 
2023-01-18 16:23:29,555 - [test_logs3.py --> line: 21] - CRITICAL: 这是logger1-CRITICAL等级的信息 

代码整合日志装饰器💗:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
日志装饰器,控制程序日志输入,默认为 True
如设置 False,则程序不会打印日志
"""
import ast
from functools import wraps
from utils.logging_tool.log_control import INFO, ERROR


def log_decorator(switch: bool):  # 参数开关
    """
    封装日志装饰器, 打印请求信息
    :param switch: 定义日志开关
    :return:
    """
    def decorator(func):
        @wraps(func)
        def swapper(*args, **kwargs):
            # 判断日志为开启状态,才打印日志
            res = func(*args, **kwargs)
            # 判断日志开关为开启状态
            if switch:  
                # 这里面封装的对应的响应信息
                _log_msg = f"\n======================================================\n" \
                               f"用例标题: {res.detail}\n" \
                               f"请求路径: {res.url}\n" \
                               f"请求方式: {res.method}\n" \
                               f"请求头:   {res.headers}\n" \
                               f"请求内容: {res.request_body}\n" \
                               f"接口响应内容: {res.response_data}\n" \
                               f"接口响应时长: {res.res_time} ms\n" \
                               f"Http状态码: {res.status_code}\n" \
                               "====================================================="
                
                # 判断正常打印的日志,控制台输出绿色
                if  res.status_code == 200:
                    INFO.logger.info(_log_msg)
                else:
                    # 失败的用例,控制台打印红色
                    ERROR.logger.error(_log_msg)
            return res
        return swapper
    return decorator

代码整合日志输出类💗:

common.setting.py下的文件路径读取

import os
from typing import Text

def root_path():
    """ 获取 根路径 """
    path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    print(path)
    return path

def ensure_path_sep(path: Text) -> Text:
    """兼容 windows 和 linux 不同环境的操作系统路径 """
    if "/" in path:
        path = os.sep.join(path.split("/"))

    if "\\" in path:
        path = os.sep.join(path.split("\\"))

    print(root_path() + path)
    return root_path() + path

logging文件类

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
日志封装,可设置不同等级的日志颜色
"""
import logging
from logging import handlers
from typing import Text
import colorlog
import time
from common.setting import ensure_path_sep


class LogHandler:
    """ 日志打印封装"""
    # 日志级别关系映射
    level_relations = {
        'debug': logging.DEBUG,
        'info': logging.INFO,
        'warning': logging.WARNING,
        'error': logging.ERROR,
        'critical': logging.CRITICAL
    }

    def __init__(
            self,
            filename: Text,
            level: Text = "info",
            when: Text = "D",
            fmt: Text = "%(levelname)-8s%(asctime)s%(name)s:%(filename)s:%(lineno)d %(message)s"
    ):
        self.logger = logging.getLogger(filename)

        formatter = self.log_color()

        # 设置日志格式
        format_str = logging.Formatter(fmt)
        # 设置日志级别
        self.logger.setLevel(self.level_relations.get(level))
        # 往屏幕上输出
        screen_output = logging.StreamHandler()
        # 设置屏幕上显示的格式
        screen_output.setFormatter(formatter)
        # 往文件里写入#指定间隔时间自动生成文件的处理器
        time_rotating = handlers.TimedRotatingFileHandler(
            filename=filename,
            when=when,
            backupCount=3,
            encoding='utf-8'
        )
        # 设置文件里写入的格式
        time_rotating.setFormatter(format_str)
        # 把对象加到logger里
        self.logger.addHandler(screen_output)
        self.logger.addHandler(time_rotating)
        self.log_path = ensure_path_sep('\\logs\\log.log')

    @classmethod
    def log_color(cls):
        """ 设置日志颜色 """
        log_colors_config = {
            'DEBUG': 'cyan',
            'INFO': 'green',
            'WARNING': 'yellow',
            'ERROR': 'red',
            'CRITICAL': 'red',
        }

        formatter = colorlog.ColoredFormatter(
            '%(log_color)s[%(asctime)s] [%(name)s] [%(levelname)s]: %(message)s',
            log_colors=log_colors_config
        )
        return formatter


now_time_day = time.strftime("%Y-%m-%d", time.localtime())
INFO = LogHandler(ensure_path_sep(f"\\logs\\info-{now_time_day}.log"), level='info')
ERROR = LogHandler(ensure_path_sep(f"\\logs\\error-{now_time_day}.log"), level='error')
WARNING = LogHandler(ensure_path_sep(f'\\logs\\warning-{now_time_day}.log'))

if __name__ == '__main__':
    ERROR.logger.error("测试")

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值