加入收藏 | 设为首页 | 会员中心 | 我要投稿 均轻资讯网 (https://www.ijunqing.com/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 移动互联 > 应用 > 正文

如何优雅地调试你的python程序之logging库使用

发布时间:2022-09-19 12:58:20 所属栏目:应用 来源:
导读:  写在开始:由于在学校没有编程规范要求,从而养成了使用print直接打屏的恶习。

  1. python的日志记录工具logging 1.1 为什么需要日志记录

  日志记录是一种跟踪某些软件运行时发生的事件的方法。该
  写在开始:由于在学校没有编程规范要求,从而养成了使用print直接打屏的恶习。
 
  1. python的日志记录工具logging 1.1 为什么需要日志记录
 
  日志记录是一种跟踪某些软件运行时发生的事件的方法。该软件的开发人员将日志记录调用添加到其代码中,以指示已发生某些事件。事件由描述性消息描述,该消息可以可选地包含可变数据(即,对于事件的每次出现可能不同的数据)。事件也具有开发人员对事件的重要性;重要性也可以称为水平或严重程度。
 
  1.2 为什么使用python自带的日志记录工具
 
  使用标准库模块提供的日志记录API的主要好处是所有Python模块都可以参与日志记录,因此您的应用程序日志可以包含您自己的消息应用程序日志,这些消息与来自第三方模块的消息集成在一起。
 
  1.3 何时使用哪种日志
 
  Python 标准库 logging 用作记录日志,默认分为六种日志级别(括号为级别对应的数值)分别对应于不同的日志的重要程度:
 
  NOTSET(0)
 
  DEBUG(10)
 
  INFO(20)
 
  WARNING(30)
 
  ERROR(40)
 
  CRITICAL(50)
 
  我们自定义日志级别时注意不要和默认的日志级别数值相同,logging 执行时输出大于等于设置的日志级别的日志信息,如设置日志级别是 INFO,则 INFO、WARNING、ERROR、CRITICAL 级别的日志都会输出。默认级别为WARNING,这意味着将仅跟踪此级别及更高级别的事件,除非日志包已配置为执行其他操作。。
 
  要确定何时使用日志记录,请参阅下表,其中列出了针对一组常见任务中的每个任务的最佳工具。(双语对照,极致阅读体验)
 
  was应用日志路径_信息化应用高级研修日志_应用程序日志
 
  日志记录功能以它们用于跟踪的事件的级别或严重性命名。标准水平及其适用性如下所述(按严重程度递增顺序):
 
  信息化应用高级研修日志_应用程序日志_was应用日志路径
 
  2. 如何记录日志
 
  两种方法:
 
  1. 打印到控制台。
 
  import logging
  logging.warning('Watch out!')  # will print a message to the console
  logging.info('I told you so')  # will not print anything
  因为默认日志级别为warning所以下面info的日志不会打印在屏幕上
 
  2.将它们写入磁盘文件。
 
  import logging
  logging.basicConfig(filename='example.log',level=logging.DEBUG)
  logging.debug('This message should go to the log file')
  logging.info('So should this')
  logging.warning('And this, too')
  上面的代码会在文件路径上生成一个example.log文件,现在,如果我们打开文件并查看我们的内容,我们应该找到日志消息:
 
  DEBUG:root:This message should go to the log file
  INFO:root:So should this
  WARNING:root:And this, too
  此示例还说明了如何设置作为跟踪阈值的日志记录级别。在这种情况下,因为我们将阈值设置为DEBUG,所以打印了所有消息。注意:logging.basicConfig(filename='example.log',level=logging.DEBUG)此行应在所有的日志前
 
  如果多次运行上述脚本,则连续运行的消息将附加到文件example.log中。如果您希望每次运行重新开始,而不记住先前运行的消息,则可以通过将上例中的调用更改为:来指定filemode参数:
 
  logging.basicConfig(filename='example.log', filemode='w', level=logging.DEBUG)
  3. 如何组织多个模型的日志
 
  如果您的程序包含多个模块,这里有一个如何组织日志记录的示例:
 
  # myapp.py
  import logging
  import mylib
  def main():
      logging.basicConfig(filename='myapp.log', level=logging.INFO)
      logging.info('Started')
      mylib.do_something()
      logging.info('Finished')
  if __name__ == '__main__':
      main()
  # mylib.py
  import logging
  def do_something():
      logging.info('Doing something')
  实际的日志输出应该为下面这种:
 
  INFO:root:Started
  INFO:root:Doing something
  INFO:root:Finished
  这种日志无法定位到日志产生的位置,程序出错了更无法定位错误,那么如何对上述两个python文件做有效和有意义的日志记录呢?
 
  详情请见自定义logger
 
  4. 对日志格式的更改
 
  要更改用于显示消息的格式,您需要指定要使用的格式:
 
  import logging
  logging.basicConfig(format='%(levelname)s:%(message)s', level=logging.DEBUG)
  logging.debug('This message should appear on the console')
  logging.info('So should this')
  logging.warning('And this, too')
  会输出下面的日志结果:
 
  DEBUG:This message should appear on the console
  INFO:So should this
  WARNING:And this, too
  发现没有,这样的日志会显得更加简洁明了
 
  要显示事件的日期和时间,您可以在格式字符串中放置'%(asctime)s':
 
  import logging
  logging.basicConfig(format='%(asctime)s %(message)s', datefmt='%m/%d/%Y %I:%M:%S %p')
  logging.warning('is when this event was logged.')
  日志结果如下:
 
  12/12/2010 11:46:36 AM is when this event was logged.
  到此为止,对于基础的日志使用已经完全够用了,如果你想更进一步,那么你可以继续阅读: 自定义logger
 
  日志库采用模块化方法,并提供几类组件:记录器Logger,处理程序Handlers,过滤器Filter和格式化Formatters程序。
 
  日志事件信息在实例中的记录器,处理程序,过滤器和格式化程序之间传递。
 
  一个系统只有一个 Logger 对象,并且该对象不能被直接实例化,没错,这里用到了单例模式,获取 Logger 对象的方法为getLogger。注意:这里的单例模式并不是说只有一个 Logger 对象,而是指整个系统只有一个根 Logger 对象,Logger 对象在执行 info()、error() 等方法时实际上调用都是根 Logger 对象对应的 info()、error() 等方法。
 
  我们可以创造多个 Logger 对象,但是真正输出日志的是根 Logger 对象。每个 Logger 对象都可以设置一个名字,如果设置logger = logging.getLogger(__name__),__name__ 是 Python 中的一个特殊内置变量,他代表当前模块的名称(默认为 __main__)。则 Logger 对象的 name 为建议使用使用以点号作为分隔符的命名空间等级制度。
 
  Logger 对象可以设置多个 Handler 对象和 Filter 对象,Handler 对象又可以设置 Formatter 对象。Formatter 对象用来设置具体的输出格式,常用变量格式如下表所示,所有参数见Python(3.7)官方文档:
 
  信息化应用高级研修日志_应用程序日志_was应用日志路径
 
  Logger 对象和 Handler 对象都可以设置级别,而默认 Logger 对象级别为 30 ,也即 WARNING,默认 Handler 对象级别为 0,也即 NOTSET。logging 模块这样设计是为了更好的灵活性,比如有时候我们既想在控制台中输出DEBUG 级别的日志,又想在文件中输出WARNING级别的日志。可以只设置一个最低级别的 Logger 对象,两个不同级别的 Handler 对象,示例代码如下:
 
  import logging
  import logging.handlers
  logger = logging.getLogger("logger")
  handler1 = logging.StreamHandler()
  handler2 = logging.FileHandler(filename="test.log")
  logger.setLevel(logging.DEBUG)
  handler1.setLevel(logging.WARNING)
  handler2.setLevel(logging.DEBUG)
  formatter = logging.Formatter("%(asctime)s %(name)s %(levelname)s %(message)s")
  handler1.setFormatter(formatter)
  handler2.setFormatter(formatter)
  logger.addHandler(handler1)
  logger.addHandler(handler2)
  # 分别为 10、30、30
  # print(handler1.level)
  # print(handler2.level)
  # print(logger.level)
  logger.debug('This is a customer debug message')
  logger.info('This is an customer info message')
  logger.warning('This is a customer warning message')
  logger.error('This is an customer error message')
  logger.critical('This is a customer critical message')
  控制台输出为:
 
  2018-10-13 23:24:57,832 logger WARNING This is a customer warning message
  2018-10-13 23:24:57,832 logger ERROR This is an customer error message
  2018-10-13 23:24:57,832 logger CRITICAL This is a customer critical message
  test.log文件输出:
 
  2018-10-13 23:44:59,817 logger DEBUG This is a customer debug message
  2018-10-13 23:44:59,817 logger INFO This is an customer info message
  2018-10-13 23:44:59,817 logger WARNING This is a customer warning message
  2018-10-13 23:44:59,817 logger ERROR This is an customer error message
  2018-10-13 23:44:59,817 logger CRITICAL This is a customer critical message
  创建了自定义的 Logger 对象,就不要在用 logging 中的日志输出方法了,这些方法使用的是默认配置的 Logger 对象,否则会输出的日志信息会重复。
 
  2018-10-13 22:21:35,873 logger WARNING This is a customer warning message
  WARNING:logger:This is a customer warning message
  2018-10-13 22:21:35,873 logger ERROR This is an customer error message
  ERROR:logger:This is an customer error message
  2018-10-13 22:21:35,873 logger CRITICAL This is a customer critical message
  CRITICAL:logger:This is a customer critical message
  注意上面的日志默认登记为warning所以前面的debug和info日志不会输出。
 
  logger配置
 
  通过上面的例子,我们知道创建一个 Logger 对象所需的配置了,上面直接硬编码在程序中配置对象,配置还可以从字典类型的对象和配置文件获取。
 
  从字典获取配置:
 
  import logging.config
  config = {
      'version': 1,
      'formatters': {
          'simple': {
              'format': '%(asctime)s - %(name)s - %(levelname)s - %(message)s',
          },
          # 其他的 formatter
      },
      'handlers': {
          'console': {
              'class': 'logging.StreamHandler',
              'level': 'DEBUG',
              'formatter': 'simple'
          },
          'file': {
              'class': 'logging.FileHandler',
              'filename': 'logging.log',
              'level': 'DEBUG',
              'formatter': 'simple'
          },
          # 其他的 handler
      },
      'loggers':{
          'StreamLogger': {
              'handlers': ['console'],
              'level': 'DEBUG',
          },
          'FileLogger': {
              # 既有 console Handler,还有 file Handler
              'handlers': ['console', 'file'],
              'level': 'DEBUG',
          },
          # 其他的 Logger
      }
  }
  logging.config.dictConfig(config)
  StreamLogger = logging.getLogger("StreamLogger")
  FileLogger = logging.getLogger("FileLogger")
  # 省略日志输出
  从配置文件中获取配置信息:
 
  test.ini
  [loggers]
  keys=root,sampleLogger
  [handlers]
  keys=consoleHandler
  [formatters]
  keys=sampleFormatter
  [logger_root]
  level=DEBUG
  handlers=consoleHandler
  [logger_sampleLogger]
  level=DEBUG
  handlers=consoleHandler
  qualname=sampleLogger
  propagate=0
  [handler_consoleHandler]
  class=StreamHandler
  level=DEBUG
  formatter=sampleFormatter
  args=(sys.stdout,)
  [formatter_sampleFormatter]
  format=%(asctime)s - %(name)s - %(levelname)s - %(message)s
  import logging.config
  logging.config.fileConfig(fname='test.ini', disable_existing_loggers=False)
  logger = logging.getLogger("sampleLogger")
  # 省略日志输出
  上述代码直接从test.ini中获取配置信息
 
  实战中的问题
 
  1. 中文乱码时使用utf8的编码方式
 
  handler = logging.FileHandler(filename="test.log", encoding="utf-8")  # 自定义logger
  logging.basicConfig(handlers=[logging.FileHandler("test.log", encoding="utf-8")], level=logging.DEBUG)  # 使用默认的logger
  2. 有时候我们又不想让日志输出,但在这后又想输出日志。如果我们打印信息用的是 print() 方法,那么就需要把所有的 print() 方法都注释掉,而使用了 logging 后,我们就有了一键开关闭日志的 "魔法"。一种方法是在使用默认配置时,给 logging.disabled() 方法传入禁用的日志级别,就可以禁止设置级别以下的日志输出了,另一种方法时在自定义 Logger 时,Logger 对象的 disable 属性设为 True,默认值是 False,也即不禁用。
 
  logging.disable(logging.INFO)
  logger.disabled = True
  3.如果将日志保存在一个文件中,那么时间一长,或者日志一多,单个日志文件就会很大,既不利于备份,也不利于查看。我们会想到能不能按照时间或者大小对日志文件进行划分呢?答案肯定是可以的,并且还很简单,logging 考虑到了我们这个需求。logging.handlers 文件中提供了 TimedRotatingFileHandler 和 RotatingFileHandler 类分别可以实现按时间和大小划分。打开这个 handles 文件,可以看到还有其他功能的 Handler 类,它们都继承自基类 BaseRotatingHandler。
 
  # TimedRotatingFileHandler 类构造函数
  def __init__(self, filename, when='h', interval=1, backupCount=0, encoding=None, delay=False, utc=False, atTime=None):
  # RotatingFileHandler 类的构造函数
  def __init__(self, filename, mode='a', maxBytes=0, backupCount=0, encoding=None, delay=False)
  示例代码如下:
 
  # 每隔 1000 Byte 划分一个日志文件,备份文件为 3 个
  file_handler = logging.handlers.RotatingFileHandler("test.log", mode="w", maxBytes=1000, backupCount=3, encoding="utf-8")
  # 每隔 1小时 划分一个日志文件,interval 是时间间隔,备份文件为 10 个
  handler2 = logging.handlers.TimedRotatingFileHandler("test.log", when="H", interval=1, backupCount=1
 

(编辑:均轻资讯网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!