日志

  • 跟踪软件运行时所发生的事件的一种方法,软件开发者在代码中调用日志函数,记录发生的特定的事件;
  • 日志函数:logging.debug()、 logging.info()、 logging.warning()、logging. error() 和 logging.critical();
  • 日志函数的事件级别或严重程度如下表:(数值越大,严重程度越高)
  • 日志输出的默认等级是WARNING,即WARNING,ERRORCRITICAL等级的信息,默认会显示;
  • 日志可以打印在控制台,也可以写入磁盘文件

1、日志的等级

  • 定义的记录级别越低,信息越多;级别越高,信息越少。
级别数字值何时使用
NOTSET0一般不使用
DEBUG10详细信息,一般只在调试问题时使用
INFO20证明事情按预期工作。
WARNING30默认等级,某些没有预料到的事件的提示,或者在将来可能会出现的问题提示。例如:磁盘空间不足。但是软件还是会照常运行。
ERROR40由于更严重的问题,软件已不能执行一些功能了。
CRITICAL50严重错误,表明软件已不能继续运行了。

2、不同等级日志的输出

  • 日志输出的默认等级是WARNING,即WARNING,ERROR,CRITICAL等级的信息,默认会显示;
  • logging.debug('debug级别,最低级别,一般开发人员用来打印一些调试信息')
  • logging.info('info级别,正常输出信息,一般用来打印一些正常的操作,如开发自定义提示信息')
  • logging.warning('waring级别,一般用来打印告警信息')
  • logging.error('error级别,一般用来打印一些错误信息')
  • logging.critical('critical 级别,一般用来打印一些致命的错误信息,等级最高')

3、打印日志到控制台

# 打印日志
import logging

# 打印到控制台
logging.debug('debug级别,最低级别,打印调试信息')
logging.info('info级别,正常输出信息,打印正常完成的操作信息')
logging.warning('waring级别,打印告警信息')
logging.error('error级别,打印报错信息')
logging.critical('critical 级别,等级最高,打印致命的错误信息')

输出结果:WARNING,ERROR,CRITICAL等级的信息,在默认级别为WARNING时,会打印显示

3、日志输出格式和级别 logging.basicConfig(format, level)

使用basicConfig方法配置logging记录格式:

格式描述
filename指定使用指定的文件名,而不是StreamHandler创建FileHandler。
filemode指定打开文件的模式,如果指定了filename(如果文件模式未指定,则默认为'a')。
format为处理程序使用指定的格式字符串。
datefmt使用指定的日期/时间格式。
level将根记录器级别设置为指定的级别。
handlers如果指定,这应该是一个已经创建的处理程序的迭代器添加到根记录器。任何尚未设置格式化程序的处理程序都将被分配在此函数中创建的默认格式化程序。

二、

2、控制日志输出格式和级别  logging.basicConfig(format, level)

logging.basicConfig(format, level)函数的入参:

  • format,日志输出格式;
  • level,日志输出级别:
    • logging.DEBUG 输出logging.debug()、 logging.info()、 logging.warning()、logging. error() 、logging.critical()中的信息;  -- 全部等级都输出
    • logging.INFO  输出logging.info()、 logging.warning()、logging. error() 、logging.critical()中的信息,不输出logging.debug()中的信息;  -- 除DEBUG级别都输出
    • logging.WARNING  输出logging.warning()、logging. error() 、logging.critical()中的信息,不输出logging.debug()、 logging.info()中的信息;  -- 默认等级
    • logging.ERROR  仅输出logging. error() 、logging.critical()中的信息;  -- 只输出ERROR,CRITICAL级别
    • logging.CRITICAL  仅输出logging.critical()中的信息;  -- 只输出CRITICAL级别
logging.basicConfig(format='%(asctime)s - %(pathname)s[line:%(lineno)d] - %(levelname)s: %(message)s',
                    level=logging.DEBUG)

日志记录格式化字符串format:

属性名格式描述
asctime%(asctime)s易读的时间格式: 默认情况下是'2020-11-18 12:50:10,393'的形式(逗号之后的数字是毫秒部分的时间)
filename%(filename)s路径名的文件名部分。
pathname%(pathname)s输出运行的文件路径,如:D:/Nikey/Python/PyCharm/basicProgram/爬虫/日志/tmp.py
funcName%(funcName)s日志调用所在的函数名
levelname%(levelname)s消息的级别名称('DEBUG''INFO''WARNING''ERROR''CRITICAL').
levelno%(levelno)s对应数字格式的日志级别 (DEBUGINFOWARNINGERROR,CRITICAL).,输出正在执行的日志打印的日志级别
lineno%(lineno)d发出日志记录调用的源码行号 (如果可用)。[line:%(lineno)d]  输出运行的代码行号,如:[line:16]
module%(module)s所在的模块名(如test6.py模块则记录test6)
message%(message)s记录的信息,输出正在执行的日志打印的日志信息,如:debug级别,最低级别,打印调试信息
name%(name)s调用的logger记录器的名称
process%(process)d进程ID
processName%(processName)s进程名
thread%(thread)d线程ID
threadName%(threadName)s线程名

格式描述
filename指定使用指定的文件名而不是StreamHandler创建FileHandler。
filemode指定打开文件的模式,如果指定了filename(如果文件模式未指定,则默认为'a')。
format为处理程序使用指定的格式字符串。
datefmt使用指定的日期/时间格式。
level将根记录器级别设置为指定的级别。
handlers如果指定,这应该是一个已经创建的处理程序的迭代器添加到根记录器。任何尚未设置格式化程序的处理程序都将被分配在此函数中创建的默认格式化程序。

1)控台打印logging.DEBUG级别的日志

# 打印日志
import logging

# 控制日志输出格式和级别
logging.basicConfig(format='%(asctime)s - %(pathname)s - [line:%(lineno)d] - %(levelname)s: %(message)s',
                    level=logging.DEBUG)

# 打印到控制台
logging.debug('debug级别,最低级别,打印调试信息')
logging.info('info级别,正常输出信息,打印正常完成的操作信息')
logging.warning('waring级别,打印告警信息')
logging.error('error级别,打印报错信息')
logging.critical('critical 级别,等级最高,打印致命的错误信息')

输出结果:都是红色字体显示

2020-11-18 13:01:25,381 - D:/Nikey/Python/PyCharm/basicProgram/爬虫/日志/tmp.py - [line:9] - DEBUG: debug级别,最低级别,打印调试信息
2020-11-18 13:01:25,382 - D:/Nikey/Python/PyCharm/basicProgram/爬虫/日志/tmp.py - [line:10] - INFO: info级别,正常输出信息,打印正常完成的操作信息
2020-11-18 13:01:25,382 - D:/Nikey/Python/PyCharm/basicProgram/爬虫/日志/tmp.py - [line:11] - WARNING: waring级别,打印告警信息
2020-11-18 13:01:25,382 - D:/Nikey/Python/PyCharm/basicProgram/爬虫/日志/tmp.py - [line:12] - ERROR: error级别,打印报错信息
2020-11-18 13:01:25,382 - D:/Nikey/Python/PyCharm/basicProgram/爬虫/日志/tmp.py - [line:13] - CRITICAL: critical 级别,等级最高,打印致命的错误信息

2)控台打印logging.INFO级别的日志

# 打印日志
import logging

# 控制日志输出格式和级别
logging.basicConfig(format='%(asctime)s - [line:%(lineno)d] - %(levelname)s: %(message)s',
                    level=logging.INFO)

# 打印到控制台
logging.debug('debug级别,最低级别,打印调试信息')
logging.info('info级别,正常输出信息,打印正常完成的操作信息')
logging.warning('waring级别,打印告警信息')
logging.error('error级别,打印报错信息')
logging.critical('critical 级别,等级最高,打印致命的错误信息')

输出结果:

3)控台打印logging.WARNING级别的日志

# 打印日志
import logging

# 控制日志输出格式和级别
logging.basicConfig(format='%(asctime)s - [line:%(lineno)d] - %(levelname)s: %(message)s',
                    level=logging.WARNING)

# 打印到控制台
logging.debug('debug级别,最低级别,打印调试信息')
logging.info('info级别,正常输出信息,打印正常完成的操作信息')
logging.warning('waring级别,打印告警信息')
logging.error('error级别,打印报错信息')
logging.critical('critical 级别,等级最高,打印致命的错误信息')

输出结果:

4)控台打印logging.ERROR级别的日志

# 打印日志
import logging

# 控制日志输出格式和级别
logging.basicConfig(format='%(asctime)s - [line:%(lineno)d] - %(levelname)s: %(message)s',
                    level=logging.ERROR)

# 打印到控制台
logging.debug('debug级别,最低级别,打印调试信息')
logging.info('info级别,正常输出信息,打印正常完成的操作信息')
logging.warning('waring级别,打印告警信息')
logging.error('error级别,打印报错信息')
logging.critical('critical 级别,等级最高,打印致命的错误信息')

输出结果:

5)控台打印logging.CRITICAL级别的日志

# 打印日志
import logging

# 控制日志输出格式和级别
logging.basicConfig(format='%(asctime)s - [line:%(lineno)d] - %(levelname)s: %(message)s',
                    level=logging.CRITICAL)

