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/22 import logging logging.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) break if __name__ == '__main__': login()
終端驗證
Input user name:andy Input password:nopasswd Process 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/22 import logging import os log_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) break if __name__ == '__main__': login()
運行:
終端輸出:
Input user name:andy Input password:andy 2017-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/25 import logging import logging.config logging.config.fileConfig('logging.conf') # create logger logger = logging.getLogger('simpleExample') # 'application' code logger.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=DEBUG handlers=consoleHandler [logger_simpleExample] level=INFO handlers=simpleHandler qualname=simpleExample propagate=1 [handler_consoleHandler] class=StreamHandler level=DEBUG formatter=consoleFormatter args=(sys.stdout,) [handler_simpleHandler] class=FileHandler level=INFO formatter=simpleFormatter args=('simple.log','w') [formatter_consoleFormatter] format=%(levelname)s :%(message)s [formatter_simpleFormatter] format=%(asctime)s - %(name)s - %(levelname)s - %(message)s datefmt=
之前的例子沒已經刪除了,找到原因:propagate=1 ,只有當propagate 為True時,logger的信息才能傳到上一級logger,或者說父logger,如果你設置為0
那么,就只有文件中有寫入,而終端不會有信息打印出來。