日志级别优先级

CRITICAL = FATAL > ERROR > WARNING = WARN > INFO > DEBUG > NOTSET

 

Python logging 源码定义

CRITICAL = 50
FATAL = CRITICAL
ERROR = 40
WARNING = 30
WARN = WARNING
INFO = 20
DEBUG = 10
NOTSET = 0

 

1. 简单的将日志打印到屏幕 

示例代码

import logging

# 默认打印日志到控制台
def test_1():
    logging.debug('logging debug')
    logging.info('logging info')
    logging.warn('logging warn')        # WARNING:root:logging info

运行结果,在控制台打印:

WARNING:root:logging warn

默认情况下,logging将日志打印到屏幕,日志默认级别为WARNING,因此在控制台只输出了warn,而debug和info被过滤掉了。

日志级别大小关系为:CRITICAL > ERROR > WARNING > INFO > DEBUG > NOTSET,当然也可以自己定义日志级别。

 

2. 通过logging.basicConfig函数配置日志输出格式

示例代码

# 配置日志写入到文件
def test_2():
    logging.basicConfig(level=logging.DEBUG,
                        format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s',
#                         datefmt='%Y-%m-%d %H:%M:%S.%f',
                        filename='test_logging2.log',
                        filemode='a')

    logging.debug('logging debug')
    logging.info('logging info')
    logging.warn('logging warn')    

运行结果,在日志文件 test_logging2.log 写入日志:

2015-01-08 11:24:57,195 test_logger.py[line:26] DEBUG logging debug
2015-01-08 11:24:57,195 test_logger.py[line:27] INFO logging info
2015-01-08 11:24:57,195 test_logger.py[line:28] WARNING logging warn
2015-01-08 11:25:03,581 test_logger.py[line:26] DEBUG logging debug
2015-01-08 11:25:03,581 test_logger.py[line:27] INFO logging info
2015-01-08 11:25:03,581 test_logger.py[line:28] WARNING logging warn

 

logging.basicConfig 函数参数说明

filename: 指定日志文件名

filemode: 和file函数意义相同,指定日志文件的打开模式'w'或'a','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被忽略

 

3. 将日志同时输出到文件和屏幕

示例代码:

# 配置日志写入到文件和打印到控制台
def test_3():
    logging.basicConfig(level=logging.DEBUG,
                        format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s',
                        filename='test_logging3.log',
                        filemode='a')
    # 定义一个StreamHandler,将INFO级别或更高的日志信息打印到标准错误,并将其添加到当前的日志处理对象
    console = logging.StreamHandler()
    console.setLevel(logging.INFO)
    formatter = logging.Formatter('%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s')
    console.setFormatter(formatter)
    logging.getLogger('').addHandler(console)
    
    logging.debug('logging debug')
    logging.info('logging info')
    logging.warn('logging warn')   

运行结果

在控制台打印:

2015-01-08 11:56:41,264 test_logger.py[line:45] INFO logging info
2015-01-08 11:56:41,265 test_logger.py[line:46] WARNING logging warn

在日志文件 test_logging3.log 输出:

2015-01-08 11:32:22,334 test_logger.py[line:44] DEBUG logging debug
2015-01-08 11:32:22,334 test_logger.py[line:45] INFO logging info
2015-01-08 11:32:22,335 test_logger.py[line:46] WARNING logging warn
2015-01-08 11:56:41,264 test_logger.py[line:44] DEBUG logging debug
2015-01-08 11:56:41,264 test_logger.py[line:45] INFO logging info
2015-01-08 11:56:41,265 test_logger.py[line:46] WARNING logging warn

说明:文件日志输出级别设置为logging.DEBUG,控制台日志输出级别设置为logging.INFO,因此控制台只输出两条日志,把DEBUG日志过滤掉了。

 

4. logging日志回滚

示例代码:

# 配置日志回滚
def test_4():
#     import logging.handlers                            # ok
    from logging.handlers import RotatingFileHandler    # ok
    rhandler = logging.handlers.RotatingFileHandler(filename='test_logging4.log', maxBytes=10*1024*1024, backupCount=5)
    rhandler.setLevel(logging.DEBUG)
    formatter = logging.Formatter('%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s')
    rhandler.setFormatter(formatter)
    logging.getLogger('').addHandler(rhandler)
    
    logging.debug('logging debug')
    logging.info('logging info')
    logging.warn('logging warn')     
    logging.error('logging error')     

运行结果,在日志文件 test_logging4.log 输出:

2015-01-08 12:01:58,271 test_logger.py[line:60] WARNING logging warn
2015-01-08 12:01:58,271 test_logger.py[line:61] ERROR logging error
2015-01-08 12:02:20,244 test_logger.py[line:60] WARNING logging warn
2015-01-08 12:02:20,244 test_logger.py[line:61] ERROR logging error

从上例和本例可以看出,logging有一个日志处理的主对象,其它处理方式都是通过addHandler添加进去的。