# 打印到控制台
logging.debug('debug级别,最低级别,打印调试信息')
logging.info('info级别,正常输出信息,打印正常完成的操作信息')
logging.warning('waring级别,打印告警信息')
logging.error('error级别,打印报错信息')
logging.critical('critical 级别,等级最高,打印致命的错误信息')

输出结果:

二、保存日志到log文件 -- logging.basicConfig(level, filename, filemode, format)

  • logging.basicConfig()设置filename filemode入参后,日志只会保存log到文件,不会输出到控制台
  • filename 日志保存的位置
  • filemode 日志文件输出模式:
    • filemode='a'  追加模式,不写时,默认是追加模式;
    • filemode='w' 写模式,每次都会重新写日志,覆盖之前的日志
# 打印日志
import logging

logging.basicConfig(level=logging.DEBUG, #日志级别
                    filename='new.log', #保存的日志文件
                    filemode='a',# a是追加模式
                    format= #日志格式
                    '%(asctime)s - %(pathname)s[line:%(lineno)d] - %(levelname)s: %(message)s'
                    )

# 打印到日志文件,控台不显示
logging.debug('debug调试')
logging.info('info正常输出')
logging.warning('waring告警信息')
logging.error('error报错信息')
logging.critical('critical致命错误')

输出结果:日志信息,只显示在new.log文件中,控台不显示日志信息;

日志文件输出:new.log

控台输出:无输出

三、日志信息,打印到控台,同时也保存到log日志文件

logging库采取了模块化的设计,提供了许多组件:记录器、处理器、过滤器和格式化器。

  • Logger 暴露了应用程序代码能直接使用的接口。
  • Handler将(记录器产生的)日志记录发送至合适的目的地。
  • Filter提供了更好的粒度控制,它可以决定输出哪些日志记录。
  • Formatter 指明了最终输出中日志记录的布局。

Loggers:

Logger 对象要做三件事情。首先,它们向应用代码暴露了许多方法,这样应用可以在运行时记录消息。其次,记录器对象通过严重程度(默认的过滤设施)或者过滤器对象来决定哪些日志消息需要记录下来。第三,记录器对象将相关的日志消息传递给所有感兴趣的日志处理器。

常用的记录器对象的方法分为两类:配置和发送消息。

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

Logger.setLevel()指定logger将会处理的最低的安全等级日志信息, debug是最低的内置安全等级,critical是最高的内建安全等级。例如,如果严重程度为INFO,记录器将只处理INFO,WARNING,ERROR和CRITICAL消息,DEBUG消息被忽略。
Logger.addHandler()和Logger.removeHandler()从记录器对象中添加和删除处理程序对象。处理器详见Handlers。
Logger.addFilter()和Logger.removeFilter()从记录器对象添加和删除过滤器对象。

Handlers

处理程序对象负责将适当的日志消息(基于日志消息的严重性)分派到处理程序的指定目标。Logger 对象可以通过addHandler()方法增加零个或多个handler对象。举个例子,一个应用可以将所有的日志消息发送至日志文件,所有的错误级别(error)及以上的日志消息发送至标准输出,所有的严重级别(critical)日志消息发送至某个电子邮箱。在这个例子中需要三个独立的处理器,每一个负责将特定级别的消息发送至特定的位置。

常用的有4种: 

1)    logging.StreamHandler -> 控制台输出 


使用这个Handler可以向类似与sys.stdout或者sys.stderr的任何文件对象(file object)输出信息。

它的构造函数是:
StreamHandler([strm])
其中strm参数是一个文件对象。默认是sys.stderr


2)   logging.FileHandler  -> 文件输出


和StreamHandler类似,用于向一个文件输出日志信息。不过FileHandler会帮你打开这个文件。它的构造函数是:
FileHandler(filename[,mode])
filename是文件名,必须指定一个文件名。
mode是文件的打开方式。默认是’a',即添加到文件末尾。


3)   logging.handlers.RotatingFileHandler -> 按照大小自动分割日志文件,一旦达到指定的大小重新生成文件 


这个Handler类似于上面的FileHandler,但是它可以管理文件大小。当文件达到一定大小之后,它会自动将当前日志文件改名,然后创建 一个新的同名日志文件继续输出。比如日志文件是chat.log。当chat.log达到指定的大小之后,RotatingFileHandler自动把 文件改名为chat.log.1。不过,如果chat.log.1已经存在,会先把chat.log.1重命名为chat.log.2。。。最后重新创建 chat.log,继续输出日志信息。它的构造函数是:
RotatingFileHandler( filename[, mode[, maxBytes[, backupCount]]])
其中filename和mode两个参数和FileHandler一样。
maxBytes用于指定日志文件的最大文件大小。如果maxBytes为0,意味着日志文件可以无限大,这时上面描述的重命名过程就不会发生。
backupCount用于指定保留的备份文件的个数。比如,如果指定为2,当上面描述的重命名过程发生时,原有的chat.log.2并不会被更名,而是被删除。


