广告
返回顶部
首页 > 资讯 > 后端开发 > Python >python Howto之logging
  • 846
分享到

python Howto之logging

pythonHowtologging 2023-01-31 05:01:58 846人浏览 薄情痞子

Python 官方文档:入门教程 => 点击学习

摘要

    本文来源于对py2.7.9 docs中howto-logging部分加之源代码的理解。官方文档链接如下,我用的是下载的pdf版本,应该是一致的:https://docs.python.org/2/howto/logging.html

    本文来源于对py2.7.9 docs中howto-logging部分加之源代码的理解。官方文档链接如下,我用的是下载的pdf版本,应该是一致的:https://docs.python.org/2/howto/logging.html

    我们不按照文档上由浅入深的讲解顺序,因为就这么点东西不至于有“入”这个动作。

    使用logging模块记录日志涉及四个主要类,使用官方文档中的概括最为合适:

    logger提供了应用程序可以直接使用的接口;

    handler将(logger创建的)日志记录发送到合适的目的输出;

    filter提供了细度设备来决定输出哪条日志记录;

    fORMatter决定日志记录的最终输出格式。

    

    写log的一般顺序为:

    一、创建logger:

    我们不要通过logging.Logger来直接实例化得到logger,而是需要通过logging.getLogger("name")来生成logger对象。

    不是说我们不能实现Logger的实例化,而是我们期待的是同一个name得到的是同一个logger,这样多模块之间可以共同使用同一个logger,getLogger正是这样的解决方案,它内部使用loggerDict字典来维护,可以保证相同的名字作为key会得到同一个logger对象。我们可以通过实例来验证一下:

#test_logger1.py
#coding:utf-8

import logging
print logging.getLogger("mydear")    
import test_logger2
test_logger2.run()           #调用文件2中的函数,保证两个模块共同处于生存期

#test_logger2.py
#coding:utf-8

import logging
def run():
    print logging.getLogger("mydear")

输出:

<logging.Logger object at 0x00000000020ECF28>
<logging.Logger object at 0x00000000020ECF28>

结果表明两个文件中通过"mydear"调用getLogger可以保证得到的logger对象是同一个。而分别进行Logger类的实例化则不能保证。

    有了logger之后就可以配置这个logger,例如设置日志级别setLevel,绑定控制器addHandler,添加过滤器addFilter等。

    配置完成后,就可以调用logger的方法写日志了,根据5个日志级别对应有5个日志记录方法,分别为logger.debug,logger.info,logger.warning,logger.error,logger.critical。


    二、配置Logger对象的日志级别:

    logger.setLevel(logging.DEBUG)  #DEBUG以上的日志级别会被此logger处理


    三、创建handler对象

    handler负责将log分发到某个目的输出,存在多种内置的Handler将log分发到不同的目的地,或是控制台,或是文件,或是某种形式的stream,或是Socket等。一个logger可以绑定多个handler,例如,一条日志可以同时输出到控制台和文件中。

    以FileHandler和StreamHandler为例:

    logfile= logging.FileHandler("./log.txt")  #创建一个handler,用于将日志输出到文件中   

    console = logging.StreamHandler()         #创建另一个handler,将日志导向流

    handler对象也需要设置日志级别,由于一个logger可以包含多个handler,所以每个handler设置日志级别是有必要的。用通俗的话讲,比如,我们需要处理debug以上级别的消息,所以我们将logger的日志级别定为DEBUG;然后我们想把error以上的日志输出到控制台,而DEBUG以上的消息输出到文件中,这种分流就需要两个Handler来控制。

    logfile.setLevel(logging.DEBUG)

    console.setLevel(logging.ERROR)

    除了对handler对象设置日志级别外,还可以指定formatter,即日志的输出格式。对handler对象设置日志格式,说明了可以将一条记录以不同的格式输出到控制台,文件或其他目的地。

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

    logfile.setFormatter(formatter) #设置handler的日志输出格式

    formatter创建时使用的关键字,最后会以列表的形式展现,这不是重点。


    四、绑定handler到logger中

    至此handlers和logger已经准备好了,下面我们就将handlers绑定到logger上,一个logger对象可以绑定多个handler。

    logger.addHandler(logfile)  #logger是通过getLogger得到的Logger对象

    logger.addHandler(console)


    五、使用logger真正写日志

    logger.debug("some debug message.")

    logger.info("some info message.")

   

    看上去,中间步骤(创建handler,设置日志级别,设置输出格式等)更像是配置Logger,一旦配置完成则直接调用写日志的接口即可,稍后这些日志将按照先前的配置输出。

    

    呜呼,好多内容啊,来点简单的吧.

    下面的代码,是最简单的。导入logging之后就进行了写日志操作:

