make sure your python version > Python 2.3 所有的logging对象都是从root logging对象“继承”而来?!
###1 从一个小案例说起: cat howto_logging.py

#coding=utf8
# file name:	howto_logging.py
# this file shows how to use logging
# made by vasks
import logging

# 创建一个logger,级别:DEBUG
logger = logging.getLogger('Err_Logger')
logger.setLevel(logging.DEBUG)

# 创建一个handler,用于写入日志文件
fh = logging.FileHandler('err.log')
fh.setLevel(logging.DEBUG)

# 再创建一个handler,用于输出到控制台
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)

# 定义handler的输出格式
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s - %(lineno)d')
fh.setFormatter(formatter)
ch.setFormatter(formatter)

# 给logger添加handler
logger.addHandler(fh)
logger.addHandler(ch)

# 记录日志 NOTSET < DEBUG < INFO < WARNING < ERROR < CRITICAL
logger.debug('just a debug')
logger.info('just a info')
logger.warning('waring,please check out')
logger.error('error,oh no')
logger.critical("critical,it doesn't work")

终端输出执行结果:

2015-04-02 17:03:29,421 - Err_Logger - DEBUG - just a debug - 29
2015-04-02 17:03:29,423 - Err_Logger - INFO - just a info - 30
2015-04-02 17:03:29,423 - Err_Logger - WARNING - waring,please check out - 31
2015-04-02 17:03:29,423 - Err_Logger - ERROR - error,oh no - 32
2015-04-02 17:03:29,423 - Err_Logger - CRITICAL - critical,it doesn't work - 33
[Finished in 0.1s]

err.log的结果:

cat err.log
2015-04-02 17:03:29,421 - Err_Logger - DEBUG - just a debug - 29
2015-04-02 17:03:29,423 - Err_Logger - INFO - just a info - 30
2015-04-02 17:03:29,423 - Err_Logger - WARNING - waring,please check out - 31
2015-04-02 17:03:29,423 - Err_Logger - ERROR - error,oh no - 32
2015-04-02 17:03:29,423 - Err_Logger - CRITICAL - critical,it doesn't work - 33

2. logging 详解

####2.1 logger:提供日志接口,供应用代码使用。logger最长用的操作有两类:配置和发送日志消息。

  • logging.getLogger(name): 可以通过logging.getLogger(name)获取logger对象,如果不指定name则返回root对象,多次使用相同的name调用getLogger方法返回同一个logger对象。
  • Logger.setLevel(lvl): 设置logger的level, level有以下几个级别:NOTSET < DEBUG < INFO < WARNING < ERROR < CRITICAL 如果把looger的级别设置为INFO, 那么小于INFO级别的日志都不输出, 大于等于INFO级别的日志都输出。小于的就不会输出。

####2.2 handler:将日志记录发送到合适的目的地,比如文件,socket等。一个logger对象可以通过addHandler方法添加0到多个handler,每个handler又可以定义不同日志级别,以实现日志分级过滤显示。 我们这里设置了两个,一个输出到终端,一个输出到err.log文件。 handler主要有以下几种: StreamHandler: 输出到控制台 FileHandler: 输出到文件 handler还可以设置自己的level以及输出格式。

####2.3 filter:提供一种优雅的方式决定一个日志记录是否发送到handler。

####2.4 formatter:指定日志记录输出的具体格式。formatter的构造方法需要两个参数:消息的格式字符串和日期字符串,这两个参数都是可选的。

更多的格式:

格式                       描述(代表什么)
%(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).
%(funcName)s	Name of function containing the logging call.
%(lineno)d	        Source line number where the logging call was issued (if available).
%(created)f	        Time when the LogRecord was created (as returned by time.time()).
%(relativeCreated)d	Time in milliseconds when the LogRecord was created, relative to the time the logging module was loaded.
%(asctime)s	        Human-readable time when the LogRecord was created. By default this is of the form “2003-07-08 16:49:45,896”     (the numbers after the comma are millisecond portion of the time).
%(msecs)d	        Millisecond portion of the time when the LogRecord was created.
%(thread)d	        Thread ID (if available).
%(threadName)s	Thread name (if available).
%(process)d	        Process ID (if available).
%(message)s	        The logged message, computed as msg % args.

这里是中文的:

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

####2.5 logging.basicConfig() ####2.5.1 logging.basicConfig([**kwargs])

  • 这个函数用来配置root logger, 为root logger创建一个Handler, 设置默认的格式。
  • 这些函数: logging.debug()、logging.info()、logging.warning()、 logging.error()、logging.critical() 如果调用的时候发现root logger没有任何 handler, 会自动调用basicConfig添加一个handler
  • 如果root logger已有handler, 这个函数不做任何事情,使用basicConfig来配置root logger的输出格式和level。 例子:
import logging
      logging.basicConfig(format='%(levelname)s:%(message)s', 
                                              level=logging.DEBUG)
      logging.debug('This message should appear on the console')

关于root logger以及logger的父子关系

前面多次提到root logger, 实际上logger实例之间还有父子关系, root logger就是处于 最顶层的logger, 它是所有logger的祖先。如下图:

root logger是默认的logger 如果不创建logger实例, 直接调用logging.debug()、logging.info()、logging.warning()、logging.error()、logging.critical()这些函数,那么使用的logger就是 root logger, 它可以自动创建,也是单实例的。

如何得到root logger 通过logging.getLogger()或者logging.getLogger("")得到root logger实例。

默认的level root logger默认的level是logging.WARNING

如何表示父子关系 logger的name的命名方式可以表示logger之间的父子关系. 比如:

parent_logger = logging.getLogger('foo')
    child_logger = logging.getLogger('foo.bar')

什么是effective level logger有一个概念,叫effective level。 如果一个logger没有显示地设置level,那么它就 用父亲的level。如果父亲也没有显示地设置level, 就用父亲的父亲的level,以此推.... 最后到达root logger,一定设置过level。默认为logging.WARNING child loggers得到消息后,既把消息分发给它的handler处理,也会传递给所有祖先logger处理,

来看一个例子

import logging

# 设置root logger
r = logging.getLogger()
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
ch.setFormatter(formatter)
r.addHandler(ch)

# 创建一个logger作为父亲
p = logging.getLogger('foo')
p.setLevel(logging.DEBUG)
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(asctime)s - %(message)s')
ch.setFormatter(formatter)
p.addHandler(ch)

# 创建一个孩子logger
c = logging.getLogger('foo.bar')
c.debug('foo')

输出如下:

2015-04-02 18:44:38,128 - foo
2015-04-02 18:44:38,128 - DEBUG - foo
[Finished in 0.2s]

可见, 孩子logger没有任何handler,所以对消息不做处理。但是它把消息转发给了它的父亲以及root logger。最后输出两条日志。

再看个例子:

import logging
     
    # set up logging to file - see previous section for more details
    logging.basicConfig(level=logging.DEBUG,
                        format='%(asctime)s %(name)-12s %(levelname)-8s %(message)s',
                        datefmt='%m-%d %H:%M',
                        filename='mylogging.log',
                        filemode='w')
    # define a Handler which writes INFO messages or higher to the sys.stderr
    console = logging.StreamHandler()
    console.setLevel(logging.WARNING)
    # set a format which is simpler for console use
    formatter = logging.Formatter('%(name)-12s: %(levelname)-8s %(message)s')
    # tell the handler to use this format
    console.setFormatter(formatter)
    # add the handler to the root logger
    logging.getLogger('').addHandler(console)
     
    # Now, we can log to the root logger, or any other logger. First the root...
    logging.info('Jackdaws love my big sphinx of quartz.')
     
    # Now, define a couple of other loggers which might represent areas in your
    # application:
     
    logger1 = logging.getLogger('myapp.area1')
    logger2 = logging.getLogger('myapp.area2')
     
    logger1.debug('Quick zephyrs blow, vexing daft Jim.')
    logger1.info('How quickly daft jumping zebras vex.')
    logger2.warning('Jail zesty vixen who grabbed pay from quack.')
    logger2.error('The five boxing wizards jump quickly.')

终端输出:

myapp.area2 : WARNING  Jail zesty vixen who grabbed pay from quack.
    myapp.area2 : ERROR    The five boxing wizards jump quickly.
    [Finished in 0.1s]

终端下 StreamHandler(): logging.info root自己info没有设置的级别warning大,不输出 logger1 的info和debug没有设置的级别:warning 大,所以不输出 logger2符合条件,输出 文本输出:

04-02 19:15 root                 INFO     Jackdaws love my big sphinx of quartz.
04-02 19:15 myapp.area1  DEBUG    Quick zephyrs blow, vexing daft Jim.
04-02 19:15 myapp.area1  INFO     How quickly daft jumping zebras vex.
04-02 19:15 myapp.area2  WARNING  Jail zesty vixen who grabbed pay from quack.
04-02 19:15 myapp.area2  ERROR    The five boxing wizards jump quickly.

basicConfig中设置的报警级别是debug,所以大家都输出了,输出到了文本。

####2.5.1 详细说下basicConfig参数:

filename: 指定日志文件名
    filemode: 和file函数意义相同,指定日志文件的打开模式,'w'或'a'
    format: 指定输出的格式和内容,format可以输出很多有用信息,如上例所示:
     %(levelno)s: 打印日志级别的数值
     %(levelname)s: 打印日志级别名称
     %(pathname)s: 打印当前执行程序的路径,其实就是sys.argv[0]
     %(filename)s: 打印当前执行程序名
     %(funcName)s: 打印日志的当前函数
     %(lineno)d: 打印日志的当前行号
     %(asctime)s: 打印日志的时间
     %(thread)d: 打印线程ID
     %(threadName)s: 打印线程名称
     %(process)d: 打印进程ID
     %(message)s: 打印日志信息
    datefmt: 指定时间格式,同time.strftime()
    level: 设置日志级别,默认为logging.WARNING
    stream: 指定将日志的输出流,可以指定输出到sys.stderr,sys.stdout或者文件,默认输出到sys.stderr,当stream和filename同时指定时,stream被忽略

本文引用这些博客: