Python日志全攻略

前言


 logging模板块是Python的一个内置标准库,用于实现对日志的控制输出,对于日常的日志输出,甚至是系统级的日志输出,也均可以使用logging模块来进行实现。日志信息默认输出到sys.stdout输出流,更换输出流后需要重新指定日志的输出流!

一、Python内置logging模块


1. 使用basicConfig进行简单的一次性配置服务

logging.basicConfig用于对logging模块整个日志输出的一次性配置,也就是说屡次配置时以第一次配置的为准,以后再使用basicConfig进行配置则无效。实例:

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

# filename:设置日志输出文件,以天为单位输出到不一样的日志文件,以避免单个日志文件日志信息过多,
# 日志文件若是不存在则会自动建立,但前面的路径如log文件夹必须存在,不然会报错
log_file = 'log/sys_%s.log' % datetime.datetime.strftime(datetime.datetime.now(), '%Y-%m-%d')
# level:设置日志输出的最低级别,即低于此级别的日志都不会输出
# 在平时开发测试的时候能够设置成logging.debug以便定位问题,但正式上线后建议设置为logging.WARNING,既能够下降系统I/O的负荷,也能够避免输出过多的无用日志信息
log_level = logging.WARNING
# format:设置日志的字符串输出格式
log_format = '%(asctime)s[%(levelname)s]: %(message)s'
logging.basicConfig(filename=log_file, level=logging.WARNING, format=log_format)
logger = logging.getLogger()

# 如下日志输出因为level被设置为了logging.WARNING,因此debug和info的日志不会被输出
logger.debug('This is a debug message!')
logger.info('This is a info message!')
logger.warning('This is a warning message!')
logger.error('This is a error message!')
logger.critical('This is a critical message!')

logging.basicConfig的参数:

filename:设置日志输出的文件,默认输出到控制台。
filemode:设置打开日志文件的方式,默认为“a”,即追加。
format:设置日志输出的字符串格式,具体的格式有以下几种:

  • %(name)s:日志记录器的名称
  • %(levelno)s:日志级别数值
  • %(levelname)s:日志级别名称
  • %(pathname)s:输出日志时当前文件的绝对路径
  • %(filename)s:输出日志时当前文件名(包含后缀)
  • %(module)s:输出日志时的模块名(即%(filename)s不包含后缀名)
  • %(funcName)s:输出日志时所在函数名
  • %(lineno)d:输出日志时在文件中的行号
  • %(asctime)s:输出日志时的时间
  • %(thread)d:输出日志的当前线程ID
  • %(threadName)s:输出日志的当前线程名称
  • %(process)s:输出日志的当前进程ID
  • %(processName)s:输出日志的当前进程名称
  • %(message)s:输出日志的内容