logging的几种handle方式如下

logging.StreamHandler: 日志输出到流,可以是sys.stderr、sys.stdout或者文件

logging.FileHandler: 日志输出到文件

日志回滚方式,实际使用时用RotatingFileHandler和TimedRotatingFileHandler

logging.handlers.BaseRotatingHandler

logging.handlers.RotatingFileHandler

logging.handlers.TimedRotatingFileHandler

logging.handlers.SocketHandler: 远程输出日志到TCP/IP sockets

logging.handlers.DatagramHandler:  远程输出日志到UDP sockets

logging.handlers.SMTPHandler:  远程输出日志到邮件地址

logging.handlers.SysLogHandler: 日志输出到syslog

logging.handlers.NTEventLogHandler: 远程输出日志到Windows NT/2000/XP的事件日志

logging.handlers.MemoryHandler: 日志输出到内存中的制定buffer

logging.handlers.HTTPHandler: 通过"GET"或"POST"远程输出到HTTP服务器

由于StreamHandler和FileHandler是常用的日志处理方式,所以直接包含在logging模块中,而其他方式则包含在logging.handlers模块中

 

5. 通过logging.config模块配置日志

test_logger.conf 日志配置文件 

# test_logger.conf

[loggers]
keys = root, logging_file, logging_rotate


########## logger ##########
[logger_root]
level = DEBUG
handlers = consoleHandler

[logger_logging_file]
handlers = consoleHandler, fileHandler
qualname = logging_file
propagate = 0

[logger_logging_rotate]
handlers = consoleHandler, rotateHandler
qualname = logging_rotate
propagate = 0


########## handlers ##########
[handlers]
keys = consoleHandler, fileHandler, rotateHandler

[handler_consoleHandler]
class = StreamHandler
level = INFO
formatter = form02
args = (sys.stdout,)

[handler_fileHandler]
class = logging.FileHandler
level = DEBUG
formatter = form01
args = ('logging_file.log', 'a')

[handler_rotateHandler]
class = logging.handlers.RotatingFileHandler
level = DEBUG
formatter = form02
args = ('logging_rotate.log', 'a', 10 * 1024 * 1024, 5)


########## formatter ##########
[formatters]
keys = form01, form02
[formatter_form01]
format = %(asctime)s %(filename)s[line: %(lineno)d] %(levelname)s %(message)s
datefmt = %Y-%m-%d %H:%M:%S

[formatter_form02]
format = %(asctime)s %(filename)s[line: %(lineno)d] %(levelname)s %(message)s
#datefmt = %Y-%m-%d %H:%M:%S

 

5.1 通过配置文件,实现上述示例 test_3

示例代码

def test_5_3():
    ''' test_3 配置文件conf实现 '''
    import logging.config
    logging.config.fileConfig(fname="test_logger.conf")
    logger = logging.getLogger('logging_file')
    
    logger.debug('logging debug')
    logger.info('logging info')
    logger.warn('logging warn')     
    logger.error('logging error')  

运行结果

在控制台打印:

2015-01-08 12:10:21,847 test_logger.py[line: 73] INFO logging info
2015-01-08 12:10:21,847 test_logger.py[line: 74] WARNING logging warn
2015-01-08 12:10:21,847 test_logger.py[line: 75] ERROR logging error

在日志文件 logging_file.log 输出:

2015-01-08 12:08:46 test_logger.py[line: 72] DEBUG logging debug
2015-01-08 12:08:46 test_logger.py[line: 73] INFO logging info
2015-01-08 12:08:46 test_logger.py[line: 74] WARNING logging warn
2015-01-08 12:08:46 test_logger.py[line: 75] ERROR logging error
2015-01-08 12:10:21 test_logger.py[line: 72] DEBUG logging debug
2015-01-08 12:10:21 test_logger.py[line: 73] INFO logging info
2015-01-08 12:10:21 test_logger.py[line: 74] WARNING logging warn
2015-01-08 12:10:21 test_logger.py[line: 75] ERROR logging error

 

5.2 通过配置文件,实现上述示例 test_4

示例代码

def test_5_4():
    ''' test_4 配置文件conf实现 '''
    import logging.config
    logging.config.fileConfig(fname="test_logger.conf")
    logger = logging.getLogger('logging_rotate')
    
    logger.debug('logging debug')
    logger.info('logging info')
    logger.warn('logging warn')     
    logger.error('logging error') 

运行结果

在控制台打印:

2015-01-08 12:12:58,278 test_logger.py[line: 84] INFO logging info
2015-01-08 12:12:58,279 test_logger.py[line: 85] WARNING logging warn
2015-01-08 12:12:58,280 test_logger.py[line: 86] ERROR logging error

在日志文件 logging_rotate.log 输出:

2015-01-08 12:12:27,473 test_logger.py[line: 83] DEBUG logging debug
2015-01-08 12:12:27,473 test_logger.py[line: 84] INFO logging info
2015-01-08 12:12:27,473 test_logger.py[line: 85] WARNING logging warn
2015-01-08 12:12:27,474 test_logger.py[line: 86] ERROR logging error
2015-01-08 12:12:58,278 test_logger.py[line: 83] DEBUG logging debug
2015-01-08 12:12:58,278 test_logger.py[line: 84] INFO logging info
2015-01-08 12:12:58,279 test_logger.py[line: 85] WARNING logging warn
2015-01-08 12:12:58,280 test_logger.py[line: 86] ERROR logging error

说明:

5.1 和 5.2 示例中,控制台和日志文件输出的日志级别不同,是因为在日志配置文件 test_logger.conf 中,配置的控制台输出日志级别为 level = INFO,配置的文件输出日志级别为 level = DEBUG

 

6. logging是线程安全的

logging 有关线程安全的源码:

#---------------------------------------------------------------------------
#   Thread-related stuff
#---------------------------------------------------------------------------

#
#_lock is used to serialize access to shared data structures in this module.
#This needs to be an RLock because fileConfig() creates and configures
#Handlers, and so might arbitrary user threads. Since Handler code updates the
#shared dictionary _handlers, it needs to acquire the lock. But if configuring,
#the lock would already have been acquired - so we need an RLock.
#The same argument applies to Loggers and Manager.loggerDict.
#
if thread:
    _lock = threading.RLock()
else:
    _lock = None

def _acquireLock():
    """
    Acquire the module-level lock for serializing access to shared data.

    This should be released with _releaseLock().
    """
    if _lock:
        _lock.acquire()

def _releaseLock():
    """
    Release the module-level lock acquired by calling _acquireLock().
    """
    if _lock:
        _lock.release()

 

总结

Python logging日志的配置和使用,跟其他平台的日志(例如如 log4j)都相似,下面附上本文示例的完整代码,其中引用的配置文件 test_logger.conf,请见上述示例5

示例的完整代码:

# !/usr/bin/env python  
# -*- coding:utf-8 -*-  
# http://www.mimvp.com
  
'''
CRITICAL = FATAL > ERROR > WARNING = WARN > INFO > DEBUG > NOTSET
'''
  
import logging

# 默认打印日志到控制台
def test_1():
    logging.debug('logging debug')
    logging.info('logging info')
    logging.warn('logging warn')        # WARNING:root:logging info


# 配置日志写入到文件
def test_2():
    logging.basicConfig(level=logging.DEBUG,
                        format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s',
#                         datefmt='%Y-%m-%d %H:%M:%S.%f',
                        filename='test_logging2.log',
                        filemode='a')

    logging.debug('logging debug')
    logging.info('logging info')
    logging.warn('logging warn')       


# 配置日志写入到文件和打印到控制台
def test_3():
    logging.basicConfig(level=logging.DEBUG,
                        format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s',
                        filename='test_logging3.log',
                        filemode='a')
    # 定义一个StreamHandler,将INFO级别或更高的日志信息打印到标准错误,并将其添加到当前的日志处理对象
    console = logging.StreamHandler()
    console.setLevel(logging.INFO)
    formatter = logging.Formatter('%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s')
    console.setFormatter(formatter)
    logging.getLogger('').addHandler(console)
    
    logging.debug('logging debug')
    logging.info('logging info')
    logging.warn('logging warn')       


# 配置日志回滚
def test_4():
#     import logging.handlers                            # ok
    from logging.handlers import RotatingFileHandler    # ok
    rhandler = logging.handlers.RotatingFileHandler(filename='test_logging4.log', maxBytes=10*1024*1024, backupCount=5)
    rhandler.setLevel(logging.DEBUG)
    formatter = logging.Formatter('%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s')
    rhandler.setFormatter(formatter)
    logging.getLogger('').addHandler(rhandler)
    
    logging.debug('logging debug')
    logging.info('logging info')
    logging.warn('logging warn')     
    logging.error('logging error')       


# 通过配置文件写入日志到控制台,日志文件,回滚日志文件
def test_5_3():
    ''' test_3 配置文件conf实现 '''
    import logging.config
    logging.config.fileConfig(fname="test_logger.conf")
    logger = logging.getLogger('logging_file')
    
    logger.debug('logging debug')
    logger.info('logging info')
    logger.warn('logging warn')     
    logger.error('logging error')       
 
def test_5_4():
    ''' test_4 配置文件conf实现 '''
    import logging.config
    logging.config.fileConfig(fname="test_logger.conf")
    logger = logging.getLogger('logging_rotate')
    
    logger.debug('logging debug')
    logger.info('logging info')
    logger.warn('logging warn')     
    logger.error('logging error')       
 
 
if __name__ == '__main__':
    test_1()
    test_2()
    test_3()
    test_4()
 
    test_5_3()
    test_5_4()