https://docs.python.org/3.5/library/logging.html,先3.5是因为我当前的python 版本是3.5
之所以要来详细的写是因为之前学django时也有这个,不是很理解,所以这里就多了解下。
写到后面发现整个文章一点条理都没有,但由于内容比较多,就不重新整理了
logging框架中主要由四个部分组成:
Loggers expose the interface that application code directly uses. Handlers send the log records (created by loggers) to the appropriate destination. Filters provide a finer grained facility for determining which log records to output. Formatters specify the layout of log records in the final output.
- Loggers: 提供应用直接调用的接口
- Handlers: 决定将日志记录发送至正确的目的地
- Filters: 提供更精细的决定哪些日志输出的能力,简单点说就是决定哪些输出哪些不输出
- Formatters: 制定最终输出的格式。
logger对象
事实上logger是不直接实例化的,但是我们可以通过模块级别的函数logging.getLogger(name)
来调用。如果多次通过同一个名字调用getLogger()将只能得到对同一个对象的引用。
注意: logging.getLogger(name) 中的name是一个支持通过“.”(点号)分层级的值。
例如: 你定义一个logger: foo,那么foo.bar, foo.baz,就都算作它的子代,就是继承,有父子关系。
logger名字的层级与python中包的层级是一致的。所以为了区分它,如果你基于模块来组织logger,
那么建议你使用:logging.getLogger(__name__)
这种形式,因为对于模块而言,在python
包的命名空间中__name__的值就是模块的名字。
常用方法:
Logger.
setLevel
(lvl):
将logger的门限设置为lvl,低于此等级的信息将被忽略,当一个Logger被创造出来时,它的等级被设置为:
NOTSET(未设置) 这样的结果就是: 如果它是一个root logger,它将处理所有的信息,如果没有root logger
它将继承父级的logger等级。Note: root logger可通过level waring来创建,注意这里是个坑,
事实上默认root logger等级为warning,不要搞错了
级别设定:前面是名字,后面是对应的数字值
Logger.
exception
(msg, *args, **kwargs) 以error级别来记录信息,异常信息也会被添加到信息(message),需要exception handler调用才行
Logger.
addFilter
(filt)
添加指定的过滤器
Logger.
removeFilter
(filt)
移除指定的过滤器
这里的方法多得让人想撞死在屏幕上,以后慢慢更新吧。
Handler
- Handler.setLevel(lel):指定被处理的信息级别,低于lel级别的信息将被忽略
- Handler.setFormatter():给这个handler选择一个格式
- Handler.addFilter(filt)、Handler.removeFilter(filt):新增或删除一个filter对象
handler有多达15种,这里只说下常见的几种:
1.logging.StreamHandler
发送信息到流,类文件对象即可,如终端,文件等
2.logging.FileHandler
发送信息到硬盘文件
3.logging.RotatingFileHandler
这个Handler类似于上面的FileHandler,但是它可以管理文件大小,轮询日志文件(不知道是不是检测日志文件大小)。当文件达到一定大小之后,它会自动将当前日志文件改名,然后创建一个新的同名日志文件继续输出。
4.logging.handlers.TimedRotatingFileHandler
这个Handler和RotatingFileHandler类似,不过,它没有通过判断文件大小来决定何时重新创建日志文件,
而是间隔一定时间就 自动创建新的日志文件。
Formatter
Formatters 决定了记录格式。Formatter会将传递来的信息拼接成一条具体的字符串,
默认情况下Format只会将信息%(message)s
直接打印出来。Format中有一些自带的LogRecord属性可以使用,如下表格:
其中lineno,pathname,经过实测了,并不是有些博客里说的什么当前日志记录的行号。后面会有实例。
这里有一个简单的应用 的例子,记录出错信息:
#!/usr/bin/env python#coding:utf-8# Created by Andy @ 2017/6/22import logginglogging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(pathname)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s ', datefmt='%a, %d %b %Y %H:%M:%S', filename=r'D:\Coding\oldboy\day13 module\test.log', filemode='w')def login(): while True: try: name = input('Input user name:') password = int(input('Input password:')) # 这里故意转成整型,触发异常 if name == 'andy' and password == 'nopasswd': print('logging succeed!') except ValueError as e: logging.debug(e) breakif __name__ == '__main__': login()
终端验证
Input user name:andyInput password:nopasswdProcess finished with exit code 0
打开test.log:
Fri, 23 Jun 2017 09:56:40 D:/Coding/oldboy/day13 module/log.py log.py[line:24] DEBUG invalid literal for int() with base 10: 'nopasswd'
注意看,lineno 并不是在日志里面的行号,而是你代码的行号,在24行调用了logging.debug(e)
这里现定义一个一simple_logger:
#!/usr/bin/env python#coding:utf-8# Created by Andy @ 2017/6/22import loggingimport oslog_path = os.path.join(os.getcwd(),'test.log')logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(pathname)s line:%(lineno)d %(message)s ', datefmt='%Y-%m-%d %H:%M:%S', # 将日期格式化成正常模式 filename=log_path, filemode='a') # 指定成追加模式,默认就是追加,所以这句非必须simple_logger = logging.getLogger('simple')fh = logging.StreamHandler() #输出到终端fh.setLevel(level = logging.WARNING) #等级比默认的高,但调用时必须一致formatter = logging.Formatter("%(asctime)s %(message)s %(levelname)s")fh.setFormatter(formatter)simple_logger.addHandler(fh)def login(): while True: try: name = input('Input user name:') password = int(input('Input password:')) # 这里故意转成整型,触发异常 if name == 'andy' and password == 'nopasswd': print('logging succeed!') except ValueError as e: simple_logger.warning(e) breakif __name__ == '__main__': login()
运行:
终端输出:
Input user name:andyInput password:andy2017-06-24 09:55:57,395 invalid literal for int() with base 10: 'andy' WARNING
test.log文件:
2017-06-24 09:55:57 D:/Coding/oldboy/day13 module/log.py line:33 invalid literal for int() with base 10: 'andy'
这晨需要 注意的是:因为默认的logger等级是debug,所以它什么消息都会输出,而simple_logger则只输出比自身高等级的信息,
并且:调用simple_logger时必须比定义的等级高于或者等于定义的等级,这句话什么意思呢?看上面的例子,simple_logger
定义时的等级为warning,那么你调用时最少得warning等级,或者比它高的critical才能正常打印消息(它规定就是这样),否则你是看不到任何效果的
(这里坑了好久,一直没消息打印而找不到原因,root logger默认等级是warning,而非Noset),但是如果你调用的等级比较低,
并不影响root 这个logger打印日志信息。
填坑来了:
如果没指定默认的logger的等级,那么默认的等级warning就会生效,所以才出现上面的情况,如果需要将debug等级的信息也输入,那么,这里需要加一条:
simple_logger.setLevel(logging.DEBUG)# 如果不设置此logger的级别,那么handler的记录是从logger传过来的,也就是默认从logger的warning来的
下面是一个使用配置文件 的例子:
#!/usr/bin/env python#coding:utf-8# Created by Andy @ 2017/6/25import loggingimport logging.configlogging.config.fileConfig('logging.conf')# create loggerlogger = logging.getLogger('simpleExample')# 'application' codelogger.debug('debug message')logger.info('info message')logger.warn('warn message')logger.error('error message')logger.critical('critical message')
配置文件:
[loggers]keys=root,simpleExample[handlers]keys=consoleHandler,simpleHandler[formatters]keys=consoleFormatter,simpleFormatter[logger_root]level=DEBUGhandlers=consoleHandler[logger_simpleExample]level=INFOhandlers=simpleHandlerqualname=simpleExamplepropagate=1[handler_consoleHandler]class=StreamHandlerlevel=DEBUGformatter=consoleFormatterargs=(sys.stdout,)[handler_simpleHandler]class=FileHandlerlevel=INFOformatter=simpleFormatterargs=('simple.log','w')[formatter_consoleFormatter]format=%(levelname)s :%(message)s[formatter_simpleFormatter]format=%(asctime)s - %(name)s - %(levelname)s - %(message)sdatefmt=
之前的例子没已经删除了,找到原因:propagate=1 ,只有当propagate 为True时,logger的信息才能传到上一级logger,或者说父logger,如果你设置为0
那么,就只有文件中有写入,而终端不会有信息打印出来。