【Python】Logging 模块 _ 打印及保存日志

1、Logging 

  用来做简单的日志。等级分为 debug()、 info()、 warning()、 error() 和 critical()

等级使用场景
DEBUG 调试 ,打印详细信息
INFO 一般信息,打印关键信息,证明程序按预定轨迹执行。
WARNING 警告信息,未预料到的 及可能出现问题和错误的提示信息,但是软件还是会照常运行    例如:磁盘空间不足。
ERROR 程序出现错误,可能会波及一些功能的使用
CRITICAL 严重错误,软件不能正常执行

 

 

 

 

 

 

 

 

  logging组件:

     Logger 记录器:     记录运行日志,并按配置日志等级过滤日志信息。

      常用的方法分为两类:配置和发送消息。

        Logger.setLevel()
            指定logger将会处理的日志等级信息。
        Logger.addHandler()和Logger.removeHandler()
            从记录器对象中添加和删除处理程序对象。
        Logger.addFilter()和Logger.removeFilter()
            从记录器对象添加和删除过滤器对象。
常用的方法

     Handler 处理器:     将日志存放到控制台或写入文件。

            logging.StreamHandler    
                控制台输出
            logging.FileHandler        
                文件输出
            logging.handlers.RotatingFileHandler
                按照大小自动分割日志文件,一旦达到指定的大小重新生成文件 
            logging.handlers.TimedRotatingFileHandler
                按照时间自动分割日志文件 
                
            配置方法:

            setLevel()方法
                指明了将会分发日志的最低级别。为什么会有两个setLevel()方法?记录器的级别决定了消息是否要传递给处理器。
                每个处理器的级别决定了消息是否要分发。
            setFormatter()
                为该处理器选择一个格式化器。
            addFilter()和removeFilter()
                分别配置和取消配置处理程序上的过滤器对象。
常用处理及配置方法

     Filter 过滤器:       过滤输出的日志信息。

                addFilter(filter),removeFilter(filter)和filter(record)方法

                常用的属性
                    name 就是初始化logger对象时传入的名字

                    level 是级别

                    pathname 是哪个文件输出的这行日志

                    lineno 是行号

                    msg 是日志本身
常用属性

     Formatter 格式化器:   格式化日志输出格式。    

            默认的时间格式为%Y-%m-%d %H:%M:%S

            常用信息
                %(name)s        Logger的名字
                
                %(levelno)s        数字形式的日志级别

                %(levelname)s    文本形式的日志级别

                %(pathname)s    调用日志输出函数的模块的完整路径名,可能没有

                %(filename)s    调用日志输出函数的模块的文件名

                %(module)s        调用日志输出函数的模块名

                %(funcName)s    调用日志输出函数的函数名

                %(lineno)d        调用日志输出函数的语句所在的代码行

                %(created)f        当前时间,用UNIX标准的表示时间的浮 点数表示

                %(relativeCreated)d        输出日志信息时的,自Logger创建以 来的毫秒数

                %(asctime)s        字符串形式的当前时间。默认格式是 “2003-07-08 16:49:45,896”。逗号后面的是毫秒

                %(thread)d        线程ID。可能没有

                %(threadName)s        线程名。可能没有

                %(process)d        进程ID。可能没有

                %(message)s        用户输出的消息
默认的时间格式为%Y-%m-%d %H:%M:%S

  

一、引用包

import logging
from logging import handles

  

二、配置日志输出格式 

 

logging.basicConfig函数各参数(红色为常用信息)