datefmt:设置日志时间字符串的输出格式,默认时间字符串格式为%Y-%m-%d %H:%M:%S。
style:设置format字符串格式化的风格,能够是“%”,“{”或“$”,默认是“%”。
level:设置日志的级别,具体有如下几种(由高到低):调试

  • CRITICAL:致命错误
  • ERROR:严重错误
  • WARNING:须要给出的提示
  • INFO:通常的日志信息
  • DEBUG:在debug过程当中的debug信息

stream:指定日志的输出Stream,能够是sys.stderr,sys.stdout或者是文件(即便用open函数打开的文件流,可是这个文件流logging模块不会主动关闭它),默认是sys.stderr,若是同时指定了filename参数和stream参数,那stream参数就会被忽略。日志

2. 使用Handler将日志同时输出到文件和控制台

logging.StreamHandler()和FileHandler()将日志同时输出到文件控制台

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

logger = logging.getLogger()
# 设置此logger的最低日志级别,以后添加的Handler级别若是低于这个设置,则以这个设置为最低限制
logger.setLevel(logging.INFO)

# 建立一个FileHandler,将日志输出到文件
log_file = 'log/sys_%s.log' % datetime.datetime.strftime(datetime.datetime.now(), '%Y-%m-%d')
file_handler = logging.FileHandler(log_file)
# 设置此Handler的最低日志级别
file_handler.setLevel(logging.WARNING)
# 设置此Handler的日志输出字符串格式
log_formatter = logging.Formatter('%(asctime)s[%(levelname)s]: %(message)s')
file_handler.setFormatter(log_formatter)

# 建立一个StreamHandler,将日志输出到Stream,默认输出到sys.stderr
stream_handler = logging.StreamHandler()
stream_handler.setLevel(logging.INFO)

# 将不一样的Handler添加到logger中,日志就会同时输出到不一样的Handler控制的输出中
# 注意若是此logger在以前使用basicConfig进行基础配置,由于basicConfig会自动建立一个Handler,因此此logger将会有3个Handler
# 会将日志同时输出到3个Handler控制的输出中
logger.addHandler(file_handler)
logger.addHandler(stream_handler)

经过给logger添加不一样的Handler,能够将日志同时输出到不一样的地方,须要注意的是使用basicConfig进行一次性基础配置时,会根据配置内容自动建立一个Handler,因此若是以后再添加了N个Handler,实际上总的Handler数量有N+1个。

通常在logging或者logging.handlers下就能够你想要的Handler,不一样的Handler会以不一样的方式输出到不一样的地方,如下是几种经常使用的Handler:

from logging import FileHandler: 以“a”(追加)的方式将日志输出到文件,若是文件不存在,则自动建立该文件。
from logging import StreamHandler: 将日志输出到Stream,好比sys.stderr、sys.stdour、文件流等。
from logging.handlers import RotatingFileHandler: 将日志输出到文件,能够经过设置文件大小,文件达到上限后自动建立一个新的文件来继续输出文件。
from logging.handlers import TimedRotatingFileHandler: 将日志输出到文件,能够经过设置时间,使日志根据不一样的时间自动建立并输出到不一样的文件中。
from logging.handlers import HTTPHandler: 将日志发送到一个HTTP服务器。
from logging.handlers import SMTPHandler: 将日志发送到一个指定的邮件地址。

3. 使用不一样的日志级别输出日志

最低级别:logger.setLevel为设置logger的最低日志级别,若是handler中也设置了级别,则不能低于这个级别,低于这个级别的设置是无效的。

不一样级别的意义:在开发或者部署应用程序时,须要尽量详尽的信息来进行开发和调试,这时候用的比较多的是来自DEBUG和INFO级别的日志信息,可是到了正式上线或者生产环境时,应该使用WARNING和CRITICAL级别的日志信息以下降机器的I/O压力和提升获取到错误信息的效率。

日志量:日志的信息量应该是与日志级别成反比的:DEBUG>INFO>WARNING>ERROR>CRITICAL。

二、loguru库


1. 将日志信息输出到控制台的同时保存至本地文件

>>> from loguru import logger
>>> logger.add('out.log')
1
>>> logger.info('hello world')
2022-03-01 20:42:22.011 | INFO     | __main__:<module>:1 - hello world
>>> with open('out.log') as f:
...     print(f.read())
...
2022-03-01 20:42:22.011 | INFO     | __main__:<module>:1 - hello world

2. 更改日志信息格式

>>> import sys
>>> from loguru import logger
>>> logger.remove()
>>> logger.add(sys.stdout, format='{time} {level} {message}')
1
>>> logger.info('hello world')
2022-03-01T21:26:46.850506+0800 INFO hello world

3. 通用框架

import sys
from loguru import logger
logger.remove()
logger.add(sys.stdout, level='INFO', format='{message}')
logger.add('output.log', level='INFO', format='{message}')
logger.info('hello world')

三、nb_log库

GitHub - ydf0509/nb_log: pip install nb_log 各种日志handler和自动转化项目的任意print的效果。日志自动彩色炫酷,可点击控制台的日志自动精确跳转到pycharm的文件和行号。文件日志多进程切割安全。在10个最重要方面全方位超过loguru

问题


1. 将控制台输出保存到文件 (更换输出流)

  • 重定向标准输出流,但不会再控制台输出
  • 使用tee命令重定向,但是控制台信息断断续续
  • 自定义logger,显示正常
#1 输出流
import sys
f = open('a.log', 'a')
sys.stdout = f
sys.stderr = f	

#2 tee
python a_script.py 2>&1 | tee a.log

#3 自定义logger
import sys
class Logger(object):
    def __init__(self, filename='default.log', stream=sys.stdout):
	    self.terminal = stream
	    self.log = open(filename, 'a')

    def write(self, message):
	    self.terminal.write(message)
	    self.log.write(message)
        self.log.flush()        #即时将缓存区数据导出

    def flush(self):
	    pass

sys.stdout = Logger(a.log, sys.stdout)
sys.stderr = Logger(a.log_file, sys.stderr)		# redirect std err, if necessary

2. 将日志信息添加到新的输出流

在问题1切换输出流后导致日志不能输出

#问题1中的自定义logger
sys.stdout = Logger('a.log', sys.stdout)
sys.stderr = Logger('a.log', sys.stderr)

#将logging的stream指定为新的sys.stdout
logging.basicConfig(stream=sys.stdout, level = logging.INFO, format='%(asctime)s-%(levelname)s-%(message)s')
#之后可以正常调用logger

3. tf.Keras训练进度条在多行输出

 pycharm中或者更换了新的输出流后,换行了,说明此处self._dynamic_displayFalse

self._dynamic_display = ((hasattr(sys.stdout, 'isatty') and
                          sys.stdout.isatty()) or
                          'ipykernel' in sys.modules)

_dynamic_display为True有两种条件

  • hasattr(sys.stdout, ‘isatty’) and sys.stdout.isatty()
  • ‘ipykernel’ in sys.modules

第一种是检测当前输出是否在终端上,第二种是当前导入的包里有没有ipykernel
而pycharm中直接运行的脚本,sys.stdout.isatty()为False,故我们需要满足另一个条件,才能进入正确的分支。
于是只要在代码中 import ipykernel 就可以解决

References


http://www.javashuo.com/article/p-ytorvmmq-c.html

【Python】将控制台输出保存至文件(loguru)_Xavier Jiezou的博客-CSDN博客_loguru 文件

python将控制台输出保存至文件_KFXW的博客-CSDN博客_python输出结果存到文件

  • 1
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Python日志模块是标准库中的一个模块,可以用来记录应用程序的日志信息。封装Python日志可以方便地统一管理应用程序的日志信息,使得应用程序的开发和维护更加容易。 以下是一个简单的Python日志封装的例子: ```python import logging class Logger(object): def __init__(self, logger_name): self.logger = logging.getLogger(logger_name) self.logger.setLevel(logging.INFO) formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') console_handler = logging.StreamHandler() console_handler.setLevel(logging.INFO) console_handler.setFormatter(formatter) self.logger.addHandler(console_handler) def info(self, msg): self.logger.info(msg) def error(self, msg): self.logger.error(msg) def warning(self, msg): self.logger.warning(msg) def debug(self, msg): self.logger.debug(msg) ``` 上面的代码定义了一个Logger类,该类封装了Python的logging模块。在类的构造函数中,我们设置了日志的级别为INFO,并且添加了一个控制台输出的处理器。在类的方法中,我们分别封装了info、error、warning和debug级别的日志输出。在应用程序中,我们可以通过该类来统一管理应用程序的日志信息,例如: ```python logger = Logger('my_logger') logger.info('This is an info message.') logger.error('This is an error message.') logger.warning('This is a warning message.') logger.debug('This is a debug message.') ``` 通过上述代码,我们可以在控制台中看到相应级别的日志信息。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值