日志是每一个编程语言必备的模块,借助日志不仅可以监控在线服务的状态,也可以在出问题之后迅速的定位问题。

基本使用

# -*- coding: utf-8 -*-

import logging
import sys

# 获取 logger 实例,如果参数为空则返回 root logger
# 最基本的入口,该方法参数可以为空,默认的 logger 名称是 root,如果在同一个程序中一直都使用同名的 logger,其实会拿到同一个实例,使用这个技巧就可以跨模块调用同样的 logger 来记录日志
logger = logging.getLogger("AppName")

# 指定 logger 输出格式
formatter = logging.Formatter('%(asctime)s %(levelname)-8s: %(message)s')

# 文件日志
file_handler = logging.FileHandler("test.log")
file_handler.setFormatter(formatter)  # 可以通过 setFormatter 指定输出格式

# 控制台日志
console_handler = logging.StreamHandler(sys.stdout)
console_handler.formatter = formatter  # 也可以直接给 formatter 赋值

# 为 logger 添加的日志处理器
logger.addHandler(file_handler)
logger.addHandler(console_handler)

# 指定日志的最低输出级别,默认为 WARN 级别
logger.setLevel(logging.INFO)

# 输出不同级别的 log
logger.debug('this is debug info')
logger.info('this is information')
logger.warn('this is warning message')
logger.error('this is error message')
logger.fatal('this is fatal message, it is same as logger.critical')
logger.critical('this is critical message')

# 2016-10-08 21:59:19,493 INFO    : this is information
# 2016-10-08 21:59:19,493 WARNING : this is warning message
# 2016-10-08 21:59:19,493 ERROR   : this is error message
# 2016-10-08 21:59:19,493 CRITICAL: this is fatal message, it is same as logger.critical
# 2016-10-08 21:59:19,493 CRITICAL: this is critical message

# 移除一些日志处理器
logger.removeHandler(file_handler)

格式化输出

# 格式化输出

service_name = "Booking"
logger.error('%s service is down!' % service_name)  # 使用 python 自带的字符串格式化,不推荐
logger.error('%s service is down!', service_name)  # 使用 logger 的格式化,推荐
logger.error('%s service is %s!', service_name, 'down')  # 多参数格式化
logger.error('{} service is {}'.format(service_name, 'down')) # 使用 format 函数,推荐

# 2016-10-08 21:59:19,493 ERROR   : Booking service is down!

异常

当你使用 logging 模块记录异常信息时,不需要传入该异常对象,只要你直接调用 logger.error() 或者 logger.exception() 就可以将当前异常记录下来。

# 记录异常信息

try:
    1 / 0
except:
    # 等同于 error 级别,但是会额外记录当前抛出的异常堆栈信息
    logger.exception('this is an exception message')

# 2016-10-08 21:59:19,493 ERROR   : this is an exception message
# Traceback (most recent call last):
#   File "D:/Git/py_labs/demo/use_logging.py", line 45, in <module>
#     1 / 0
# ZeroDivisionError: integer division or modulo by zero

常见配置

通过日志名称来区分同一程序的不同模块

logger = logging.getLogger("App.UI")
logger = logging.getLogger("App.Service")

import logging
import time

logging.basicConfig(format="%(asctime)s %(levelname)s %(message)s",
                        datefmt="%Y %b %d %H:%M:%S",
                        filename="./log.log",
                        filemode="w",               # default is "a"
                        level=logging.INFO)

while True:
    for i in range(6):
        logging.log(i*10, "a log")                  # logging.log(level, msg)
        time.sleep(1)

fmt 中允许使用的变量可以参考下表

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

Handler 日志处理器

最常用的是 StreamHandler 和 FileHandler, Handler 用于向不同的输出端打 log,比如可以将一份日志分别输出到文件和终端。

console = logging.StreamHandler(stream=sys.stdout)                    # 默认流为sys.stderr
console.setLevel(logging.INFO)
formatter = logging.Formatter('%(name)-12s: %(levelname)-8s %(message)s')
console.setFormatter(formatter)
logging.getLogger().addHandler(console)

files = logging.FileHandler("log2.log", mode="a", encoding="utf-8")   # 设置文件流
files.setLevel(logging.WARNING)
formatter = logging.Formatter("%(levelname)s %(message)s")
files.setFormatter(formatter)
logging.getLogger().addHandler(files)

说明:

  • StreamHandler instances send error messages to streams (file-like objects).
  • FileHandler instances send error messages to disk files.
  • RotatingFileHandler instances send error messages to disk files, with support for maximum log file sizes and log file rotation.
  • TimedRotatingFileHandler instances send error messages to disk files, rotating the log file at certain timed intervals.
  • SocketHandler instances send error messages to TCP/IP sockets.
  • DatagramHandler instances send error messages to UDP sockets.
  • SMTPHandler instances send error messages to a designated email address.

日志格式配置的方式

logging 的配置大致有下面几种方式。

  • 通过代码进行完整配置,参考开头的例子,主要是通过 getLogger 方法实现。
  • 通过代码进行简单配置,下面有例子,主要是通过 basicConfig 方法实现。
  • 通过配置文件,下面有例子,主要是通过 logging.config.fileConfig(filepath)

通过 basicConfig 配置 logger : https://docs.python.org/2/library/logging.html#logging.basicConfig

日志重复输出的坑

可能的原因有很多,但总结下来无非就一个,日志中使用了重复的 handler

切记添加 handler 时不要重复。