【4】进程管理-9-日志记录--logging

Python的logging模块提供了通用的日志系统,可以方便第三方模块或者是应用使用。这个模块提供不同的日志级别,并可以采用不同的方式记录日志,比如文件,HTTP GET/POST,SMTP,Socket等,甚至可以自己实现具体的日志记录方式。

简单说明

logging模块与log4j的机制是一样的,只是具体的实现细节不同。模块提供logger,handler,filter,formatter。

logger:提供日志接口,供应用代码使用。logger最长用的操作有两类:配置和发送日志消息。可以通过logging.getLogger(name)获取logger对象,如果不指定name则返回root对象,多次使用相同的name调用getLogger方法返回同一个logger对象。

handler:将日志记录(log record)发送到合适的目的地(destination),比如文件,socket等。一个logger对象可以通过addHandler方法添加0到多个handler,每个handler又可以定义不同日志级别,以实现日志分级过滤显示。

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

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

与log4j类似,logger,handler和日志消息的调用可以有具体的日志级别(Level),只有在日志消息的级别大于logger和handler的级别。

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

import logging

logging.debug('This is debug message')
logging.info('This is info message')
logging.warning('This is warning message')
 
屏幕上打印:
WARNING:root:This is warning message

默认情况下,logging将日志打印到屏幕,日志级别为WARNING; 日志级别大小关系为:CRITICAL > ERROR > WARNING > INFO > DEBUG > NOTSET,当然也可以自己定义日志级别。级别越大越打印在屏幕上额。

2.通过logging.basicConfig函数对日志的输出格式及方式做相关配置

import logging

logging.basicConfig(level=logging.DEBUG,
				format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s',
				datefmt='%a, %d %b %Y %H:%M:%S',
				filename='myapp.log',
				filemode='w')
	
logging.debug('This is debug message')
logging.info('This is info message')
logging.warning('This is warning message')
 
./myapp.log文件中内容为:
Sun, 24 May 2009 21:48:54 demo2.py[line:11] DEBUG This is debug message
Sun, 24 May 2009 21:48:54 demo2.py[line:12] INFO This is info message
Sun, 24 May 2009 21:48:54 demo2.py[line:13] WARNING This is warning message

logging.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被忽略

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

import logging

logging.basicConfig(level=logging.DEBUG,
				format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s',
				datefmt='%a, %d %b %Y %H:%M:%S',
				filename='myapp.log',
				filemode='w')

#################################################################################################
#定义一个StreamHandler,将INFO级别或更高的日志信息打印到标准错误,并将其添加到当前的日志处理对象#
console = logging.StreamHandler()
console.setLevel(logging.INFO)
formatter = logging.Formatter('%(name)-12s: %(levelname)-8s %(message)s')
console.setFormatter(formatter)
logging.getLogger('').addHandler(console)
#################################################################################################

logging.debug('This is debug message')
logging.info('This is info message')
logging.warning('This is warning message')
 
屏幕上打印:
root        : INFO     This is info message
root        : WARNING  This is warning message
./myapp.log文件中内容为:
Sun, 24 May 2009 21:48:54 demo2.py[line:11] DEBUG This is debug message
Sun, 24 May 2009 21:48:54 demo2.py[line:12] INFO This is info message
Sun, 24 May 2009 21:48:54 demo2.py[line:13] WARNING This is warning message

4.logging之日志回滚

import logging
from logging.handlers import RotatingFileHandler

#################################################################################################
#定义一个RotatingFileHandler,最多备份5个日志文件,每个日志文件最大10M
Rthandler = RotatingFileHandler('myapp.log', maxBytes=10*1024*1024,backupCount=5)
Rthandler.setLevel(logging.INFO)
formatter = logging.Formatter('%(name)-12s: %(levelname)-8s %(message)s')
Rthandler.setFormatter(formatter)
logging.getLogger('').addHandler(Rthandler)
################################################################################################