filename: 指定日志文件名,将文件写入 
filemode: 和file函数意义相同,指定日志文件的打开模式,'w' 覆盖写入 'a' 追加写入 ,默认写入模式为a
format: 指定输出的格式和内容,format可以输出很多有用信息,如上例所示:
    %(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:    打印日志信息
datefmt: 指定时间格式,同time.strftime()
   英文:    %A 周, %B 月
   英文缩写:  %a 周, %b 月, %c 日期+时间缩写 (输出格式:Wed Jan 12 10:00:06 2022)
   时间:    %d 日, %w 周, %m 月, %Y 年, %H:%M:%S 时分秒, %T 时间 (10:09:32), %D 日期 (12/01/22), %F 日期(2022-01-12)
level: 设置日志级别,默认为logging.WARNING 
stream: 指定将日志的输出流,可以指定输出到sys.stderr,sys.stdout或者文件,默认输出到sys.stderr,当stream和filename同时指定时,stream被忽略

 

设置日志格式

# 设置打印格式
logging.basicConfig(format='%(asctime)s - %(name)s[line:%(lineno)d] - %(levelname)s: %(message)s', # 日志格式
                    datefmt='%F %T ', # 日期格式
                    level=logging.INFO) # 日志等级info

运行结果:  

 

 

 

三、打印日志(单一打印日志)

1、仅打印到控制台

# coding:utf-8
import logging

# 打印到控制台

logging.debug("打印到控制台")
logging.info("打印到控制台")
logging.warning("打印到控制台")
logging.error("打印到控制台")
logging.critical("打印到控制台")

 

  logging 默认设置的等级为warning

  运行结果:  

 

2、仅将日志写入文件

# 设置打印格式
logging.basicConfig(format='%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s', # 日志格式
                    datefmt='%F %T ', # 日期格式
                    level=logging.INFO,# 日志等级
                    filename="info.log",    # 日志写入文件info.log
                    filemode="a")      #a 追加写入, w 覆盖写入

   运行结果:  

 

  info.log内容:  

 

 

 

 

四、输出控制台并写入日志文件

 

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import os, datetime, logging
from logging import handlers

# >>> 定义Logger类
class Logger(object):
    # 定义类属性
    level_relations = {'debug': logging.DEBUG, 'info': logging.INFO, 'warning': logging.WARNING, 'error': logging.ERROR, 'crit': logging.CRITICAL}#日志级别关系映射

    # 使用函数exists()对文件存在与否进行判断,存在为True,不存在为False.
    if os.path.exists('logs') == True:  # 文件存在
        filepath = os.getcwd() + '\logs\log_' + datetime.datetime.now().strftime('%Y%m%d') + '.log'
    else:
        os.makedirs(".\logs")  # 创建文件夹
        filepath = os.getcwd() + '\logs\log_' + datetime.datetime.now().strftime('%Y%m%d') + '.log'

    # 定义构造函数  when=D 天数,新生成的文件名上会带上时间 backCount 保留文件生成个数
    def __init__(self, filename=filepath, level='info', when='D', backCount=3, fmt='%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s'):
     #when:描述滚动周期的基本单位;“S”: Seconds ,“M”: Minutes ,“H”: Hours ,“D”: Days ,“W”: Week day (0=Monday) ,“midnight”: Roll over at midnight
        self.logger = logging.getLogger(filename)   # 指定日志文件名
        format_str = logging.Formatter(fmt) # 设置日志格式
        self.logger.setLevel(self.level_relations.get(level))   # 设置日志级别
        sh = logging.StreamHandler()    # 往屏幕上输出
        sh.setFormatter(format_str)     # 设置屏幕上显示的格式
        th = handlers.TimedRotatingFileHandler(filename=filename, when=when, backupCount=backCount, encoding='utf-8')  # 写入文件
        th.setFormatter(format_str) # 设置文件里写入的格式
        self.logger.addHandler(sh)  # 把对象加到logger里 屏幕输出
        self.logger.addHandler(th)  # 把对象加到logger里 文件写入

    def getLog(self):
        return self.logger

#创建log对象,隐式调用了我们手动创建的 __init__() 构造方法,并设置日志等级
# logs = Logger().logger  # 方法一
# logs = Logger().getLog()  # 方法二
# 方法三:重置日志等级
logs = Logger(level="debug").getLog()


# 该部分只有文件作为脚本时才会被执行,而 import 到其他脚本中是不会被执行的
if __name__ == '__main__':
    logs.debug('debug 信息')
    logs.info('info 信息')
    logs.warning('警告 信息')
    logs.error('报错 信息')
    logs.critical('严重 信息')

 

 

运行结果

  控制台:    

 

 

   .log文件:   

 

自动分割日志文件

import logging
from logging import handlers

class Logger(object):
    level_relations = {'debug': logging.DEBUG,  'info': logging.INFO, 'warning': logging.WARNING,  'error': logging.ERROR, 'crit': logging.CRITICAL}#日志级别关系映射

    def __init__(self, filename, level='info', when='D', backCount=3, fmt='%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s'):
        self.logger = logging.getLogger(filename) # 创建一个logger
        format_str = logging.Formatter(fmt)#设置日志格式
        self.logger.setLevel(self.level_relations.get(level))#设置日志级别
        sh = logging.StreamHandler()#往屏幕上输出
        sh.setFormatter(format_str) #设置屏幕上显示的格式
        th = handlers.TimedRotatingFileHandler(filename=filename, when=when, backupCount=backCount, encoding='utf-8') #往文件里写入#指定间隔时间自动生成文件的处理器
        th.setFormatter(format_str)#设置文件里写入的格式
        self.logger.addHandler(sh) #把对象加到logger里
        self.logger.addHandler(th)#

if __name__ == '__main__':
    log = Logger('./logs/debug.log', level='debug')
    log.logger.debug('debug')
    log.logger.info('info')
    log.logger.warning('警告')
    log.logger.error('报错')
    log.logger.critical('严重')
    Logger('./logs/error.log', level='error').logger.error('error')
info error  

运行结果:

 

 

 

 

 

 

 

2、loguru

# 安装
pip install loguru
# 引用
from loguru import logger as logg
# 打印日志
logg.info("debug")

 

# 写入文件
# logg.add("logs_20220414.log")
# 将日志保存在当前路径上一级目录的logs目录下,命名为 log_name 
log_name = "log_{}.log".format(datetime.datetime.now().strftime('%Y%m%d'))
logs.add(sink=os.path.abspath("..") + "\\logs\\{}".format(log_name), level=log_level.upper()) 

# 设置生成日志文件,utf-8编码,每天0点切割,zip压缩,保留3天,异步写入 
# logs.add(sink='test.log', level="INFO", rotation="00:00", retention="3 days", compression="zip", encoding="utf-8", enqueue=True)

 

 

 

 

from loguru import logger as logg
logg.add("logs.log")

def case():
    logg.debug("debug")
    logg.warning("warning")


case()
源码

 

执行结果:

 

 

 

# 仅输出到文件 不打印到控制台

# 删除以前添加的处理程序并停止向其接收器发送日志。
logs.remove(handler_id=None)     # 移除控制台输出

 

 

# 集成loguru到控制台(即html报告)
class PropogateHandler(logging.Handler):
    def emit(self, record):
        logging.getLogger(record.name).handle(record)
logs.add(PropogateHandler(), format="{time:YYYY-MM-DD at HH:mm:ss} | {message}")

 

执行结果:

 

posted @ 2022-01-12 11:37  Phoenixy  阅读(1417)  评论(0编辑  收藏  举报