Python的标准logging模块

Python 2.3 introduced the logging module to the Python standard library. logging provides a standard interface for outputting information from a running application. The classic example of a logging mechanism is writing data to a text file, which is a plain old log file. While the log file is likely the most common form of logging, the logging module provides the ability to send information to file-like objects, TCP and UDP sockets, email servers, the Unix syslog, the NT event log, memory buffers, and HTTP servers in addition to "real" files.

从Python2.3起,Python的标准库加入了logging模块.logging模块给运行中的应用提供了一个标准的信息输出接口.典型的logging机制实现是把要输出的数据简单地写到一个txt文件中去.写log文件的方式是一种常见的打log的方式,而logging模块提供的更多,它可以把输出信息输出到所有类文件的对象中去,甚至TCP和UDP的sockets,email服务器,Unix的syslog系统,NT系列的事件log系统,内存的buffer和HTTP服务器,当然还有”真正的”文件中去.

The logging library takes a modular approach and offers the several categories of components: loggers, handlers, filters, and formatters. Loggers expose the interface that application code directly uses. Handlers send the log records to the appropriate destination. Filters provide a finer grained facility for determining which log records to send on to a handler. Formatters specify the layout of the resultant log record.

Logging库被设计成模块的方式,它提供了以下几个子模块:loggers,handlers,filters和formatters.Loggers把应用需要直接调用的接口暴露出来.Handlers把log记录发到相应的目的地.Filters决定哪些记录需要发给handler.Formatters定义了log记录的输出格式.

Loggers

Logger objects have a threefold job. First, they expose several methods to application code so that applications can log messages at runtime. Second, logger objects determine which log messages to act upon based upon severity (the default filtering facility) or filter objects. Third, logger objects pass along relevant log messages to all interested log handlers.

Logger对象扮演了三重角色.首先,它暴露给应用几个方法以便应用可以在运行时写log.其次,Logger对象按照log信息的严重程度或者根据filter对象来决定如何处理log信息(默认的过滤功能).最后,logger还负责把log信息传送给相关的loghandlers.

The most widely used methods on logger objects fall into two categories: configuration and message sending.

Logger中最长使用的方法分成两部分中:configuration和message sending.

The configuration methods are:

用于Configuration的方法:

  • setLevel(level)
  • addFilter(filter)
  • removeFilter(filter)
  • addHandler(handler)
  • removeHandler(handler)

setLevel() specifies the lowest-severity log message a logger will handle, where debug is the lowest built-in severity level and critical is the highest built-in severity. For example, if the severity level is info, the logger will handle only info, warning, error, and critical messages and will ignore debug messages.

addFilter() and removeFilter() add and remove filter objects from the logger object. This article does not address filters.

setLevel()方法定义了一个logger处理的最底严重程度(比如说中/高/底三种,我定义为中,那么只有严重程度为中或者高的log才会被处理).debug级别是内置的最低级别,critical是最高级别.举例来说,如果严重级别设为info级,logger仅仅处理info,warning,error和critical级的log,而debug级别的则忽略掉.

With the logger object configured, the following methods create log messages:

根据logger对象的设置,以下的方法被用来写log:

  • 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]])
  • exception(message[, *args])
  • log(log_level, log_message, [*args[, **kwargs]])

debug(), info(), warning(), error(), and critical() all create log records with a message of log_message and a level that corresponds to their respective method names. log_message is actually a format string, which may contain the standard string substitution syntax of %s, %d, %f, and so on. *args is a list of objects that correspond with the substitution fields in log_message. With regard to **kwargs, the logging methods care only about a keyword of exc_info and use it to determine whether to log exception information.

debug(),info(),warning(),error()和critical()方法用一个log_message格式字符串和与之对应的各个参数来生成log信息.log_message实际上是一个格式字符串,它可以包含诸如%s,%d,%f此类的替换符号.*args是实际要替换%s,%d,%f参数的列表.至于这个**kwargs关键字参数,logging只处理一个关键字exc_info,这个关键字决定是否对异常信息打log.

