入门

设置 logging 的完整流程

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
import logging

# Create a logger object
logger = logging.getLogger()
logger.setLevel(logging.INFO)

# Create a stream handler
sh = logging.StreamHandler()

# Set the level of the handler
sh.setLevel(logging.INFO)

# Create a formatter and set it for the handler
formatter = logging.Formatter('%(asctime)s: %(levelname)s: %(message)s')
sh.setFormatter(formatter)

# Add the handler to the logger
logger.addHandler(sh)

# Log an info message
logger.info('hello')
  1. 包含关系:logger –> handler –> formater

    • logger 中设置 handler
    • handler 中设置 formater

基本功能

https://docs.python.org/zh-cn/3/howto/logging.html#logging-basic-tutorial

日志级别 level

  • DEBUG
  • INFO
  • info()
  • debug()

记录到文件

1
logging.basicConfig(filename='example.log',level=logging.DEBUG)

获取命令行参数 –log=INFO

  • 注意:需要验证输入值的合法性

    1
    
    getattr(logging, loglevel.upper())

验证:

1
2
3
4
5
6
7
8
9
# command line argument. Convert to upper case to allow the user to
# specify --log=DEBUG or --log=debug
# * 获取输入
numeric_level = getattr(logging, loglevel.upper(), None)

# * 验证
if not isinstance(numeric_level, int):
    raise ValueError('Invalid log level: %s' % loglevel)
    logging.basicConfig(level=numeric_level, ...)

设置格式 format

1
logging.basicConfig(format='%(levelname)s:%(message)s', level=logging.DEBUG)

显示日期 datefmt

1
2
3
logging.basicConfig(format='%(asctime)s %(message)s')

logging.basicConfig(format='%(asctime)s %(message)s', datefmt='%m/%d/%Y %I:%M:%S %p')
  • 通过设置 log format

    • %(asctime)s

高级功能

logging 的 四大组件

  • 记录器 logger

    • 信息接收接口
  • 处理程序 handlers

    • 决定信息分发到哪里
  • 过滤器 filters

    • 决定是否输出信息
  • 格式化程序 formatters

    • 信息样式

负责传输信息的类

LogRecord

logger

  • 以 "." 号分割的层级命名结构
  • eg: 'scan.text' 'scan.html'
  • 根 logger

    • 名称:root logger
    • 即默认的 logger

      • logging.info() 对应的 logger

为每个模块生成 logger

1
logger = logging.getLogger(__name__)
  • loggging.getLogger(logger_name)

    • 多次调用引用的是同一个对象

消息方法

  • debug()
  • info()
  • warning()
  • error()
  • exception()

    • 注意:输出错误的同时,还会输出调用栈

配置

basicConfig()

  • Logger.setLevel()
  • Logger.addHandler() <—> Logger.removeHandler()
  • Logger.addFilter() <—> Logger.removeFilter()

logger 的 命名层级

当前层级 logger 不能处理的 记录 [没有设置 setLevel()],会递归到上级 logger 来处理

If a level is not explicitly set on a logger, the level of its parent is used instead as its effective level. If the parent has no explicit level set, its parent is examined, and so on - all ancestors are searched until an explicitly set level is found.

Handlers 处理程序

https://docs.python.org/3/howto/logging.html#useful-handlers

  • 用于配置的方法

    • setLevel()
    • addFormatter()

StreamHandler(stream=None)

用于 file-like object(object with write() and flush() methods)

https://docs.python.org/3/library/logging.handlers.html#logging.StreamHandler

  • 方法

    • emit(LogRecord_obj)
    • flush()
    • setStream()
  • 属性

    • terminator
    • line terminator, like '\n'
  • 注意

    • 当 stream==None 时,输出到 str.err
    • 即输出到控制台

FileHandler(filename, mode='a', encoding=None, delay=False)

  • 方法

    • close()
    • emit()

Formatters

被绑定到 Handlers 上面

配置

使用范例

范例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import logging

# create logger
logger = logging.getLogger('simple_example')
logger.setLevel(logging.DEBUG)

# create console handler and set level to debug
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)

# create formatter
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')

# add formatter to ch
ch.setFormatter(formatter)

# add ch to logger
logger.addHandler(ch)

# 'application' code
logger.debug('debug message')
logger.info('info message')
logger.warning('warn message')
logger.error('error message')
logger.critical('critical message')

使用配置文件 logging.conf 文件

loggging.config.fileConfig()

使用 dict

loggging.config.dictConfig()

异常处理

  • 手动控制

    1
    2
    3
    4
    
    import traceback
      
    # * 把异常调用栈,转换成字符串
    logging.error(traceback.format_exc())
  • 自动转换

    1
    
    logging.exception(e)

多进程 multiprocessing + logging

python 官方解决方案

参考:

方法:

  1. 使用 logging.QueueHandler
  2. 在主进程起:

    1. queue 用来传递 LogRecord
    2. listener_process 用来监听 queue, 并处理 queue 传递过来的 LogRecord,并发送给 root logger

      1
      2
      3
      4
      5
      
      record = queue.get()
      if record is None:  # We send this as a sentinel to tell the listener to quit.
          break
      logger = logging.getLogger(record.name)
      logger.handle(record)
    3. 配置

      • listener 只需要配置普通的 handler, 如 FileHandler

        • 负责末端 log 处理,不添加 QueueHandler, 负责造成循环处理
      • worker 配置添加 QueueHandler

        • 其中的 logger 收到的 log, 会被传递给 queue 发送给 listener
      • 注意:

        • 这里实际上在每一个进程都设置了 logger, handler, 他们只能在当前进程生效
  3. 在主进程起一个 listener_process 进程

代码: