logging 日志模块
http://python.usyiyi.cn/python_278/library/logging.html 中文官方
http://blog.csdn.net/zyz511919766/article/details/25136485 清晰明了,入门必备
http://my.oschina.net/leejun2005/blog/126713 继承讲的很棒
http://my.oschina.net/u/126495/blog/464892 实例分析
一:概述
在实际项目中,需要对一些数据进行日志记录,并将日志记录到不同的存储单元中,例如数据库,文本,或者推送到图形化界面中,当需要时发现自己实现一个日
志库其实是要很大的代价,因此,第三方的日志库上进行定制化处理 正文内容是对logging的理解和使用方式,非常方便
1:四个主要类,使用官方文档中的概括:
logger 提供了应用程序可以直接使用的接口;
handler 将(logger创建的)日志记录发送到合适的目的输出;
filter 提供了细度设备来决定输出哪条日志记录;用处不太大
formatter 决定日志记录的最终输出格式
2:模块级函数
logging.getLogger([name]): #返回一个logger对象,如果没有指定名字将返回root logger,最常用
logging.basicConfig(): #给logger对象的配置管理函数 ,不常用
logging.debug()、logging.info()、logging.warning()、logging.error()、logging.critical(): #logger的日志级别
二: logging工作流演示
cat demo.py
#coding:utf-8
import logging
# 创建一个logger命名为mylogger, %(name)s可调用这个名字
mylogger = logging.getLogger('mylogger')
mylogger.setLevel(logging.DEBUG)
# 定义日志输出格式formatter
formatter = logging.Formatter('%(asctime)s - %(name)s - %(filename)s- %(levelname)s - %(message)s')
# 创建一个handler,用于写入日志文件,只输出debug级别以上的日志,并调用定义的输出格式
fh = logging.FileHandler('test.log')
fh.setLevel(logging.DEBUG)
fh.setFormatter(formatter)
# 再创建一个handler,用于输出到控制台, 一般不用
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
ch.setFormatter(formatter)
# 给我们开始实例化的logger对象添加handler
mylogger.addHandler(fh)
mylogger.addHandler(ch)
# 直接在本模块中调用记录两条日志——生产环境会封装成函数调用
mylogger.info('foorbar')
mylogger.debug('just a test ')
$ python demo.py
2015-10-30 15:44:01,722 - mylogger - test1.py- INFO - foorbar
2015-10-30 15:44:01,723 - mylogger - test1.py- DEBUG - just a test
http://www.php101.cn/2015/03/05/Python%E4%B8%AD%E7%9A%84Logging%E7%AE%80%E4%BB%8B/ 精彩实例
三:logging模块的API
1:logging.getLogger([name])
返回一个logger实例,如果没有指定name,返回root logger。只要name相同,返回的logger实例都是同一个而且只有一个,即name和logger实例是一一对
应的。这意味着,无需把logger实例在各个模块中传递。只要知道name,就能得到同一个logger实例
2:logger.setLevel(lvl)——设置logger的level,
level有以下几个级别:
NOTSET < DEBUG < INFO < WARNING < ERROR < CRITICAL
不同日志级别对应的数字对照
级别 数值
CRITICAL 50
ERROR 40
WARNING 30
INFO 20
DEBUG 10
NOTSET 0
如果把logger的级别设置为INFO, 那么小于INFO级别的日志都不输出, 大于等于INFO级别的日志都输出。也就意味着同一个logger实例,如果多个地方调用,会
出现很多重复的日志
logger.debug("debug") # 不输出
logger.info("info") # 输出
logger.warning("warning") # 输出
logger.error("error") # 输出
logger.critical("critical") # 输出
3:logger.addHandler(hd)—— logger雇佣handler来帮它处理日志
handler对象负责发送相关的信息到指定目的地。Python的日志系统有多种Handler可以使用。有些Handler可以把信息输出到控制台,有些Logger可以把信息输
出到文件,还有些 Handler可以把信息发送到网络上。如果觉得不够用,还可以编写自己的Handler。可以通过addHandler()方法添加多个多handler
handler主要有以下几种:
logging.StreamHandler: #日志输出到流即控制台,可以是sys.stderr、sys.stdout
logging.FileHandler: #日志输出到文件
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
由于StreamHandler和FileHandler是常用的日志处理方式,所以直接包含在logging模块中,而其他方式则包含在logging.handlers模块中,
handle常见调用
Handler.setLevel(lel) #指定被处理的信息级别,低于lel级别的信息将被忽略
Handler.setFormatter() #给这个handler选择一个格式
Handler.addFilter(filt)、Handler.removeFilter(filt):#新增或删除一个filter对象
举两个栗子
1: logging内部调用——测试学习即可
cat demo.py
import logging
import logging.handlers
LOG_FILE = 'api.log'
#定义日志文件切割规则最多备份5个日志文件,每个日志文件最大10M
handler = logging.handlers.RotatingFileHandler(LOG_FILE, maxBytes = 10*1024*1024, backupCount = 5)
#handler=logging.handlers.TimedRotatingFileHandler(LOG_FILE, when='midnight') #每天零点切换
#定义日志输出格式
fmt = '%(asctime)s - %(filename)s:%(lineno)s - %(name)s - %(message)s'
formatter = logging.Formatter(fmt) # 实例化formatter
handler.setFormatter(formatter) # 为handler添加formatter
#实例化一个logger对象,并为其绑定handle
mylogger = logging.getLogger('test')
mylogger.addHandler(handler) # 为mylogger添加handler
mylogger.setLevel(logging.DEBUG) # 为mylogger设置输出级别
#调用mylogger
mylogger.info('first info message')
mylogger.debug('first debug message')
2: logging封装为函数——适用生产环境中
cat util.py
#/usr/bin/env python
#coding:utf-8
import logging,logging.handlers
def WriteLog(log_name):
log_filename = "/tmp/test.log"
log_level = logging.DEBUG
format = logging.Formatter('%(asctime)s %(filename)s [line:%(lineno)2d]-%(funcName)s %(levelname)s %(message)s')
handler = logging.handlers.RotatingFileHandler(log_filename, mode='a', maxBytes=10*1024*1024, backupCount=5)
handler.setFormatter(format)
logger = logging.getLogger(log_name)
logger.addHandler(handler)
logger.setLevel(log_level)
return logger #函数最终将实例化的logger对象返回,后面直接调用即可
if __name__ == "__main__":
WriteLog('api').info('123') #模块内部直接调用函数。等价下面两行,下面的方法不推荐
# writelog = WriteLog('api')
# writelog.info('123')
'''
外面程序调用函数
cat test.py
import util
def index()
util.WriteLog('api').info('123')
index()
'''
4:logging.basicConfig([**kwargs])——加载logger的各项配置参数,不好用
cat test.py
import logging
logging.basicConfig(level=logging.DEBUG, #输出debug及其级别更高级别的日志
format='%(asctime)s %(filename)s [line:%(lineno)d] %(levelname)s %(message)s',
datefmt='%d %b %Y %H:%M:%S',
filename='myapp.log', #日志文件输出的文件地址,不写默认打印到桌面
filemde='w')
logging.debug("this is debug message")
logging.info("this is info message")
logging.warning("this is warning message")
tail -f myapp.log
30 Oct 2015 14:18:51 test.py [line:8] DEBUG this is debug message
30 Oct 2015 14:18:51 test.py [line:9] INFO this is info message
30 Oct 2015 14:18:51 test.py [line:10] WARNING this is warning message
关于logging.basicConfig函数的常用配置:
filename: 指定日志文件名
filemode: 和file函数意义相同,指定日志文件的打开模式,'w'或'a'
datefmt: 指定时间格式,同time.strftime()
level: 设置日志级别,默认为logging.WARNING,即warning及级别更高的日志才输出
stream: 指定将日志的输出流,可以指定输出到sys.stderr,sys.stdout或者文件,默认输出到sys.stderr,当stream和filename同时指定时,stream被
忽略
format: 指定输出的格式和内容,format可以输出很多有用信息,如上例所示:
%(name)s 打印logger名,默认为root
%(levelno)s: 打印日志级别的数值
%(levelname)s: 打印日志级别名称
%(pathname)s: 打印当前执行程序的路径,其实就是sys.argv[0]
%(filename)s: 打印当前执行程序名
%(funcName)s: 打印日志的当前函数
%(lineno)d: 打印日志的当前行号
%(asctime)s: 打印日志的时间
%(message)s: 打印日志信息
%(thread)d: 打印线程ID
%(threadName)s: 打印线程名称
%(process)d: 打印进程ID
5: logging.config模块通过配置文件的方式,加载logger的参数——最好用的方式
cat logger.conf
# 定义logger模块,root是父类,必需存在的,其它的是自定义。
# logging.getLogger(NAME)就相当于向logging模块注册了实例化了
# name 中用 . 表示 log 的继承关系
[loggers]
keys=root,example01,example02
# [logger_xxxx] logger_模块名称
# level 级别,级别有DEBUG、INFO、WARNING、ERROR、CRITICAL
# handlers 处理类,可以有多个,用逗号分开
# qualname logger名称,应用程序通过 logging.getLogger获取。对于不能获取的名称,则记录到root模块。
# propagate 是否继承父类的log信息,0:否 1:是
[logger_root]
level=DEBUG
handlers=hand01,hand02
[logger_example01]
handlers=hand01,hand02
qualname=example01
propagate=0
[logger_example02]
handlers=hand01,hand03
qualname=example02
propagate=0
# [handler_xxxx]
# class handler类名
# level 日志级别
# formatter,上面定义的formatter
# args handler初始化函数参数
[handlers]
keys=hand01,hand02,hand03
[handler_hand01]
class=StreamHandler
level=INFO
formatter=form02
args=(sys.stderr,)
[handler_hand02]
class=FileHandler
level=DEBUG
formatter=form01
args=('myapp.log', 'a')
[handler_hand03]
class=handlers.RotatingFileHandler
level=INFO
formatter=form02
args=('myapp.log', 'a', 10*1024*1024, 5)
# 日志格式
[formatters]
keys=form01,form02
[formatter_form01]
format=%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s
datefmt=%a, %d %b %Y %H:%M:%S
[formatter_form02]
format=%(asctime)s%(name)-12s: %(levelname)-8s %(message)s
datefmt=%a, %d %b %Y %H:%M:%S
调用
import logging
import logging.config
logging.config.fileConfig("logger.conf")
logger = logging.getLogger("example01")
logger.debug('This is debug message')
logger.info('This is info message')
logger.warning('This is warning message')
生产环境中使用案例——通过函数定义调用
A:定义到工具模块中
cat util.py
import logging,
import logging.config
def write_log(loggername):
work_dir = os.path.dirname(os.path.realpath(__file__))
log_conf= os.path.join(work_dir, 'conf/logger.conf')
logging.config.fileConfig(log_conf)
logger = logging.getLogger(loggername)
return logger
B: 外部模块调用
cat test.py
import util
util.write_log('api').info('just a test')
四: 关于root logger以及logger的父子关系
logger实例之间还有父子关系, root logger就是处于最顶层的logger, 它是所有logger的祖先。如下图:
如何得到root logger
root logger是默认的logger如果不创建logger实例, 直接调用logging.debug()、logging.info()logging.warning(),logging.error()、logging.critical()这
些函数,
那么使用的logger就是 root logger, 它可以自动创建,也是单实例的。
root logger的日志级别
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处理,
eg:
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')
输出:
2011-08-31 21:04:29,893 - foo #父亲处理
2011-08-31 21:04:29,893 - DEBUG - foo #祖先处理
可见, 孩子logger没有任何handler,所以对消息不做处理。但是它把消息转发给了它的父亲以及root logger。最后输出两条日志。
这也就出现了一个问题,同一条日志会重复输出
解决方案
1:每个logger实例都给一个独立的名字,输出之间互不影响,
2: logging.conf中定义不继承