#coding:utf-8

import logging
logging.debug("debug mes")
logging.info("info mes")
logging.warning("warn mes")

    控制台输出如下:

    WARNING:root:warn mes

    咦?发生了什么情况,为什么只输出了warning?handler、logger、formatter去哪儿了?

    -_-!说好的最简单的呢?为了让自己讲信用,我尽可能把它解释成“最简单的”。

    

    知识点1:logger间存在继承关系

    logger通过名字来决定继承关系,如果一个logger的名字是"mydest",另一个logger的名字是"mydest.dest1"(getLogger("mydest.dest1")),那么就称后者是前者的子logger,会继承前者的配置。上面的代码没有指定logger,直接调用logging.debug等方法时,会使用所有logger的祖先类RootLogger

从上面的代码运行结果可以猜测出,该RootLogger设置的日志级别是logging.WARN,输出目的地是标准流。从源码可以更清楚的看出来:

root = RootLogger(WARNING)  #设置WARNING的级别

    至于rootLogger的输出目的地的配置,我们跟踪logging.debug的源代码来看一下:

def debug(msg, *args, **kwargs):
    """
    Log a message with severity 'DEBUG' on the root logger.
    """
    if len(root.handlers) == 0:
        basicConfig()
    root.debug(msg, *args, **kwargs)

    大约可以看到,如果rootLogger没有配置handler,就会不带参数运行basicConfig函数(*请看知识点2),我们看一下basicConfig的源代码:

def basicConfig(**kwargs):
    _acquireLock()
    try:
        if len(root.handlers) == 0:
            filename = kwargs.get("filename")
            if filename:
                mode = kwargs.get("filemode", 'a')
                hdlr = FileHandler(filename, mode)
            else:
                stream = kwargs.get("stream")
                hdlr = StreamHandler(stream)
            fs = kwargs.get("format", BASIC_FORMAT)
            dfs = kwargs.get("datefmt", None)
            fmt = Formatter(fs, dfs)
            hdlr.setFormatter(fmt)
            root.addHandler(hdlr)
            level = kwargs.get("level")
            if level is not None:
                root.setLevel(level)
    finally:
        _releaseLock()

    因为参数为空,所以我们就看出了,该rootLoger使用了不带参数的StreamHandler,也可以看到诸如format之类的默认配置。之后我们跟踪StreamHandler(因为我们想看到日志输出目的地的配置,而handler就是控制日志流向的,所以我们要跟踪它)的源代码:

class StreamHandler(Handler):
    """
    A handler class which writes logging records, appropriately formatted,
    to a stream. Note that this class does not close the stream, as
    sys.stdout or sys.stderr may be used.
    """

    def __init__(self, stream=None):
        """
        Initialize the handler.

        If stream is not specified, sys.stderr is used.
        """
        Handler.__init__(self)
        if stream is None:
            stream = sys.stderr  ####
        self.stream = stream

    不带参数的StreamHandler将会把日志流定位到sys.stderr流,标准错误流同样会输出到控制台

    知识点2:basicConfig函数用来配置RootLogger

    basicConfig函数仅用来配置RootLogger,rootLogger是所有Logger的祖先Logger,所以其他一切Logger会继承该Logger的配置。

    从上面的basicConfig源码看,它可以有六个关键字参数,分别为:

    filename:执行使用该文件名为rootLogger创建FileHandler,而不是StreamHandler

    filemode:指定文件打开方式,默认是"a"

    stream:指定一个流来初始化StreamHandler。此参数不能和filename共存,如果同时提供了这两个参数,则stream参数被忽略

    format:为rootLogger的handler指定输出格式

    datefmt:指定输出的日期时间格式

    level:设置rootLogger的日志级别

    使用样例:

logging.basicConfig(
                   filename = './log.txt',
                   filemode = 'a',
                   #stream = sys.stdout,
                   format = '%(levelname)s:%(message)s',
                   datefmt = '%m/%d/%Y %I:%M:%S',
                   level = logging.DEBUG
                    )

    知识点3 通过示例详细讨论Logger配置的继承关系

    首先准备下继承条件:log2继承自log1,logger的名称可以随意,要注意‘.’表示的继承关系。

#coding:utf-8

import logging
log1 = logging.getLogger("mydear")
log1.setLevel(logging.WARNING)
log1.addHandler(StreamHandler())
log2 = logging.getLogger("mydear.app")
log2.error("display")
log2.info("not display")

    level的继承

    原则:子logger写日志时,优先使用本身设置了的level;如果没有设置,则逐层向上级父logger查询,直到查询到为止。最极端的情况是,使用rootLogger的默认日志级别logging.WARNING。

    从源代码中看更为清晰, 感谢Python的所见即所得:

def getEffectiveLevel(self):
        """
        Get the effective level for this logger.

        Loop through this logger and its parents in the logger hierarchy,
        looking for a non-zero logging level. Return the first one found.
        """
        logger = self
        while logger:
            if logger.level:
                return logger.level
            logger = logger.parent
        return NOTSET

    handler的继承

    原则:先将日志对象传递给子logger的所有handler处理,处理完毕后,如果该子logger的propagate属性没有设置为0,则将日志对象向上传递给第一个父Logger,该父logger的所有handler处理完毕后,如果它的propagate也没有设置为0,则继续向上层传递,以此类推。最终的状态,要么遇到一个Logger,它的propagate属性设置为了0;要么一直传递直到rootLogger处理完毕。

    在上面实例代码的基础上,我们再添加一句代码,即:

#coding:utf-8

import logging
log1 = logging.getLogger("mydear")
log1.setLevel(logging.WARNING)
log1.addHandler(StreamHandler())
log2 = logging.getLogger("mydear.app")
log2.error("display")
log2.info("not display")
print log2.handlers      #打印log2绑定的handler

    输出如下:

    display
    []

    说好的继承,但是子logger竟然没有绑定父类的handler,what's wrong?

    看到下面调用handler的源代码,就真相大白了。可以理解成,这不是真正的(类)继承,只是"行为上的继承":

def callHandlers(self, record):
        """
        Pass a record to all relevant handlers.

        Loop through all handlers for this logger and its parents in the
        logger hierarchy. If no handler was found, output a one-off error
        message to sys.stderr. Stop searching up the hierarchy whenever a
        logger with the "propagate" attribute set to zero is found - that
        will be the last logger whose handlers are called.
        """
        c = self
        found = 0
        while c:
            for hdlr in c.handlers:         #首先遍历子logger的所有handler
                found = found + 1
                if record.levelno >= hdlr.level:
                    hdlr.handle(record)
            if not c.propagate:             #如果logger的propagate属性设置为0,停止
                c = None    #break out 
            else:                           #否则使用直接父logger
                c = c.parent
        ...

   

    额,最简单的样例牵引出来这么多后台的逻辑,不过我们懂一下也是有好处的。

    下面,我们将一些零碎的不是很重要的东西罗列一下,这篇就结束了。

    1.几种LogLevel是全局变量,以整数形式表示,也可以但是不推荐自定义日志级别,如果需要将level设置为用户配置,则获取level和检查level的一般代码是:

#假设loglevel代表用户设置的level内容
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, ...)

    2.format格式,用于创建formatter对象,或者basicConfig中,就不翻译

    %(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

    3.写日志接口

    logging.warn("%s am a hero", "I")   #1 %格式以参数形式提供实参

    logging.warn("%s am a hero" % ("I",)) #2 直接提供字符串,也可以使用format,template

    logging.warn("%(name)s am a hero", {'name':"I"})  #关键字参数   

    logging.warn("%(name)s am a hero" % {'name':"I"}) #甚至这样也可以

    logging.warn("%(name)s am a hero, %(value)s" % {'name':"I", 'value':'Yes'}) #原来%也能解析关键字参数,不一定非是元组

    如果关键字和位置参数混用呢,%应该不会有什么作为了,最强也就能这样:

    logging.warn("%(name)s am a hero, %()s" % {'name':"I" ,'': 'Yes'})#也是字典格式化的原理

    4.配置logging:

    上面已经讲了如果配置handler,绑定到logger。如果需要一个稍微庞大的日志系统,可以想象,我们会使用好多的addHandler,SetFormatter之类的,有够烦了。幸好,logging模块提供了两种额外配置方法,不需要写众多代码,直接从配置结构中获悉我们的配置意图

    方式一:使用配置文件

import logging
import logging.config
logging.config.fileConfig('logging.conf')
# create logger
logger = logging.getLogger('simpleExample')
# 'application' code
logger.debug('debug message')
logger.info('info message')
logger.warn('warn message')
logger.error('error message')
logger.critical('critical message')

#配置文件logging.conf的内容
[loggers]
keys=root,simpleExample
[handlers]
keys=consoleHandler
[formatters]
keys=simpleFormatter
[logger_root]
level=DEBUG
handlers=consoleHandler
[logger_simpleExample]
level=DEBUG
handlers=consoleHandler
qualname=simpleExample
propagate=0
[handler_consoleHandler]
class=StreamHandler
level=DEBUG
formatter=simpleFormatter
args=(sys.stdout,)
[formatter_simpleFormatter]
format=%(asctime)s - %(name)s - %(levelname)s - %(message)s
datefmt=

    方式二:使用字典

请参阅python2.7.9 Library文档,链接:

Https://docs.python.org/2/library/logging.config.html?highlight=dictconfig#configuration-dictionary-schema


    5.众多的handler满足不同的输出需要

    StreamHandler,FileHandler,NullHandler,RotatingFileHandler,TimedRotatingFileHandler,SocketHandler,DatagramHandler,SMTPHandler,SysLogHandler,NTEventLogHandler,MemoryHandler,HTTPHandler,WatchedFileHandler,

    其中前三种在logging模块中给出,其他的在logging.handlers模块中给出。

    

    

    

--结束END--

本文标题: python Howto之logging

本文链接: https://www.lsjlt.com/news/188700.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • python Howto之logging
        本文来源于对py2.7.9 docs中howto-logging部分加之源代码的理解。官方文档链接如下,我用的是下载的pdf版本,应该是一致的:https://docs.python.org/2/howto/logging.html...
    99+
    2023-01-31
    python Howto logging
  • Python Unicode HOWTO
    character, code point, glyph[glɪf], encoding Release: 1.1 This HOWTO discusses Python’s support for Unico...
    99+
    2023-01-31
    Python Unicode HOWTO
  • python之logging模块使用
    #!/usr/bin/env python # encoding: utf-8 import logging #定义handler的输出格式 formatter=logging.Formatter('%(asctime)s--%(name)...
    99+
    2023-01-31
    模块 python logging
  • python-logging
    logging.debug('这是一条debug,开始使用日志啦') logging.info('这是一条info,开始使用日志啦') logging.warning('这是一条warning,开始使用日志啦') logging.error...
    99+
    2023-01-31
    python logging
  • Python基础之logging模块知识总结
    目录前言一、日志级别二、basicConfig三、日志写文件四、traceback记录前言 logging模块是Python内置的标准模块,主要用于输出脚本运行日志,可以设置输出日志的等级、日志保存路径等。 ...
    99+
    2022-06-02
    Python logging模块 python常用模块
  • python logging Timed
      我的定时任务会在每天早上5点钟调一个交易,交易中日志用的logging模块,今天发现log有问题,排查如下。查看日志的时候发现本应5点打的日志,日志时间却显示为2016-09-12 21:00:02,379 - INFO - novel...
    99+
    2023-01-31
    python logging Timed
  • python logging with
    Recently, I was made a service which can provide a simple way to get best model. so, i spent lot of time to read source...
    99+
    2023-01-31
    python logging
  • Python学习-logging
    Python的logging模块提供了通用的日志系统,可以方便第三方模块或者是应用使用。这个模块提供不同的日志级别,并可以采用不同的方式记录日志。logging的日志可以分为debug(),info(),warning(),error()和...
    99+
    2023-01-31
    Python logging
  • Python - logging模块
    logging模块两种配置日志输出格式: 1、 输出如下: 2、 第二种方式如下: 注意 logger = logging.getLogger() 不带参数默认是root用户。 logger1 = logging.getL...
    99+
    2023-01-31
    模块 Python logging
  • Python Logging库HTTP
    Python的logging库是标准库中用来实现日志的库,功能强大,而且使用起来也算是方便。该库提供了很多个不同的Handler,用来对日志进行不同的处理。例如FileHandler用来将日志记录到文件,RotateFileHandle...
    99+
    2023-01-31
    Python Logging HTTP
  • python中的logging
    python logging模块可能会令人困惑的地方通过参阅python logging模块的代码,发现一些有趣的现象: 1. logging对象其实是一个树形结构,每个创建的logging对象都是root logging对象的孩子结点。当...
    99+
    2023-01-31
    python logging
  • Python logging模块
    logging模块是Python的一个标准库模块,开发过程中,可以通过该模块,灵活的完成日志的记录。 logging模块提供了两种记录日志的方式:    1)使用logging提供的模块级别的函数(logging.basicConfig,...
    99+
    2023-01-31
    模块 Python logging
  • python logging 模块
    python logging模块   import sys  import logging    #首先获得一个类  logger = logging.getLogger('example') #example为名字 logger.setL...
    99+
    2023-01-31
    模块 python logging
  • python模块--logging
    logging模块                                                                                                               ...
    99+
    2023-01-31
    模块 python logging
  • Python基础之hashlib模块subprocess模块logging模块
    目录一、hashlib模块基本操作与用法二、subprocess模块简介基本操作与用法三、logging模块简介基本操作与用法一、hashlib模块 什么是哈希模块: hashlib...
    99+
    2022-11-11
  • Python的Logging模块
    1.日志的相关概念🍃 日志是指记录系统或应用程序运行状态、事件和错误信息的文件或数据。在计算机系统中,日志通常用于故障排除、性能分析、安全审计等方面。日志可以记录各种信息,如系统启动...
    99+
    2023-09-17
    服务器 linux python
  • Python标准库 - logging
    编写代码时, 常要跟踪下其运行过程, 记录日志是常用的方式. 较简单的就是print命令打印到终端, 或通过open函数写入文件. 但随着代码量的增加, 该方式不可控的弊端, 也凸显出来, 这也正是logging模块出现的背景.对于logg...
    99+
    2023-01-31
    标准 Python logging
  • 说一说Python logging
    最近有个需求是把以前字符串输出的log 改为json 格式,看了别人的例子,还是有些比较茫然,索性就把logging 整个翻了一边,做点小总结. 初看log 在程序中, log 的用处写代码的你用你知道,l...
    99+
    2022-06-04
    说一说 Python logging
  • Python logging简介详解
    目录一、日志级别1.级别定义2.简单示例3.指定级别二、基本概念1.Logger2.Handler3.Formatter三、示例1.日志输出至控制台2.日志输出至文件四、默认Logg...
    99+
    2022-11-12
  • Python 日志模块logging
    logging模块: logging是一个日志记录模块,可以记录我们日常的操作。 logging日志文件写入默认是gbk编码格式的,所以在查看时需要使用gbk的解码方式打开。 logging日志等级:CRITICAL(50) > E...
    99+
    2023-01-30
    模块 日志 Python
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作