logging 滿好用的,再也不需要用到 print 改用 logging 即可,還可以寫一份進 file 裡,超方便。
Python 3 官方教學:
https://docs.python.org/3/library/logging.html
Python 2 官方教學:
https://docs.python.org/2/howto/logging.html
Python 3 和 2 用法有小小的不一樣,結果也不同,用 3 寫法在 2 裡,會只有 console 有資料,file 是空的。
logging 模組預先定義了如下 6 種安全等級常數以及對應之日誌輸出函數,預設是在 WARNING 等級,所以使用 .info() 方法來輸出時,預設是看不到東西的,請降低 level=logging.INFO ,如此才能看到 .info() 輸出的訊息。
我用的範例:
import datetime
log_filename = datetime.datetime.now().strftime("log/tk%Y-%m-%d_%H_%M_%S.log")
logging.basicConfig(level=logging.DEBUG,
format='%(asctime)s %(name)-12s %(levelname)-8s %(message)s',
datefmt='%m-%d %H:%M:%S',
filename=log_filename)
# 定義 handler 輸出 sys.stderr
console = logging.StreamHandler()
console.setLevel(logging.INFO)
# 設定輸出格式
formatter = logging.Formatter('%(name)-12s: %(levelname)-8s %(message)s')
# handler 設定輸出格式
console.setFormatter(formatter)
# 加入 hander 到 root logger
logging.getLogger('').addHandler(console)
網友的範例:
- http://zwindr.blogspot.tw/2016/08/python-logging.html
- http://emineminschang.blogspot.tw/2016/08/logging-python-logging-module.html
1、模塊級函數
logging.getLogger([name]):返回一個logger對象,如果沒有指定名字將返回root logger
logging.debug、logging.info、logging.warning、logging.error、logging.critical:設定root logger的日誌級別
logging.basicConfig:用默認Formatter為日誌系統建立一個StreamHandler,設置基礎配置並加到root logger中
2、Logger每個程序在輸出信息之前都要獲得一個Logger。Logger通常對應了程序的模塊名,比如聊天工具的圖形介面模塊可以這樣獲得它的Logger:
LOG=logging.getLogger("chat.gui")
而核心模塊可以這樣:
LOG=logging.getLogger("chat.kernel")
Logger.setLevel(lel):指定最低的日誌級別,低於lel的級別將被忽略。debug是最低的內置級別,critical為最高
Logger.addFilter(filt)、Logger.removeFilter(filt):添加或刪除指定的filter
Logger.addHandler(hdlr)、Logger.removeHandler(hdlr):增加或刪除指定的handler
要把 log 印出來,有一些 method 可以用,每個 method 代表不同的嚴重程度。
* logging.debug() * logging.info() * logging.warning() * logging.error() * logging.critical()
Logger.debug、Logger.info、Logger.warning、Logger.error、Logger.critical:可以設置的日誌級別
NOTSET < DEBUG < INFO < WARNING < ERROR < CRITICAL
如果把looger的級別設置為INFO, 那麼小於INFO級別的日誌都不輸出,大於等於INFO級別的日誌都輸出
個人的使用情況是,在開發的時候使用 DEBUG level,等到上 production 的時候,會把 level 設成 WARNING。這樣有個好處是,我不用修改任何 code 就可以把一些 debug 資訊都隱藏起來,比起用 print 大法,再一個一個找出來刪掉來要來得方便。
3、Handlers
handler對象負責發送相關的信息到指定目的地。Python的日誌系統有多種Handler可以使用。有些Handler可以把信息輸出到控制台,有些Logger可以把信息輸出到文件,還有些 Handler可以把信息發送到網絡上。如果覺得不夠用,還可以編寫自己的Handler。可以通過addHandler方法添加多個多handler
Handler.setLevel(lel):指定被處理的信息級別,低於lel級別的信息將被忽略
Handler.setFormatter:給這個handler選擇一個格式
Handler.addFilter(filt)、Handler.removeFilter(filt):新增或刪除一個filter對象
4、Formatters
Formatter對象設置日誌信息最後的規則、結構和內容,默認的時間格式為%Y-%m-%d %H:%M:%S,下面是Formatter常用的一些信息
%(name)s | Logger的名字 |
%(levelno)s | 數字形式的日誌級別 |
%(levelname)s | 文本形式的日誌級別 |
%(pathname)s | 調用日誌輸出函數的模塊的完整路徑名,可能沒有 |
%(filename)s | 調用日誌輸出函數的模塊的文件名 |
%(module)s | 調用日誌輸出函數的模塊名 |
%(funcName)s | 調用日誌輸出函數的函數名 |
%(lineno)d | 調用日誌輸出函數的語句所在的代碼行 |
%(created)f | 當前時間,用UNIX標準的表示時間的浮 點數表示 |
%(relativeCreated)d | 輸出日誌信息時的,自Logger創建以 來的毫秒數 |
%(asctime)s | 字符串形式的當前時間。默認格式是 “2003-07-08 16:49:45,896″。逗號後面的是毫秒 |
%(thread)d | 線程ID。可能沒有 |
%(threadName)s | 線程名。可能沒有 |
%(process)d | 進程ID。可能沒有 |
%(message)s | 用戶輸出的消息 |
5、設置過濾器
細心的朋友一定會發現前文調用logging.getLogger時參數的格式類似於”A.B.C”。採取這樣的格式其實就是為了可以配置過濾器。看一下這段代碼:
LOG=logging.getLogger(“chat.gui.statistic”)
console = logging.StreamHandler
console.setLevel(logging.INFO)
formatter = logging.Formatter(』%(asctime)s %(levelname)s %(message)s』)
console.setFormatter(formatter)
filter=logging.Filter(“chat.gui”)
console.addFilter(filter)
LOG.addHandler(console)
和前面不同的是我們在Handler上添加了一個過濾器。現在我們輸出日誌信息的時候就會經過過濾器的處理。名為”A.B”的過濾器只讓名字帶有 “A.B”前綴的Logger輸出信息。可以添加多個過濾器,只要有一個過濾器拒絕,日誌信息就不會被輸出。另外,在Logger中也可以添加過濾器。
每個Logger可以附加多個Handler。接下來我們就來介紹一些常用的Handler:
1) logging.StreamHandler
使用這個Handler可以向類似與sys.stdout或者sys.stderr的任何文件對象(file object)輸出信息。它的構造函數是:
StreamHandler([strm])
其中strm參數是一個文件對象。默認是sys.stderr
2) logging.FileHandler
和StreamHandler類似,用於向一個文件輸出日誌信息。不過FileHandler會幫你打開這個文件。它的構造函數是:
FileHandler(filename[,mode])
filename是文件名,必須指定一個文件名。
mode是文件的打開方式。參見Python內置函數open的用法。默認是』a’,即添加到文件末尾。
3) logging.handlers.RotatingFileHandler
這個Handler類似於上面的FileHandler,但是它可以管理文件大小。當文件達到一定大小之後,它會自動將當前日誌文件改名,然後創建 一個新的同名日誌文件繼續輸出。比如日誌文件是chat.log。當chat.log達到指定的大小之後,RotatingFileHandler自動把 文件改名為chat.log.1。不過,如果chat.log.1已經存在,會先把chat.log.1重命名為chat.log.2。。。最後重新創建 chat.log,繼續輸出日誌信息。它的構造函數是:
RotatingFileHandler( filename[, mode[, maxBytes[, backupCount]]])
其中filename和mode兩個參數和FileHandler一樣。
maxBytes用於指定日誌文件的最大文件大小。如果maxBytes為0,意味著日誌文件可以無限大,這時上面描述的重命名過程就不會發生。
backupCount用於指定保留的備份文件的個數。比如,如果指定為2,當上面描述的重命名過程發生時,原有的chat.log.2並不會被更名,而是被刪除。
4) logging.handlers.TimedRotatingFileHandler
這個Handler和RotatingFileHandler類似,不過,它沒有通過判斷文件大小來決定何時重新創建日誌文件,而是間隔一定時間就 自動創建新的日誌文件。重命名的過程與RotatingFileHandler類似,不過新的文件不是附加數字,而是當前時間。它的構造函數是:
TimedRotatingFileHandler( filename [,when [,interval [,backupCount]]])
其中filename參數和backupCount參數和RotatingFileHandler具有相同的意義。
interval是時間間隔。
when參數是一個字符串。表示時間間隔的單位,不區分大小寫。它有以下取值:
S 秒
M 分
H 小時
D 天
W 每星期(interval==0時代表星期一)
midnight 每天凌晨
5) logging.handlers.SocketHandler
6) logging.handlers.DatagramHandler
以上兩個Handler類似,都是將日誌信息發送到網絡。不同的是前者使用TCP協議,後者使用UDP協議。它們的構造函數是:
Handler(host, port)
其中host是主機名,port是埠名
7) logging.handlers.SysLogHandler
8) logging.handlers.NTEventLogHandler
9) logging.handlers.SMTPHandler
10) logging.handlers.MemoryHandler
11) logging.handlers.HTTPHandler
最後舉個例子吧:#FORMAT = '%(asctime)-15s %(clientip)s %(user)-8s %(message)s'
#logging.basicConfig(format=FORMAT)
#d = {'clientip': '192.168.0.1', 'user': 'fbloggs'}
#logger = logging.getLogger('tcpserver')
#logger.warning('Protocol problem: %s', 'connection reset', extra=d)
#FORMAT = '%(asctime)-15s %(message)s'
#logging.basicConfig(filename = "C:\\Users\\june\\Desktop\\1.txt", level = logging.DEBUG, filemode = "a", format=FORMAT)
#logging.debug('this is a message')
#logging.debug('test')
#import logging
#import datetime
#
#curDate = datetime.date.today - datetime.timedelta(days=0)
#logName = 'C:\\Users\\june\\Desktop\\error_%s.log' %curDate
#
#logging.basicConfig(level=logging.INFO,
# format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s',
# #datefmt='%a, %d %b %Y %H:%M:%S',
# filename=logName,
# filemode='a')
#
##2013-10-21 03:25:51,509 writeLog.py[line:14] INFO This is info message
##2013-10-21 03:25:51,510 writeLog.py[line:15] WARNING This is warning message
#logging.debug('This is debug message')
#logging.info('This is info message')
#logging.warning('This is warning message')
<
/
div>
import
logging
import
logging.config
logging.config.fileConfig(
"logging.conf"
)
#create logger
loggerInfo
=
logging.getLogger(
"infoLogger"
)
#"application" code
loggerInfo.debug(
"debug message"
)
loggerInfo.info(
"info message"
)
loggerInfo.warn(
"warn message"
)
loggerInfo.error(
"error message"
)
loggerInfo.critical(
"critical message"
)
# 定義logger模塊,root是父類,必需存在的,其它的是自定義。
# logging.getLogger(NAME)便相當於向logging模塊註冊了一種日誌列印
# name 中用 . 表示 log 的繼承關係
[loggers]
keys=root,infoLogger,errorLogger
# 定義handler
[handlers]
keys=infoHandler,errorHandler
# 定義格式化輸出
[formatters]
keys=infoFmt,errorFmt
#
# 實現上面定義的logger模塊,必需是[logger_xxxx]這樣的形式
# [logger_xxxx] logger_模塊名稱
# level 級別,級別有DEBUG、INFO、WARNING、ERROR、CRITICAL
# handlers 處理類,可以有多個,用逗號分開
# qualname logger名稱,應用程式通過 logging.getLogger獲取。對於不能獲取的名稱,則記錄到root模塊。
# propagate 是否繼承父類的log信息,0:否 1:是
[logger_root]
level=INFO
handlers=errorHandler
#
# handler
# [handler_xxxx]
# class handler類名
# level 日誌級別
# formatter,上面定義的formatter
# args handler初始化函數參數
[handler_infoHandler]
class=StreamHandler
level=INFO
formatter=infoFmt
args=(sys.stdout,)
[handler_errorHandler]
class=logging.handlers.TimedRotatingFileHandler
level=ERROR
formatter=errorFmt
# When computing the next rollover time for the first time (when the handler is created),
# the last modification time of an existing log file, or else the current time,
# is used to compute when the next rotation will occur.
# 這個功能太雞肋了,是從handler被創建的時間算起,不能按自然時間 rotation 切分,除非程序一直運行,否則這個功能會有問題
# 臨時解決方案參考下面的連結:Python 多進程日誌記錄
# http://blogread.cn/it/article/4175?f=wb2
args=(
'C:\\Users\\june\\Desktop\\error.log'
'M'
, 1, 5)
#
# 日誌格式
# %(asctime)s 年-月-日 時-分-秒,毫秒 2013-04-26 20:10:43,745
# %(filename)s 文件名,不含目錄
# %(pathname)s 目錄名,完整路徑
# %(funcName)s 函數名
# %(levelname)s 級別名
# %(lineno)d 行號
# %(module)s 模塊名
# %(message)s 消息體
# %(name)s 日誌模塊名
# %(process)d 進程id
# %(processName)s 進程名
# %(thread)d 線程id
# %(threadName)s 線程名
[formatter_infoFmt]
format
=%(asctime)s %(levelname)s %(message)s
datefmt=
class=logging.Formatter
[formatter_errorFmt]
#coding=utf-8
import
logging
import
datetime
format
=
'%(asctime)s - %(filename)s - [line:%(lineno)d] - %(levelname)s - %(message)s'
curDate
=
datetime.date.today
-
datetime.timedelta(days
=
0
)
infoLogName
=
r
'C:/Users/june/Desktop/info_%s.log'
%
curDate
errorLogName
=
r
'C:/Users/june/Desktop/error_%s.log'
%
curDate
formatter
=
logging.Formatter(
format
)
infoLogger
=
logging.getLogger(
"infoLog"
)
errorLogger
=
logging.getLogger(
"errorLog"
)
infoLogger.setLevel(logging.INFO)
errorLogger.setLevel(logging.ERROR)
infoHandler
=
logging.FileHandler(infoLogName,
'a'
)
infoHandler.setLevel(logging.INFO)
infoHandler.setFormatter(formatter)
errorHandler
=
logging.FileHandler(errorLogName,
'a'
)
errorHandler.setLevel(logging.ERROR)
errorHandler.setFormatter(formatter)
testHandler
=
logging.StreamHandler
testHandler.setFormatter(formatter)
testHandler.setLevel(logging.ERROR)
infoLogger.addHandler(infoHandler)
infoLogger.addHandler(testHandler)
errorLogger.addHandler(errorHandler)
#infoLogger.debug("debug message")
#infoLogger.info("info message")
#infoLogger.warn("warn message")
# # 下面這行會同時列印在文件和終端上
#infoLogger.error("error message")
#
#errorLogger.error("error message")
#errorLogger.critical("critical message")
謝謝分享!
寫得很清楚,謝謝
不客氣