exception() creates a log message similar to error(). The difference is that exception() dumps a stack trace along with it. Call this method only from an exception handler.

exception()跟error()方法基本一样.不同之处是exception()多出一个stack trace用于转储.exception()方法只能从一个exception handler里面调用.

log() takes a log level as an explicit argument. This is a little more verbose for logging messages than using the log level convenience methods listed above, but this is how to log at custom log levels.

Log()方法显式的带一个level参数,用这个可以得到比使用上面所列举的方法更为详细的log信息,这属于自定义log信息的范畴了.

logging.getLogger([name]) returns a reference to a logger instance with a name of name if a name is provided, or root if not. The names are period-separated (.) hierarchical structures. Multiple calls to logging.getLogger() with the same name will return a reference to the same logger object. Loggers that are further down in the hierarchical list are children of loggers higher up in the list. For example, given a logger with a name of foo, loggers with names of foo.bar, foo.bar.baz, and foo.bam are all children of foo. Child loggers propagate messages up to their parent loggers. Because of this, it is unnecessary to define and configure all the loggers an application uses. It is sufficient to configure a top-level logger and create child loggers as needed.

Logging.getLogger([name])方法返回一个logger实例的引用,如果name参数给出,则用这个参数的值作为名字,如果没有则用root做默认值.名字是以点号分割的命名方式命名的(a.b.c).对同一个名字的多个调用logging.getLogger()方法会返回同一个logger对象.这种命名方式里面,后面的loggers是前面logger的子.比如说,有一个名字是foo的logger,那么诸如foo.bar,foo.bar.baz和foo.bam这样的logger都是foo这个logger的子,子loggers自动继承父loggers的log信息,正因为此,没有必要把一个应用的所有logger都配置一边,只要把顶层的logger配置好了,然后子logger根据需要继承就行了.

Handlers

