[102]python日志模块logging&loguru

logging模块

日志级别

级别何时使用
DEBUG详细信息,典型地调试问题时会感兴趣。
INFO证明事情按预期工作。
WARNING表明发生了一些意外,或者不久的将来会发生问题(如‘磁盘满了’)。软件还是在正常工作。
ERROR由于更严重的问题,软件已不能执行一些功能了。
CRITICAL严重错误,表明软件已不能继续运行了。

简单配置使用

# -*- coding:utf-8 -*-
import logging

# 通过下面的方式进行简单配置输出方式与日志级别
logging.basicConfig(filename='logger.log', level=logging.INFO)

logging.debug('debug message')
logging.info('info message')
logging.warn('warn message')
logging.error('error message')
logging.critical('critical message')

输出:

标准输出(屏幕)未显示任何信息,发现当前工作目录下生成了logger.log,内容如下:

INFO:root:info message
WARNING:root:warn message
ERROR:root:error message
CRITICAL:root:critical message

因为通过level=logging.INFO设置日志级别为INFO,所以INFO级别及以上的日志信息均输出出来了。

默认情况下,logging模块将日志打印到屏幕上(stdout),日志级别为WARNING(即只有日志级别高于WARNING的日志信息才会输出),日志格式如下图所示:
image.png

basicConfig关键字参数

关键字描述
filename创建一个FileHandler,使用指定的文件名,而不是使用StreamHandler。
filemode如果指明了文件名,指明打开文件的模式(如果没有指明filemode,默认为’a’)。
formathandler使用指明的格式化字符串。
datefmt使用指明的日期/时间格式。
level指明根logger的级别。
stream使用指明的流来初始化StreamHandler。该参数与’filename’不兼容,如果两个都有,'stream’被忽略。

有用的format格式

格式描述
%(levelno)s打印日志级别的数值
%(levelname)s打印日志级别名称
%(pathname)s打印当前执行程序的路径
%(filename)s打印当前执行程序名称
%(funcName)s打印日志的当前函数
%(lineno)d打印日志的当前行号
%(asctime)s打印日志的时间
%(thread)d打印线程id
%(threadName)s打印线程名称
%(process)d打印进程ID
%(message)s打印日志信息

简单日志打印:

filename = time.strftime('%Y-%m-%d', time.localtime(time.time()))

logging.basicConfig(level=logging.DEBUG,
                        format='%(asctime)s \tFile \"%(filename)s\"[line:%(lineno)d] %(levelname)s %(message)s',
                        # datefmt='%a, %d %b %Y %H:%M:%S',
                        filename="./log/" + filename + ".log",
                        filemode='a')

logging.debug('debug message')
logging.info('info message')
logging.warn('warn message')
logging.error('error message')
logging.critical('critical message')

logging.basicConfig()函数中可通过具体参数来更改logging模块默认行为,可用参数有

  • filename:用指定的文件名创建FiledHandler(后边会具体讲解handler的概念),这样日志会被存储在指定的文件中。
  • filemode:文件打开方式,在指定了filename时使用这个参数,默认值为“a”还可指定为“w”。
  • format:指定handler使用的日志显示格式。
  • datefmt:指定日期时间格式。
  • level:设置rootlogger; 默认的日志级别设置为WARNING(日志级别等级CRITICAL > ERROR > WARNING > INFO > DEBUG > NOTSET)
  • stream:用指定的stream创建StreamHandler。可以指定输出到sys.stderr,sys.stdout或者文件,默认为sys.stderr。若同时列出了filename和stream两个参数,则stream参数会被忽略。

format参数中可能用到的格式化串:

%(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用户输出的消息

logging库提供了两个可以用于日志滚动的class(可以参考https://docs.python.org/2/library/logging.handlers.html,一个是RotatingFileHandler,它主要是根据日志文件的大小进行滚动,另一个是TimeRotatingFileHandler,它主要是根据时间进行滚动。在实际应用中,我们通常根据时间进行滚动,因此,本文中主要介绍TimeRotaingFileHandler的使用方法(RotatingFileHandler一样)。

只输出到文件不在ternimal打印

import logging,re
from logging.handlers import TimedRotatingFileHandler
from logging.handlers import RotatingFileHandler

filename = time.strftime('%Y-%m-%d', time.localtime(time.time()))

def log_init(message):
    log_fmt = '%(asctime)s\tFile \"%(filename)s\",line %(lineno)s\t%(levelname)s: %(message)s'
    formatter = logging.Formatter(log_fmt)

    log_file_handler = TimedRotatingFileHandler(filename="./log/" + filename + ".log", when="D", interval=1, backupCount=30)
    log_file_handler.suffix = "%Y-%m-%d.log"
    log_file_handler.extMatch = re.compile(r"^\d{4}-\d{2}-\d{2}.log$")

    log_file_handler.setFormatter(formatter)
    log_file_handler.setLevel(logging.DEBUG)
    log = logging.getLogger()
    log.addHandler(log_file_handler)
    '''
    程序运行
    '''
    log.info(message)
    #removeHandler 要放在程序运用打印日志的后面
    log.removeHandler(log_file_handler)
    #  用pop方法把logger.handlers列表中的handler移除,注意如果你add了多个handler,这里需多次pop,或者可以直接为handlers列表赋空值
    # log.handlers.pop()
    # log.handler = []

log_init('1234')

结果是每天生成一个日志文件,保留最近30天的日志文件。

when:是一个字符串,用于描述滚动周期的基本单位,字符串的值及意义如下:

  • “S”: Seconds
  • “M”: Minutes
  • “H”: Hours
  • “D”: Days
  • “W”: Week day (0=Monday)
  • “midnight”: Roll over at midnight
  • interval: 滚动周期,单位有when指定,比如:when=’D’,interval=1,表示每天产生一个日志文件;
  • backupCount: 表示日志文件的保留个数;

删除日志文件设置:

log_file_handler.suffix = “%Y-%m-%d_%H-%M.log” 
log_file_handler.extMatch = re.compile(r”^\d{4}-\d{2}-\d{2}_\d{2}-\d{2}.log$”) 

suffix和extMatch的格式要相对应,Year-4位,m-2位 以此类推

既输出到文件,又打印到terminal

import logging

filename = time.strftime('%Y-%m-%d', time.localtime(time.time()))

def initLogging(message):
    logging.basicConfig(
                    level    = logging.DEBUG,
                    format   = '%(asctime)s\tFile \"%(filename)s\",line %(lineno)s\t%(levelname)s: %(message)s',
                    # datefmt  = '%a, %d %b %Y %H:%M:%S',
                    filename = "./log/" + filename + ".log",
                    filemode = 'w')
    #添加StreamHandler在控制台也打印出来
    console = logging.StreamHandler()
    console.setLevel(logging.INFO)
    formatter = logging.Formatter('LINE %(lineno)-4d : %(levelname)-8s %(message)s')
    console.setFormatter(formatter)

    logging.getLogger('').addHandler(console)
    logging.info(message)

initLogging('123')

通过logging.StreamHandler()函数来打印到terminal

新建一个文件:myLog.py

# encoding:utf-8  
import sys  
import logging  
import time  
   
def writeLog(message):
    logger = logging.getLogger()
    # streamhandler = logging.StreamHandler()
    filename = time.strftime('%Y-%m-%d', time.localtime(time.time()))

    handler = logging.FileHandler("./log/" + filename + ".log")
    logger.addHandler(handler)
    logger.setLevel(logging.INFO )
    logger.info(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')+':'+message)

    #  添加下面一句,在记录日志之后移除句柄
    logger.removeHandler(handler)
   
if __name__ == '__main__':  
    writeLog("hello")  

然后直接在其他文件引用import myLog ,对于要写入的消息 用 myLog.writeLog('insert bad ') 写入 insert bad 的消息

日常使用

# -*- coding:utf-8 -*-
import os,sys, time
import json,requests
import logging,logging.handlers

def get_logger(log_name='',date_str=None):
    local_path="./logs/"
    if not os.path.exists(local_path):
        # os.mkdir(local_path)# 只能创建一级目录
        os.makedirs(local_path)  # 可以创建多级目录
    if date_str==None:
        date_str = time.strftime('%Y-%m-%d', time.localtime(time.time()))

    logfile = local_path + date_str + log_name + ".log"
    # hander = logging.handlers.RotatingFileHandler(logfile,encoding='utf-8')
    # 日志文件切割
    hander = logging.handlers.TimedRotatingFileHandler(filename=logfile, when="D", interval=1, backupCount=60)
    hander.suffix = hander.suffix + ".log"
    # print(hander.suffix)
    formatter = logging.Formatter('%(asctime)s [%(filename)s:%(lineno)s][%(levelname)s] %(message)s')
    hander.setFormatter(formatter)
    logger = logging.getLogger(logfile)
    ## 每次被调用后,清空已经存在handler,不然日志会打印多遍
    logger.handlers.clear()
    logger.addHandler(hander)
    logger.setLevel(logging.INFO)

    return logger

logger=get_logger(log_name='_test',date_str=None)
logger.info('test')

loguru模块

loguru模块的基础使用

如果想更简洁,可用loguru库,python3安装:pip3 install loguru

loguru默认的输出格式是上面的内容,有时间、级别、模块名、行号以及日志信息,不需要手动创建 logger,直接使用即可,另外其输出还是彩色的,看起来会更加友好。

用法如下:

from loguru import logger

logger.debug('this is a debug message')
logger.info('this is another debug message')
logger.warning('this is another debug message')
logger.error('this is another debug message')
logger.info('this is another debug message')
logger.success('this is success message!')
logger.critical('this is critical message!')

执行结果如下:
image.png

不需要配置什么东西,直接引入一个 logger,然后调用其 debug 方法即可。

如果想要输出到文件,只需要:

from loguru import logger

logger.add('my_log.log')
logger.debug('this is a debug')

运行之后会发现目录下 my_log.log 出现了刚刚控制台输出的 DEBUG 信息。上面只是基础用法,更详细的在下面

logurr详细使用

比如支持输出到多个文件,分级别分别输出,过大创建新文件,过久自动删除等等。

add 方法的定义
def add(
        self,
        sink,
        *,
        level=_defaults.LOGURU_LEVEL,
        format=_defaults.LOGURU_FORMAT,
        filter=_defaults.LOGURU_FILTER,
        colorize=_defaults.LOGURU_COLORIZE,
        serialize=_defaults.LOGURU_SERIALIZE,
        backtrace=_defaults.LOGURU_BACKTRACE,
        diagnose=_defaults.LOGURU_DIAGNOSE,
        enqueue=_defaults.LOGURU_ENQUEUE,
        catch=_defaults.LOGURU_CATCH,
        **kwargs
    ):
    pass

看看它的源代码,它支持这么多的参数,如 level、format、filter、color 等等,另外我们还注意到它有个非常重要的参数 sink,我们看看官方文档,可以了解到通过 sink 我们可以传入多种不同的数据结构,汇总如下:

  • sink 可以传入一个 file 对象,例如 sys.stderr 或者 open(‘file.log’, ‘w’) 都可以。
  • sink 可以直接传入一个 str 字符串或者 pathlib.Path 对象,其实就是代表文件路径的,如果识别到是这种类型,它会自动创建对应路径的日志文件并将日志输出进去。
  • sink 可以是一个方法,可以自行定义输出实现。
  • sink 可以是一个 logging 模块的 Handler,比如 FileHandler、StreamHandler 等等。

sink 还可以是一个自定义的类,具体的实现规范可以参见官方文档https://loguru.readthedocs.io/en/stable/api/logger.html#sink。

所以说,刚才我们所演示的输出到文件,仅仅给它传了一个 str 字符串路径,他就给我们创建了一个日志文件,就是这个原理。

基本参数

下面我们再了解下它的其他参数,例如 format、filter、level 等等。

其实它们的概念和格式和 logging 模块都是基本一样的了,例如这里使用 format、filter、level 来规定输出的格式:

logger.add('runtime.log', format="{time} {level} {message}", filter="my_module", level="INFO")
删除 sink

另外添加 sink 之后我们也可以对其进行删除,相当于重新刷新并写入新的内容。

删除的时候根据刚刚 add 方法返回的 id 进行删除即可,看下面的例子:

from loguru import logger

trace = logger.add('my_log.log')
logger.debug('this is a debug message')
logger.remove(trace)
logger.debug('this is another debug message')

看这里,我们首先 add 了一个 sink,然后获取它的返回值,赋值为 trace。随后输出了一条日志,然后将 trace 变量传给 remove 方法,再次输出一条日志,看看结果是怎样的。

控制台输出如下:
image.png

日志文件 my_log.log 内容如下:
image.png

可以发现,在调用 remove 方法之后,确实将历史 log 删除了。但实际上这并不是删除,只不过是将 sink 对象移除之后,在这之前的内容不会再输出到日志中。

这样我们就可以实现日志的刷新重新写入操作。

rotation 配置

用了 loguru 我们还可以非常方便地使用 rotation 配置,比如我们想一天输出一个日志文件,或者文件太大了自动分隔日志文件,我们可以直接使用 add 方法的 rotation 参数进行配置。

我们看看下面的例子:

logger.add('runtime_{time}.log', rotation="500 MB")

通过这样的配置我们就可以实现每 500MB 存储一个文件,每个 log 文件过大就会新创建一个 log 文件。我们在配置 log 名字时加上了一个 time 占位符,这样在生成时可以自动将时间替换进去,生成一个文件名包含时间的 log 文件。

另外我们也可以使用 rotation 参数实现定时创建 log 文件,例如:

logger.add('runtime_{time}.log', rotation='00:00')

这样就可以实现每天 0 点新创建一个 log 文件输出了。

另外我们也可以配置 log 文件的循环时间,比如每隔一周创建一个 log 文件,写法如下:

logger.add('runtime_{time}.log', rotation='1 week')

这样我们就可以实现一周创建一个 log 文件了。

retention 配置

很多情况下,一些非常久远的 log 对我们来说并没有什么用处了,它白白占据了一些存储空间,不清除掉就会非常浪费。retention 这个参数可以配置日志的最长保留时间。

比如我们想要设置日志文件最长保留 10 天,可以这么来配置:

logger.add('runtime.log', retention='10 days')

这样 log 文件里面就会保留最新 10 天的 log,妈妈再也不用担心 log 沉积的问题啦。

compression 配置

loguru 还可以配置文件的压缩格式,比如使用 zip 文件格式保存,示例如下:

logger.add('runtime.log', compression='zip')

这样可以更加节省存储空间。

字符串格式化

loguru 在输出 log 的时候还提供了非常友好的字符串格式化功能,像这样:

logger.info('If you are using Python {}, prefer {feature} of course!', 3.6, feature='f-strings')

这样在添加参数就非常方便了。

Traceback 记录

在很多情况下,如果遇到运行错误,而我们在打印输出 log 的时候万一不小心没有配置好 Traceback 的输出,很有可能我们就没法追踪错误所在了。

但用了 loguru 之后,我们用它提供的装饰器就可以直接进行 Traceback 的记录,类似这样的配置即可:

@logger.catch
def my_function(x, y, z):
    # An error? It's caught anyway!
    return 1 / (x + y + z)

我们做个测试,我们在调用时三个参数都传入 0,直接引发除以 0 的错误,看看会出现什么情况:

my_function(0, 0, 0)

运行完毕之后,可以发现 log 里面就出现了 Traceback 信息,而且给我们输出了当时的变量值,真的是不能再赞了!结果如下:

> File "run.py", line 15, in <module>
    my_function(0, 0, 0)
    └ <function my_function at 0x1171dd510>

  File "/private/var/py/logurutest/demo5.py", line 13, in my_function
    return 1 / (x + y + z)
                │   │   └ 0
                │   └ 0
                └ 0

ZeroDivisionError: division by zero

因此,用 loguru 可以非常方便地实现日志追踪,debug 效率可能要高上十倍了?

另外 loguru 还有很多很多强大的功能,这里就不再一一展开讲解了,更多的内容大家可以看看 loguru 的官方文档详细了解一下:https://loguru.readthedocs.io/en/stable/index.html。

参考:https://www.jianshu.com/p/feb86c06c4f4
https://wenku.baidu.com/view/0601b8d2b90d4a7302768e9951e79b8969026845.html
https://blog.csdn.net/cui_yonghua/article/details/107498535

  • 4
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
Python logger 是一个内置的日志记录模块,它可以帮助我们在程序记录和管理日志信息。使用 Python logger,我们可以将程序的运行状态、错误信息等输出到日志文件或者控制台。 要使用 Python logger,我们首先需要导入 logging 模块。然后,我们可以创建一个 logger 对象,并设置它的日志级别、输出格式等属性。接下来,我们可以使用 logger 对象进行日志记录,包括调用不同级别的方法,如 debug、info、warning、error 等。 下面是一个简单的示例: ```python import logging # 创建一个 logger 对象 logger = logging.getLogger('my_logger') # 设置日志级别 logger.setLevel(logging.DEBUG) # 创建一个文件处理器 file_handler = logging.FileHandler('my_log.log') # 设置文件处理器的日志级别 file_handler.setLevel(logging.DEBUG) # 创建一个控制台处理器 console_handler = logging.StreamHandler() # 设置控制台处理器的日志级别 console_handler.setLevel(logging.INFO) # 创建一个日志格式器 formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') # 设置文件处理器和控制台处理器的格式化器 file_handler.setFormatter(formatter) console_handler.setFormatter(formatter) # 将处理器添加到 logger 对象 logger.addHandler(file_handler) logger.addHandler(console_handler) # 记录日志 logger.debug('This is a debug message') logger.info('This is an info message') logger.warning('This is a warning message') logger.error('This is an error message') ``` 在上面的示例,我们创建了一个为 'my_logger' 的 logger 对象,并设置了它的日志级别为 DEBUG。然后,我们创建了一个文件处理器和一个控制台处理器,并设置它们的日志级别分别为 DEBUG 和 INFO。接着,我们创建了一个日志格式器,并将它设置给文件处理器和控制台处理器。最后,我们使用 logger 对象记录了不同级别的日志信息。 通过使用 Python logger,我们可以更好地跟踪程序的运行状态和调试信息,从而方便地进行错误排查和日志分析。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

周小董

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值