4)   logging.handlers.TimedRotatingFileHandler  -> 按照时间自动分割日志文件 


这个Handler和RotatingFileHandler类似,不过,它没有通过判断文件大小来决定何时重新创建日志文件,而是间隔一定时间就 自动创建新的日志文件。重命名的过程与RotatingFileHandler类似,不过新的文件不是附加数字,而是当前时间。它的构造函数是:
TimedRotatingFileHandler( filename [,when [,interval [,backupCount]]])
其中filename参数和backupCount参数和RotatingFileHandler具有相同的意义。
interval是时间间隔。
when参数是一个字符串。表示时间间隔的单位,不区分大小写。它有以下取值:
S 秒
M 分
H 小时
D 天
W 每星期(interval==0时代表星期一)
midnight 每天凌晨
 

配置方法:

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

Formatters

Formatter对象设置日志信息最后的规则、结构和内容,默认的时间格式为%Y-%m-%d %H:%M:%S,下面是Formatter常用的一些信息

%(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

用户输出的消息

四、需求:输出日志到控制台,同时写入log文件

保存2种类型的log日志:

  • all.log,保存debug, info, warning, critical 信息;
  • error.log,只保存error信息,同时按照时间自动分割日志文件;
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 - %(pathname)s[line:%(lineno)d] - %(levelname)s: %(message)s'):
        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')  # 往文件里写入#指定间隔时间自动生成文件的处理器
        # 实例化TimedRotatingFileHandler
        # interval是时间间隔,backupCount是备份文件的个数,如果超过这个个数,就会自动删除,when是间隔的时间单位,单位有以下几种:
        # S 秒
        # M 分
        # H 小时、
        # D 天、
        # W 每星期(interval==0时代表星期一)
        # midnight 每天凌晨
        th.setFormatter(format_str)  # 设置文件里写入的格式
        self.logger.addHandler(sh)  # 把对象加到logger里
        self.logger.addHandler(th)


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

输出:

输出的all.log文件

2020-11-18 11:28:54,559 - D:/Nikey/Python/PyCharm/basicProgram/爬虫/日志/日志.py[line:38] - DEBUG: debug
2020-11-18 11:28:54,560 - D:/Nikey/Python/PyCharm/basicProgram/爬虫/日志/日志.py[line:39] - INFO: info
2020-11-18 11:28:54,560 - D:/Nikey/Python/PyCharm/basicProgram/爬虫/日志/日志.py[line:40] - WARNING: 警告
2020-11-18 11:28:54,560 - D:/Nikey/Python/PyCharm/basicProgram/爬虫/日志/日志.py[line:41] - ERROR: 报错
2020-11-18 11:28:54,560 - D:/Nikey/Python/PyCharm/basicProgram/爬虫/日志/日志.py[line:42] - CRITICAL: 严重

输出的error.log文件

2020-11-18 11:28:54,561 - D:/Nikey/Python/PyCharm/basicProgram/爬虫/日志/日志.py[line:43] - ERROR: 报错

控制台输出

由于when=D,新生成的文件名上会带上时间,如下所示。

五、案例

打印日志到控台,保存所有日志信息到文件all.log,打印错误信息到error.log

import logging

# LOG_FORMAT = "%(asctime)s - %(levelname)s - %(message)s"
LOG_FORMAT = "%(asctime)s - %(levelname)s - %(filename)s, line:%(lineno)d - %(message)s" # 带有文件名、行号
DATE_FORMAT = "%Y-%m-%d %H:%M:%S"

logger = logging.getLogger()
logger.setLevel(logging.DEBUG)  #这里设置日志的级别

# 输出到文件
file_handler = logging.FileHandler("all.log", mode='a', encoding='utf-8')

# 输出到控制台
stream_handler = logging.StreamHandler()

# 错误日志单独输出到一个文件
error_handler = logging.FileHandler('error.log', mode='a', encoding='utf-8')
error_handler.setLevel(logging.ERROR)  #注意这里,错误日志只记录ERROR级别的日志

# 将所有的处理器加入到logger中
logger.addHandler(file_handler)
logger.addHandler(stream_handler)
logger.addHandler(error_handler)

formatter = logging.Formatter(fmt=LOG_FORMAT, datefmt=DATE_FORMAT)

# 设置格式化
file_handler.setFormatter(formatter)
stream_handler.setFormatter(formatter)
error_handler.setFormatter(formatter)

logger.info('info级别的')
logger.error('error级别')
logger.debug('debug级别')
logger.warning('warning级别')

运行结果:

1、控台输出:

 2、all.log文件内容:

 

3、error.log文件内容:

 

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