在我们日常的程序开发过程中免不了调试,输出调试日志到控制台或者输出到文件。
Python的logging模块提供了通用的日志系统,熟练使用logging模块可以方便开发者开发第三方模块或者是自己的Python应用。同样这个模块提供不同的日志级别,并可以采用不同的方式记录日志,比如文件,HTTP、GET/POST,SMTP,Socket等,甚至可以自己实现具体的日志记录方式。
Python中的logging模块与Java/Android中的log4j的机制是一样的,只是具体的实现细节不同。logging模块提供logger,handler,filter,formatter。与log4j类似,logger,handler和日志消息的调用可以有具体的日志级别(Level),只有在日志消息的级别大于logger和handler的级别。
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的构造方法需要两个参数:消息的格式字符串和日期字符串,这两个参数都是可选的。
#-*- coding:utf-8 -*-
import logging
import logging.handlers
class Logger():
def __init__(self, tag="", filename="python.log", loglevel=1, toConsole=True, toFile=True):
'''
既要把日志输出到控制台, 还要写入日志文件
'''
# 创建一个logger
# 返回一个logger实例,如果没有指定name,返回root logger。
# 只要name相同,返回的logger实例都是同一个而且只有一个,即name和logger实例是一一对应的。
# 这意味着,无需把logger实例在各个模块中传递。只要知道name,就能得到同一个logger实例。
# tag就是Logger的名字,相当于Android中的TAG
self.logger = logging.getLogger(tag)
# 防止重复记录日志的问题
if not self.logger.handlers:
# 定义handler的输出格式:
# [时间][代码文件名,函数名:行号][logger名(相当于Android中的TAG)]: 输出日志信息
format_dict = {
1 : logging.Formatter('[%(asctime)s][%(filename)s,%(funcName)s:%(lineno)s][%(name)s]: %(message)s'),
2 : logging.Formatter('[%(asctime)s][%(filename)s,%(funcName)s:%(lineno)s][%(name)s]: %(message)s'),
3 : logging.Formatter('[%(asctime)s][%(filename)s,%(funcName)s:%(lineno)s][%(name)s]: %(message)s'),
4 : logging.Formatter('[%(asctime)s][%(filename)s,%(funcName)s:%(lineno)s][%(name)s]: %(message)s'),
5 : logging.Formatter('[%(asctime)s][%(filename)s,%(funcName)s:%(lineno)s][%(name)s]: %(message)s')
}
formatter = format_dict[int(loglevel)]
# 指定最低的日志级别,低于指定级别的将被忽略。
# 级别高低顺序:NOTSET < DEBUG < INFO < WARNING < ERROR < CRITICAL
# 如果把looger的级别设置为INFO, 那么小于INFO级别的日志都不输出, 大于等于INFO级别的日志都输出
self.logger.setLevel(logging.DEBUG)
# 创建一个handler,用于写入日志文件
# 需要创建/写文件的权限
try:
if toFile:
fh = logging.FileHandler(filename)
fh.setLevel(logging.DEBUG)
fh.setFormatter(formatter)
self.logger.addHandler(fh)
except Exception as e:
print e
# 再创建一个handler,用于输出到控制台
if toConsole:
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
ch.setFormatter(formatter)
self.logger.addHandler(ch)
def getlogger(self):
return self.logger
def func():
Logger(tag="test").getlogger().debug("HHHHHHHHHHHHHHHHHHHHHHHHHHH")
if __name__ == "__main__":
Logger(tag="test").getlogger().debug("YYYYYYYYYYYYYYYYYYYYYYY")
func()
输出到控制台:
user@linux:~/share/test$ sudo python logger.py
[2017-05-15 10:06:15,533][logger.py,<module>:65][test]: YYYYYYYYYYYYYYYYYYYYYYY
[2017-05-15 10:06:15,534][logger.py,func:61][test]: HHHHHHHHHHHHHHHHHHHHHHHHHHH
输出到文件:
[2017-05-15 10:06:15,533][logger.py,<module>:65][test]: YYYYYYYYYYYYYYYYYYYYYYY
[2017-05-15 10:06:15,534][logger.py,func:61][test]: HHHHHHHHHHHHHHHHHHHHHHHHHHH
当然,也可以不用loggin也同样能编写日志输出系统:
#-*- coding:utf-8 -*-
import sys
import time
def logOut(msg, tag="", filename="python.log", toConsole=True, toFile=True):
str_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time.time())) # 获得当前时间
fname = sys._getframe().f_back.f_code.co_filename # 调用者文件名
funcname = sys._getframe().f_back.f_code.co_name # 调用者函数名
lineno = sys._getframe().f_back.f_lineno # 调用者所在行号
info = str.format("[%s][%s,%s:%d][%s] %s" % (str_time, fname, funcname, lineno, tag, msg))
if toConsole:
print info
if toFile:
try:
with open(filename, "a") as f:
f.write(info);
f.write("\n");
except Exception as e:
print e
def func():
logOut("HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH", tag="test")
if __name__ == "__main__":
logOut("YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY", tag="test")
func()
输出效果是一样的。