从上例和本例可以看出,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模块中, 上述其它处理方式的使用请参见python2.5手册!

例子:looger既写到文件中,也输出到shell,也给发邮件

import logging

ADMINS = ['txxx@sina.com']
from logging.handlers import SMTPHandler

logger = logging.getLogger('genoprime-log')
logger.setLevel(logging.INFO)

file_handler = logging.FileHandler("log/genoprime.log")
file_handler.setLevel(logging.DEBUG)

shell_handler = logging.StreamHandler()
shell_handler.setLevel(logging.DEBUG)
mail_handler = SMTPHandler('smtp.qiye.163.com',
							   'xxx@gsxx.com', ADMINS, "subject id Log",
							   credentials=("xxx@gsxx.com", "邮箱密码"), secure=()
							   )
mail_handler.setLevel(logging.DEBUG)

formatter = logging.Formatter('%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s')
file_handler.setFormatter(formatter)
shell_handler.setFormatter(formatter)
mail_handler.setFormatter(formatter)

logger.addHandler(file_handler)
logger.addHandler(mail_handler)
logger.addHandler(shell_handler)

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

#logger.conf
###############################################
[loggers]
keys=root,example01,example02
[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
###############################################
[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=%(name)-12s: %(levelname)-8s %(message)s
datefmt=

例3:

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

例4:

import logging
import logging.config

logging.config.fileConfig("logger.conf")
logger = logging.getLogger("example02")

logger.debug('This is debug message')
logger.info('This is info message')
logger.warning('This is warning message')

6.发送邮件

import logging
ADMINS = ['收件人邮箱1','收件人邮箱2']
from logging.handlers import SMTPHandler

class EncodingFormatter(logging.Formatter):
	def __init__(self, fmt, datafmt=None, encoding=None):
		logging.Formatter.__init__(self, fmt, datafmt)
		self.encoding = encoding

	def format(self, record):
		result = logging.Formatter.format(self, record)
		if isinstance(result, unicode):
			result = result.encode(self.encoding or 'utf-8')
		return result

mail_handler = SMTPHandler('smtp.qiye.163.com',
						   'xxx@gsxx.com', ADMINS, "subject id Log",
						   credentials=("xxx@gsxx.com", "邮箱密码"), secure=()
						   )
mail_handler.setLevel(logging.ERROR)
errlog = logging.getLogger()
errlog.addHandler(mail_handler)

mail_handler.setFormatter(EncodingFormatter('%(message)s',encoding='utf-8'))
errlog.error('ddd')

或者加入记录信息,但是这个时候就不支持中文咯
formatter = logging.Formatter('%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s   %(message)s')
mail_handler.setFormatter(formatter)
errlog.error('ddd')

记录到文件里

logging.basicConfig(level=logging.DEBUG,
					format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s',
					datafmt='%a,%d %b %Y %H:%M:%S',
					filename='log/genoprime.log',
					filemod='w')

logging.debug("THis is debug message!")
logging.warning("This is warning message")
logging.error("This is error message")

我采取的方式

import logging
logger = logging.getLogger('genoprime-log')
logger.setLevel(logging.INFO)

file_handler = logging.FileHandler('genoprime.log')
#file_handler = logging.FileHandler('log/genoprime.log') 运行程序前必须保证有Log文件夹的存在,而我是运行程序后生成log,所以这一步老是报错
file_handler.setLevel(logging.DEBUG)
shell_handler = logging.StreamHandler()
shell_handler.setLevel(logging.INFO)
formatter = \
logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')

file_handler.setFormatter(formatter)
shell_handler.setFormatter(formatter)

logger.addHandler(file_handler)
logger.addHandler(shell_handler)

参考资料

药企,独角兽,苏州。团队长期招人,感兴趣的都可以发邮件聊聊:tiehan@sina.cn
个人公众号,比较懒,很少更新,可以在上面提问题,如果回复不及时,可发邮件给我: tiehan@sina.cn