python学习之logging模块

什么是日志

日志记录了软件运行时所产生的信息,而信息的重要又不同,因此可根据信息的重要性将日志分等级,分类存储

何时使用日志
  • 记录程序正常操作的事件
  • 记录程序中发生的错误
  • 记录程序发生的警告
  • 记录程序异常
一个小例子
# -*- coding: utf-8 -*-

import logging
import sys

# 获取logger实例,如果参数为空则返回root logger,__name__不同以区分不同的logger实例
logger = logging.getLogger(__name__)

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

# 文件日志
file_handler = logging.FileHandler("my.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 message')
logger.info('this is info message')
logger.warn('this is warning message')
logger.error('this is error message')
logger.fatal('this is fatal message')
logger.critical('this is critical message')

结果输出

2018-08-08 20:50:34,115 INFO    : this is info message
2018-08-08 20:50:34,117 WARNING : this is warning message
2018-08-08 20:50:34,117 ERROR   : this is error message
2018-08-08 20:50:34,117 CRITICAL: this is fatal message
2018-08-08 20:50:34,117 CRITICAL: this is critical message

在以上结果中,有心的读者或许会发现没有debug的日志信息,这是为什么呢?logger又是怎样运作的?接下来,我们一起学习一下

日志级别

数值越大级别越高,默认为WARNING

CRITICAL = 50
FATAL = CRITICAL
ERROR = 40
WARNING = 30
WARN = WARNING
INFO = 20
DEBUG = 10
NOTSET = 0 
logging的基类
LogRecord

一个LogRecord对象,对应了日志中的一行数据。通常包含:时间、日志级别、message信息、当前执行的模块、行号、函数名……这些信息都包含在一个LogRecord对象里,算是日志的一个字典

Filterer

Filterer是logger和handler的基类,提供了添加和删除filter,并提供了filter(record)过滤record

class Filterer(object):
    def __init__(self):
        self.filters = []

    def addFilter(self, filter):
        if not (filter in self.filters):
            self.filters.append(filter)

    def removeFilter(self, filter):
        if filter in self.filters:
            self.filters.remove(filter)

    def filter(self, record):
        rv = 1
        for f in self.filters:
            if not f.filter(record):
                rv = 0
                break
        return rv
Filter

Filter对象用于对LogRecord进行过滤

class Filter(object):
    def __init__(self, name=''):
        self.name = name
        self.nlen = len(name)

    # 返回1,record通过,返回0,不通过
    def filter(self, record):
        if self.nlen == 0:
            return 1
        elif self.name == record.name:
            return 1
        elif record.name.find(self.name, 0, self.nlen) != 0:
            return 0
        return (record.name[self.nlen] == ".")
logging配置
  • 通过代码进行完整配置,参考开头的例子,主要是通过getLogger方法实现。
  • 通过代码进行简单配置,下面有例子,主要是通过basicConfig方法实现。
import logging
logging.basicConfig(filename='example.log',level=logging.DEBUG)
logging.debug('This message should go to the log file')
  • 通过配置文件,下面有例子,主要是通过 logging.config.fileConfig(filepath)
logging四大组件

logging不仅可把日志输出到文件,同样支持把日志输出到TCP/UDP、HTTP服务器

Logger

提供了日志接口,配置和发送日志信息,并根据日志的级别或者根据filter对象,决定记录哪些日志,之后负责把日志传递给相应的handler

  • GetLogger()

最基本的logger入口,该方法如果参数为空,默认的logger名称是root,在同一程序中都会拿到同一实例,同样也可以利用日志名称区分同一程序的不同模块

Logger实例的名称是使用句号(.)分隔的多级结构。在这种命名方式中,后面的logger是前面的logger的子(父子logger只是简单的通过命名来识别),比如:有一个名称为foo的logger,那么诸如foo.bar、foo.bar.baz和foo.bam这样的logger都是foo这个logger的子logger。子logger会自动继承父logger的定义和配置。 使用相同的名称多次调用logging.getLogger([name])方法,会返回同一个logger对象的引用。这个规则不仅仅在同一个module有效,而且对在同一个Python解释器进程的多个module也有效。因此应用程序可以在一个module中定义一个父logger,然后在其他module中继承这个logger,而不必把所有的logger都配置一遍。

  • setLevel(level)

设置logger的日志级别,只会处理日志级别高于level的日志

  • addFilter(filter)

添加过滤器

  • addHandler(handler)

日志分发给相应的handler,可添加零个或多个handler对象到自身

  • removeFilter/removerHandler

  • debug(log_message, [*args[, **kwargs]])

  • info(log_message, [*args[, **kwargs]])
  • warning(log_message, [*args[, **kwargs]])
  • error(log_message, [*args[, **kwargs]])
  • critical(log_message, [*args[, **kwargs]])

以上对应级别的日志,分别进行记录,记录异常信息时,exc_info设置为true值

Handler

Handler负责把日志时间分发到具体的目的地,抽象了log输出这个过程的抽象,一个handler处理一种场景,例如:记录log文件、控制台输出和邮件,就需要三个handler,另外handler必须经过继承后进行实例化

内置的handler

  • StreamHandler
  • FileHandler
  • RotatingFileHandler
  • TimedRotatingFileHandler
  • SocketHandler
  • DatagramHandler
  • SysLogHandler
  • NTEventLogHandler
  • SMTPHandler
  • MemoryHandler
  • HTTPHandler

handler的配置方法

  • setlevel
  • setFormatter(formatter):日志格式,之后讲解
  • addFilter(filter)
  • removeFilter(filter)

自定义handler

自定义的handler必须继承自logging.Handler,logging.Handler是一个定义了所有handler都应该实现的接口并建立了一些子类能够使用的一些默认行为的基类,handler的源码如下:

class Handler(Filterer):

    def __init__(self, level=NOTSET):
        """
        Initializes the instance - basically setting the formatter to None
        and the filter list to empty.
        """
        Filterer.__init__(self)
        self._name = None
        self.level = _checkLevel(level)
        self.formatter = None
        # Add the handler to the global _handlerList (for cleanup on shutdown)
        _addHandlerRef(self)
        self.createLock()

    def get_name(self):
        return self._name

    def set_name(self, name):
        _acquireLock()
        try:
            if self._name in _handlers:
                del _handlers[self._name]
            self._name = name
            if name:
                _handlers[name] = self
        finally:
            _releaseLock()

    name = property(get_name, set_name)

    def createLock(self):
        """
        Acquire a thread lock for serializing access to the underlying I/O.
        """
        if thread:
            self.lock = threading.RLock()
        else:
            self.lock = None

    def acquire(self):
        """
        Acquire the I/O thread lock.
        """
        if self.lock:
            self.lock.acquire()

    def release(self):
        """
        Release the I/O thread lock.
        """
        if self.lock:
            self.lock.release()

    def setLevel(self, level):
        """
        Set the logging level of this handler.
        """
        self.level = _checkLevel(level)

    def format(self, record):
        """
        Format the specified record.

        If a formatter is set, use it. Otherwise, use the default formatter
        for the module.
        """
        if self.formatter:
            fmt = self.formatter
        else:
            fmt = _defaultFormatter
        return fmt.format(record)

    def emit(self, record):
        """
        Do whatever it takes to actually log the specified logging record.

        This version is intended to be implemented by subclasses and so
        raises a NotImplementedError.
        """
        raise NotImplementedError('emit must be implemented '
                                  'by Handler subclasses')

    def handle(self, record):
        """
        Conditionally emit the specified logging record.

        Emission depends on filters which may have been added to the handler.
        Wrap the actual emission of the record with acquisition/release of
        the I/O thread lock. Returns whether the filter passed the record for
        emission.
        """
        rv = self.filter(record)
        if rv:
            self.acquire()
            try:
                self.emit(record)
            finally:
                self.release()
        return rv

    def setFormatter(self, fmt):
        """
        Set the formatter for this handler.
        """
        self.formatter = fmt

    def flush(self):
        """
        Ensure all logging output has been flushed.

        This version does nothing and is intended to be implemented by
        subclasses.
        """
        pass

    def close(self):
        """
        Tidy up any resources used by the handler.

        This version removes the handler from an internal map of handlers,
        _handlers, which is used for handler lookup by name. Subclasses
        should ensure that this gets called from overridden close()
        methods.
        """
        #get the module data lock, as we're updating a shared structure.
        _acquireLock()
        try:    #unlikely to raise an exception, but you never know...
            if self._name and self._name in _handlers:
                del _handlers[self._name]
        finally:
            _releaseLock()

    def handleError(self, record):
        """
        Handle errors which occur during an emit() call.

        This method should be called from handlers when an exception is
        encountered during an emit() call. If raiseExceptions is false,
        exceptions get silently ignored. This is what is mostly wanted
        for a logging system - most users will not care about errors in
        the logging system, they are more interested in application errors.
        You could, however, replace this with a custom handler if you wish.
        The record which was being processed is passed in to this method.
        """
        if raiseExceptions and sys.stderr:  # see issue 13807
            ei = sys.exc_info()
            try:
                traceback.print_exception(ei[0], ei[1], ei[2],
                                          None, sys.stderr)
                sys.stderr.write('Logged from file %s, line %s\n' % (
                                 record.filename, record.lineno))
            except IOError:
                pass    # see issue 5971
            finally:
                del ei
  • emit()方法负责执行记录日志所需操作的一切事情,子类中必须实现这个方法。
  • close()方法负责清理handler所使用的资源,python解释器退出时,会调用所有的flush和close,就像C++的析构函数,子类重写时必须调入父类的方法
Filter

Filter对日志的LogRecord对象进行过滤, 在上述的基类中介绍过

Formatter

一般来说,日志没有格式很怕,处理起来很不顺手,因此Formatter正是为解决这个问题而生,并且应用程序可以直接实例化。

class Formatter(object):

    converter = time.localtime

    def __init__(self, fmt=None, datefmt=None):
        if fmt:
            self._fmt = fmt
        else:
            self._fmt = "%(message)s"
        self.datefmt = datefmt

    def formatTime(self, record, datefmt=None):

    def formatException(self, ei):

    def usesTime(self):

    def format(self, record):
  • Formatter的构造函数接受两个参数,第一个参数用于日志信息的格式化字符串,第二个参数是日期的格式化字符串,第一个参数用%()s风格的字符串做替换,一些替换字符串和他们所代表的含义如下:

    %(name)s            Name of the logger (logging channel)
    %(levelno)s         Numeric logging level for the message (DEBUG, INFO,
                        WARNING, ERROR, CRITICAL)
    %(levelname)s       Text logging level for the message ("DEBUG", "INFO",
                        "WARNING", "ERROR", "CRITICAL")
    %(pathname)s        Full pathname of the source file where the logging
                        call was issued (if available)
    %(filename)s        Filename portion of pathname
    %(module)s          Module (name portion of filename)
    %(lineno)d          Source line number where the logging call was issued
                        (if available)
    %(funcName)s        Function name
    %(created)f         Time when the LogRecord was created (time.time()
                        return value)
    %(asctime)s         Textual time when the LogRecord was created
    %(msecs)d           Millisecond portion of the creation time
    %(relativeCreated)d Time in milliseconds when the LogRecord was created,
                        relative to the time the logging module was loaded
                        (typically at application startup time)
    %(thread)d          Thread ID (if available)
    %(threadName)s      Thread name (if available)
    %(process)d         Process ID (if available)
    %(message)s         The result of record.getMessage(), computed just as
                        the record is emitted

ok,通过上述这么对解释,我们应该对logging这个功能有了更深的理解,对于最开始给出的例子应该是完全理解,下来的事就是多多联系即可。

不同级别的log存入不同文件的demo
# -*— encoding=UTF-8 -*-
import logging

# 自定义一个filter规则
class LogLevelFilter(logging.Filter):
    def __init__(self, name='', level=logging.INFO):
        super(LogLevelFilter, self).__init__(name)
        self.level = level

    def filter(self, record):
        return record.levelno == self.level

logger = logging.getLogger('test')
logger.setLevel(logging.INFO) #logger的默认级别为WARNING

# info存入info.log
InfoHandler = logging.FileHandler('info.log')
InfoHandler.setLevel(logging.INFO)
InfoHandler.addFilter(LogLevelFilter(level=logging.INFO))

# warn存入warn.log
WarningHandler = logging.FileHandler('warn.log')
WarningHandler.setLevel(logging.WARNING)
WarningHandler.addFilter(LogLevelFilter(level=logging.WARNING))

# handler分发
logger.addHandler(WarningHandler)
logger.addHandler(InfoHandler)

# 日志记录
logger.info('this is a info message')
logger.warn('thie is a warn message')
参考博客
  1. https://blog.csdn.net/hallo_ween/article/details/64906838
  2. https://www.jianshu.com/p/e3abceb9ab43
  3. http://python.jobbole.com/86887/
  4. https://www.cnblogs.com/jay54520/p/8487611.html
  5. http://www.cnblogs.com/cicaday/p/pythong-logging.html
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值