Handler objects are responsible for dispatching the appropriate log messages (based on the log messages' severity) to the handler's specified destination. Logger objects can add zero or more handler objects to themselves with an addHandler() method. As an example scenario, an application may want to send all log messages to a log file, all log messages of error or higher to stdout, and all messages of critical to an email address. This scenario requires three individual handlers where each hander is responsible for sending messages of a specific severity to a specific location.

Handler对象负责分配合适的log信息(基于log信息的严重程度)到handler指定的目的地.Logger对象可以用addHandler()方法添加零个或多个handler对象到它自身.一个常见的场景是,一个应用可能希望把所有的log信息都发送到一个log文件中去,所有的error级别以上的log信息都发送到stdout,所有critical的log信息通过email发送.这个场景里要求三个不同handler处理,每个handler负责把特定的log信息发送到特定的地方.

The standard library includes the following handlers:

标准库里面包括以下的handlers:

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

This article uses only StreamHandler and FileHandler in its examples.

本文里面只用到了StreamHandler和FileHandler

There are very few methods in a handler for application developers to concern themselves with. The only handler methods that seem relevant for application developers who are using the built-in handler objects (that is, not creating custom handlers) are the following configuration methods:

Handler里面提供给应用开发者的只有很少的几个方法可用.对使用内置的handler(就是说不是自定义的handlers)的开发者可用的配置方法如下:

  • setLevel(level)
  • setFormatter(formatter)
  • addFilter(filter)
  • removeFilter(filter)

The setLevel() method, just as in logger objects, specifies the lowest severity that will be dispatched to the appropriate destination. Why are there two setLevel() methods? The level set in the logger determines which severity of messages it will pass to its handlers. The level set in each handler determines which messages that handler will send on. setFormatter() selects a Formatter object for this handler to use. addFilter() and removeFilter() respectively configure and deconfigure filter objects on handlers.

setLevel()方法跟logger对象里面的setLevel()一样,也是用于设定一个最低分发log信息的级别.为什么有两个setLevel()呢?logger的严重等级用于决定那个级别的log信息可以分发到它的handlers.handler里面的level设置用于控制那些个log信息是handler需要转寄的.setFormatter()方法选定一个格式化对象给它自己用.addFilter()和removeFilter()分别用于为handler增加一个filter和删除一个filter.

Application code should not directly instantiate and use handlers. Instead, the logging.Handler class is a base class that defines the interface that all Handlers should have and establishes some default behavior that child classes can use (or override).

应用里面Handler不应该被直接实例化.相反,应该用logging.Handler类作所有Handlers的基类,在它里面定义所有自Handler用到的接口并且创建一些默认的方法让子类来用(或者继承).

 

Formatters

Formatter objects configure the final order, structure, and contents of the log message. Unlike the base logging.Handler class, application code may instantiate formatter classes, although you could likely subclass the formatter if your application needs special behavior. The constructor takes two optional arguments: a message format string and a date format string. If there is no message format string, the default is to use the raw message. If there is no date format string, the default date format is:

Formatter对象定义了最终log信息的顺序,结构和内容.于基本的logging.Handler类不同,应用可以直接实例化formatter类,当然,如果需要你也可以子例化formatter以便定制它的一些行为.构造函数接受两个可选参数:一个信息格式字符串和一个日期格式字符串.如果没有信息格式字符串,直接输出log信息.如果没有日期格式字符串,默认的格式是:

%Y-%m-%d %H:%M:%S

with the milliseconds tacked on at the end.

毫秒加载最后面.

The message format string uses %(<dictionary key>)s styled string substitution. Here is the Formatter class's docstring, which describes the valid substitution strings and what they mean:

信息格式字符串用%(<dictionary key>)s风格的字符串做替换.下面是Formatter类的docstring,说明了现有的替换字符串和它们所代表的意思.

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

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

%(process)d         Process ID (if available)

%(message)s         The result of record.getMessage(), computed just as

                    the record is emitted

The following message format string will log the time in a human-readable format, the severity of the message, and the contents of the message, in that order:

下面的信息格式字符串会输出一个可读格式的串,严重级别和log信息内容以下面的格式给出:

"%(asctime)s - %(levelname)s - %(message)s"

 

Configuring Logging

Programmers can configure logging either by creating loggers, handlers, and formatters explicitly in a main module with the configuration methods listed above (using Python code), or by creating a logging config file. The following code is an example of configuring a very simple logger, a console handler, and a simple formatter in a Python module:

程序员可以显示地通过在主模块里面用上面列出的配置方法创建loggers,handlers和formatters的方式,或者,创建一个logging的配置文件的方式来配置logging.以下是一个非常简单的配置logger的例子,一个python模块里面包含了一个命令行handler和一个简单的formmater:

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.warn("warn message")

logger.error("error message")

logger.critical("critical message")

Running this module from the command line produces the following output:

在在字符界面下运行上面的命令产生以下输出:

jmjones@bean:~/logging $ python simple_logging_module.py

2005-03-19 15:10:26,618 - simple_example - DEBUG - debug message

2005-03-19 15:10:26,620 - simple_example - INFO - info message

2005-03-19 15:10:26,695 - simple_example - WARNING - warn message

2005-03-19 15:10:26,697 - simple_example - ERROR - error message

2005-03-19 15:10:26,773 - simple_example - CRITICAL - critical message

The following Python module creates a logger, handler, and formatter nearly identical to those in the example listed above, with the only difference being the names of the objects:

跟例子差不多,以下的Python模块创建了一个logger,一个handler和一个formatter,这里仅仅换了换名字:

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")

Here is the logging.conf file:

这里是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=

The output is nearly identical to that of the non-config-file-based example:

输出跟没有配置文件的例子完全一样:

jmjones@bean:~/logging $ python simple_logging_config.py

2005-03-19 15:38:55,977 - simpleExample - DEBUG - debug message

2005-03-19 15:38:55,979 - simpleExample - INFO - info message

2005-03-19 15:38:56,054 - simpleExample - WARNING - warn message

2005-03-19 15:38:56,055 - simpleExample - ERROR - error message

2005-03-19 15:38:56,130 - simpleExample - CRITICAL - critical message

The config file approach has a few advantages over the Python code approach. The first is the separation of configuration and code. The second is the ability of noncoders to easily modify the logging properties. The third is the really cool listen() method, which causes the application to listen on a socket for new configurations--and will update configurations at runtime without forcing you to restart the application!

用配置文件的方法比直接在Python代码里面写有几个好处.第一个好处是配置和代码的分离.第二个好处是即使看不懂程序也能方便的更改logging的属性.第三个好处是”最酷的listen()方法”,用这个方法可以让你的应用程序在一个socket上监听新的配置信息 -- 可以直接在运行时改变配置而用不着重启你的应用~!

Here is a slight modification of the previous config file-based script:

这里是一个简单的基于上面配置文件的脚本:

#!/usr/bin/env python

import logging

import logging.config

import time

import os

#specify logging config file

logging.config.fileConfig("logging.conf")

#create and start listener on port 9999

t = logging.config.listen(9999)

t.start()

#create logger

logger = logging.getLogger("simpleExample")

#watch for existence of file named "f"

#loop through the code while this file exists

while os.path.isfile('f'):

    logger.debug("debug message")

    logger.info("info message")

    logger.warn("warn message")

    logger.error("error message")

    logger.critical("critical message")

    time.sleep(5)

#cleanup

logging.config.stopListening()

t.join()

That was simple enough. Unfortunately, figuring out what format the config file needs to be took some investigation. The only useful information that I found in the Python Standard Library Reference documentation was in the logging configuration section under the listen() method:

够简单了吧!不幸地是,理解这个配置文件需要进行些研究.我仅仅在Python的标准库文档logging配置一节里面的listen()方法找到一点有用的信息:

Logging configurations will be sent as a file suitable for processing by fileConfig().

Logging配置会作为一个能被fileConfig()方法处理的文件发送.

Pushing a filename did not work. Pushing the contents of a config file did not work. I had to dig into the source a little. The docstring for the logging socket server's handle() method is:

用文件名不行,用配置文件的内容也不行.我不得不深入一下源码logging socket服务器的handler()方法的docstring是这样写的:

Handle a request.

Each request is expected to be a 4-byte length,

followed by the config file. Uses fileConfig() to do the

grunt work.

处理一个请求.

每个请求都应该是4-byte长,后面跟一个配置文件.用fileConfig()方法完成剩下的工作.

This struck me as a bit odd. Does that mean you can send lengths only for config files of up to 9,999 bytes? Converting the length of the config file to a string did not work either. I looked farther down in the source code of the handle() method. Aaahh. It does a struct.unpack(), so the socket expects the first 4 bytes to be packed binary data. I tried it this way and it worked. The following snippet of code sends the contents of the file named on the command line to localhost:9999:

这让我感到有些奇怪.难道说只能发送长度大于9,999bytes的配置文件吗?而且把一个配置文件的内容转化成一个字符串也是不起作用.我又看了一下handler()方法的源码.哈.它其实做了struct.unpack()!,所以socket才要求前面4个bytes打包二进制的数据.我用这种方式重新试了一下,可以了.下面的代码片断把指定的文件内容通过字符界面发送到了localhost:9999端口:

#!/usr/bin/env python

import socket

import sys

import struct

HOST = 'localhost'

PORT = 9999

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

print "connecting..."

s.connect((HOST, PORT))

print "connected..."

data_to_send = open(sys.argv[1], "r").read()

print "sending length..."

s.send(struct.pack(">L", len(data_to_send)))

print "sending data..."

s.send(data_to_send)

print "closing..."

s.close()

 

Example Usage

This article contains several examples of simple usage of the logging module. Two additional examples deserve special attention: using multiple handlers or formatters and using logging in multimodule Python applications.

本文包含了几个使用logging模块的简单例子.另外两个例子应该额外注意:使用多handlers多formatters和在多Python应用中使用logging的例子.

Multiple handlers and formatters

Loggers are plain Python objects. The addHandler() method has no minimum or maximum quota for the number of handlers you may add. Sometimes it will be beneficial for an application to log all messages of all severities to a text file while simultaneously logging errors or above to the console. To set this up, simply configure the appropriate handlers. The logging calls in the application code will remain unchanged. Here is a slight modification to the previous simple module-based configuration example:

Loggers是一个简单的Python对象.addHandler()方法没有最多或者最少配额,当你的应用需要在把所有的log信息打到一个txt文件中去,同时又需要把errors级别一上的错误信息打到console时,你就会体会到这个特性的好处.只要简单的配置一下合适的handlers就可以实现这个功能.应用对logging的调用用不着修改.以下是对前一个基于module的配置例子的改进:

#!/usr/bin/env python

import logging

#create logger

logger = logging.getLogger("simple_example")

logger.setLevel(logging.DEBUG)

#create console handler and set level to error

ch = logging.StreamHandler()

ch.setLevel(logging.ERROR)

#create file handler and set level to debug

fh = logging.FileHandler("spam.log")

fh.setLevel(logging.DEBUG)

#create formatter

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

   %(message)s")

#add formatter to ch and fh

ch.setFormatter(formatter)

fh.setFormatter(formatter)

#add ch and fh to logger

logger.addHandler(ch)

logger.addHandler(fh)

#"application" code

logger.debug("debug message")

logger.info("info message")

logger.warn("warn message")

logger.error("error message")

logger.critical("critical message")

Notice that the "application" code did not change between the single-handler example and this one. All that changed was the addition and configuration of a new handler named fh.

你可以看到,在单handler例子和这个例子之间应用的代码根本不需要修改.需要做的仅仅是增加一些代码并且添加一个叫fh的handler.

The ability to create new handlers with higher- or lower-severity filters can be very helpful when writing and testing an application. How many times have I written a horde of print statements in a section of code as I was trying to hammer something out, only to comment it out later? A better approach, which also opens the door for later troubleshooting, is to use logger.debug instead of print. Unlike the print statements, which you will have to delete or comment out later, the logger.debug statements can remain intact in the source code and remain dormant until you need them again. At that time, the only change that needs to happen is to modify the severity level of the logger and/or handler to debug.

在我们写应用或者测试应用的时候,这种根据不同的filter创建不同严重级别的handler的能力可以给我们很大的帮助.为了输出点东西, 我已经记不清有多少次被迫写一堆堆的代码了,一个更好的,对以后可能增加的问题open的解决方案是使用logger.debug来代替print.跟print语句不同,print在用完后必须删除或注释掉,而logger.debug可以留在源码中,并且会保持隐匿的状态,直到你再次的需要它.当你需要它的时候,你可以简单的把logger或者handler的严重级别调为debug级即可.

Using logging in multiple modules

I mentioned above that multiple calls to logging.getLogger('someLogger') return a reference to the same logger object. This is true not only within the same module, but also across modules as long as it is in the same Python interpreter process. It is true for references to the same object; additionally, application code can define and configure a parent logger in one module and create (but not configure) a child logger in a separate module, and all logger calls to the child will pass up to the parent. Here is a main module:

上面我曾提到过,所有的对logging.getLogger(‘someLogger’)的调用都会返回同一个对象.这个规则不仅仅在同一个module有效,而且对在同一个Python的解释器进程里面的多个module也有效.而且,应用代码可以在一个module里面定义一个父logger,而在另一个module里面继承这个logger,所有对这个子logger的调用都会转到父logger里面去,如下所示:

#!/usr/bin/env python

import logging

import auxiliary_module

#create logger with "spam_application"

logger = logging.getLogger("spam_application")

logger.setLevel(logging.DEBUG)

#create file handler and set level to debug

fh = logging.FileHandler("spam.log")

fh.setLevel(logging.DEBUG)

#create console handler and set level to error

ch = logging.StreamHandler()

ch.setLevel(logging.ERROR)

#create formatter

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

   %(message)s")

#add formatter to fh

fh.setFormatter(formatter)

#add formatter to ch

ch.setFormatter(formatter)

#add fh to logger

logger.addHandler(fh)

#add ch to logger

logger.addHandler(ch)

logger.info("creating an instance of auxiliary_module.Auxiliary")

a = auxiliary_module.Auxiliary()

logger.info("created an instance of auxiliary_module.Auxiliary")

logger.info("calling auxiliary_module.Auxiliary.do_something")

a.do_something()

logger.info("finished auxiliary_module.Auxiliary.do_something")

logger.info("calling auxiliary_module.some_function()")

auxiliary_module.some_function()

logger.info("done with auxiliary_module.some_function()")

Here is the auxiliary module:

这是辅助模块的代码:

#!/usr/bin/env python

import logging

#create logger

module_logger = logging.getLogger("spam_application.auxiliary")

class Auxiliary:

    def __init__(self):

        self.logger = logging.getLogger("spam_application.auxiliary.Auxiliary")

        self.logger.info("creating an instance of Auxiliary")

    def do_something(self):

        self.logger.info("doing something")

        a = 1 + 1

        self.logger.info("done doing something")

def some_function():

    module_logger.info("received a call to \"some_function\"")

The output looks like this:

输出如下:

2005-03-23 23:47:11,663 - spam_application - INFO -

   creating an instance of auxiliary_module.Auxiliary

2005-03-23 23:47:11,665 - spam_application.auxiliary.Auxiliary - INFO -

   creating an instance of Auxiliary

2005-03-23 23:47:11,665 - spam_application - INFO -

   created an instance of auxiliary_module.Auxiliary

2005-03-23 23:47:11,668 - spam_application - INFO -

   calling auxiliary_module.Auxiliary.do_something

2005-03-23 23:47:11,668 - spam_application.auxiliary.Auxiliary - INFO -

   doing something

2005-03-23 23:47:11,669 - spam_application.auxiliary.Auxiliary - INFO -

   done doing something

2005-03-23 23:47:11,670 - spam_application - INFO -

   finished auxiliary_module.Auxiliary.do_something

2005-03-23 23:47:11,671 - spam_application - INFO -

   calling auxiliary_module.some_function()

2005-03-23 23:47:11,672 - spam_application.auxiliary - INFO -

   received a call to "some_function"

2005-03-23 23:47:11,673 - spam_application - INFO -

   done with auxiliary_module.some_function()

Conclusion

logging was a great addition to the standard library. It provides application developers a simple, single interface for outputting information from a running application. On the very simple end, it can write information to a standard log file. On the more advanced end, it can write the same information to a socket--and it can do both without having to rewrite a single line of application code or even restart the process. So the next time you start to put a bunch of print statements in your code to debug something, consider using logging--specifically, logger.debug(). You won't be disappointed.

Logging模块是对原有标准库的一个非常棒的补充.有了它,应用程序就可以通过一个简单的,单一的接口在运行时输出信息.最基本地,它可以输出信息到一个标准的log文件;高级地,它可以把这些信息输出到一个socket—更棒地是它可以同时做这两件事情,并且不需要改动应用的代码甚至都不需要重启引用的进程~!.所以,下次当你想用一堆print语句来debug什么的时候,考虑一下logging—特别是logger.debug()方法.你绝对不会失望地~!

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值