在現(xiàn)實(shí)生活中给僵,記錄日志非常重要。銀行轉(zhuǎn)賬時(shí)會(huì)有轉(zhuǎn)賬記錄名党;飛機(jī)飛行過程中,會(huì)有黑盒子(飛行數(shù)據(jù)記錄器)記錄飛行過程中的一切挠轴。如果有出現(xiàn)什么問題传睹,人們可以通過日志數(shù)據(jù)來搞清楚到底發(fā)生了什么。對(duì)于系統(tǒng)開發(fā)岸晦、調(diào)試以及運(yùn)行欧啤,記錄日志都是同樣的重要。如果沒有日志記錄启上,程序崩潰時(shí)你幾乎就沒辦法弄明白到底發(fā)生了什么事情邢隧。舉個(gè)例子,當(dāng)你在寫一個(gè)服務(wù)器程序時(shí)冈在,記錄日志是非常有必要的倒慧。下面展示的就是 EZComet.com 服務(wù)器的日志文件截圖。
服務(wù)崩潰后包券,如果沒有日志纫谅,我?guī)缀鯖]辦法知道到底發(fā)生了錯(cuò)誤。日志不僅對(duì)于服務(wù)器很重要溅固,對(duì)于桌面圖形應(yīng)用同樣十分重要系宜。比如,當(dāng)你的客戶的 PC 機(jī)程序崩潰時(shí)发魄,你可以讓他們把日志文件發(fā)給你盹牧,這樣你就可以找到問題到底出在哪兒。相信我励幼,在不同的 PC 環(huán)境下汰寓,你永遠(yuǎn)不會(huì)知道會(huì)有怎樣奇怪的問題。我曾經(jīng)就接收到過這樣的錯(cuò)誤日志苹粟。
2011-08-22 17:52:54,828 - root - ERROR - [Errno 10104] getaddrinfo failed
Traceback (most recent call last):
File "<string>", line 124, in main
File "<string>", line 20, in __init__
File "h:workspaceprojectbuildpyi.win32mrdjoutPYZ1.pyz/wx._core", line 7978, in __init__
File "h:workspaceprojectbuildpyi.win32mrdjoutPYZ1.pyz/wx._core", line 7552, in _BootstrapApp 7 File "<string>", line 84, in OnInit 8 File "h:workspaceprojectbuildpyi.win32mrdjoutPYZ1.pyz/twisted.internet.wxreactor", line 175, in install 9 File "h:workspaceprojectbuildpyi.win32mrdjoutPYZ1.pyz/twisted.internet._threadedselect", line 106, in __init__
File "h:workspaceprojectbuildpyi.win32mrdjoutPYZ1.pyz/twisted.internet.base", line 488, in __init__
File "h:workspaceprojectbuildpyi.win32mrdjoutPYZ1.pyz/twisted.internet.posixbase", line 266, in installWaker 12 File "h:workspaceprojectbuildpyi.win32mrdjoutPYZ1.pyz/twisted.internet.posixbase", line 74, in __init__
File "h:workspaceprojectbuildpyi.win32mrdjoutPYZ1.pyz/socket", line 224, in meth 14 gaierror: [Errno 10104] getaddrinfo failed</pre>
我最終發(fā)現(xiàn)有滑,這個(gè)客戶的 PC 機(jī)被一種病毒感染,導(dǎo)致了調(diào)用 gethostname 函數(shù)失敗嵌削∶茫看吧,如果沒有日志可以查你怎么可能知道這些苛秕。
打印輸出不是個(gè)好辦法
盡管記錄日志非常重要肌访,但是并不是所有的開發(fā)者都能正確地使用它。我曾看到一些開發(fā)者是這樣記錄日志的艇劫,在開發(fā)的過程中插入 print 語(yǔ)句吼驶,開發(fā)結(jié)束后再將這些語(yǔ)句移除。就像這樣:
print 'Start reading database'
records = model.read_recrods() 3 print '# records', records
print 'Updating record ...'
model.update_records(records) 6 print 'done'</pre>
這種方式對(duì)于簡(jiǎn)單腳本型程序有用,但是如果是復(fù)雜的系統(tǒng)蟹演,你最好不要使用這樣的方式风钻。首先,你沒辦法做到在日志文件中只留下極其重要的消息酒请。你會(huì)看到大量的消息日志骡技。但是你卻找不到任何有用的信息。你除了移除這輸出語(yǔ)句這外羞反,沒別的辦法控制代碼布朦,但是極有可能的是你忘記了移出那些沒用的輸出。再者苟弛,print 輸出的所有信息都到了標(biāo)準(zhǔn)輸出中喝滞,這將嚴(yán)重影響到你從標(biāo)準(zhǔn)輸出中查看其它輸出數(shù)據(jù)阁将。當(dāng)然膏秫,你也可以把消息輸出到 stderr ,但是用 print 做日志記錄的方式還是不好做盅。
使用 python 的標(biāo)準(zhǔn)日志模塊
那么缤削,怎么樣記錄日志才是正確的呢?其實(shí)非常簡(jiǎn)單吹榴,使用 python 的標(biāo)準(zhǔn)日志模塊亭敢。多虧 python 社區(qū)將日志做成了一個(gè)標(biāo)準(zhǔn)模塊。它非常簡(jiǎn)單易用且十分靈活图筹。你可以像這樣使用日志系統(tǒng):
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
logger.info('Start reading database')
# read database here
records = {'john': 55, 'tom': 66}
logger.debug('Records: %s', records) 10 logger.info('Updating records ...')
# update records here
logger.info('Finish updating records')</pre>
運(yùn)行的時(shí)候就可看到:
INFO:__main__:Start reading database
INFO:__main__:Updating records ...
INFO:__main__:Finish updating records</pre>
你可能會(huì)問這與使用 print 有什么不同呢帅刀。它有以下的優(yōu)勢(shì):
- 你可以控制消息的級(jí)別,過濾掉那些并不重要的消息远剩。
- 你可決定輸出到什么地方扣溺,以及怎么輸出。
有許多的重要性別級(jí)可供選擇瓜晤,debug锥余、info、warning痢掠、error 以及 critical驱犹。通過賦予 logger 或者 handler 不同的級(jí)別,你就可以只輸出錯(cuò)誤消息到特定的記錄文件中足画,或者在調(diào)試時(shí)只記錄調(diào)試信息雄驹。讓我們把 logger 的級(jí)別改成 DEBUG 再看一下輸出結(jié)果:
logging.basicConfig(level=logging.DEBUG)</pre>
輸出變成了:
INFO:__main__:Start reading database
DEBUG:__main__:Records: {'john': 55, 'tom': 66}
INFO:__main__:Updating records ...
INFO:__main__:Finish updating records</pre>
正如看到的那樣,我們把 logger 的等級(jí)改為 DEBUG 后淹辞,調(diào)試記錄就出現(xiàn)在了輸出當(dāng)中荠医。你也可以選擇怎么處理這些消息。例如,你可以使用 FileHandler 把記錄寫進(jìn)文件中:
import logging
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
# create a file handler
handler = logging.FileHandler('hello.log')
handler.setLevel(logging.INFO) 10
# create a logging format
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)
# add the handlers to the logger
logger.addHandler(handler) 19
logger.info('Hello baby')</pre>
以合適的等級(jí)輸出日志記錄
有了靈活的日志記錄模塊后彬向,你可以按適當(dāng)?shù)牡燃?jí)將日志記錄輸出到任何地方然后配置它們兼贡。那么你可能會(huì)問,什么是合適的等級(jí)呢娃胆?在這兒我將分享一些我的經(jīng)驗(yàn)遍希。
大多數(shù)的情況下,你都不想閱讀日志中的太多細(xì)節(jié)里烦。因此凿蒜,只有你在調(diào)試過程中才會(huì)使用 DEBUG 等級(jí)。我只使用 DEBUG 獲取詳細(xì)的調(diào)試信息胁黑,特別是當(dāng)數(shù)據(jù)量很大或者頻率很高的時(shí)候废封,比如算法內(nèi)部每個(gè)循環(huán)的中間狀態(tài)。
def complex_algorithm(items):
for i, item in enumerate(items):
# do some complex algorithm computation
logger.debug('%s iteration, item=%s', i, item)
在處理請(qǐng)求或者服務(wù)器狀態(tài)變化等日常事務(wù)中丧蘸,我會(huì)使用 INFO 等級(jí)漂洋。
def handle_request(request):
logger.info('Handling request %s', request)
# handle request here
result = 'result'
logger.info('Return result: %s', result)
def start_service():
logger.info('Starting service at port %s ...', port)
service.start()
logger.info('Service is started')
當(dāng)發(fā)生很重要的事件,但是并不是錯(cuò)誤時(shí)力喷,我會(huì)使用 WARNING 刽漂。比如,當(dāng)用戶登錄密碼錯(cuò)誤時(shí)弟孟,或者連接變慢時(shí)贝咙。
def authenticate(user_name, password, ip_address):
if user_name != USER_NAME and password != PASSWORD:
logger.warn('Login attempt to %s from IP %s', user_name, ip_address)
return False
# do authentication here</pre>
有錯(cuò)誤發(fā)生時(shí)肯定會(huì)使用 ERROR 等級(jí)了。比如拋出異常拂募,IO 操作失敗或者連接問題等庭猩。
def get_user_by_id(user_id):
user = db.read_user(user_id)
if user is None:
logger.error('Cannot find user with user_id=%s', user_id)
return user
return user
我很少使用 CRITICAL 。當(dāng)一些特別糟糕的事情發(fā)生時(shí)陈症,你可以使用這個(gè)級(jí)別來記錄蔼水。比方說,內(nèi)存耗盡爬凑,磁盤滿了或者核危機(jī)(希望永遠(yuǎn)別發(fā)生 :S)徙缴。
雖然不是非得將 logger 的名稱設(shè)置為 name ,但是這樣做會(huì)給我們帶來諸多益處嘁信。在 python 中于样,變量 name 的名稱就是當(dāng)前模塊的名稱。比如潘靖,在模塊 “foo.bar.my_module” 中調(diào)用 logger.getLogger(name) 等價(jià)于調(diào)用logger.getLogger(“foo.bar.my_module”) 穿剖。當(dāng)你需要配置 logger 時(shí),你可以配置到 “foo” 中卦溢,這樣包 foo 中的所有模塊都會(huì)使用相同的配置糊余。當(dāng)你在讀日志文件的時(shí)候秀又,你就能夠明白消息到底來自于哪一個(gè)模塊。
捕捉異常并使用 traceback 記錄它
出問題的時(shí)候記錄下來是個(gè)好習(xí)慣贬芥,但是如果沒有 traceback 吐辙,那么它一點(diǎn)兒用也沒有。你應(yīng)該捕獲異常并用 traceback 把它們記錄下來蘸劈。比如下面這個(gè)例子:
try:
open('/path/to/does/not/exist', 'rb')
except (SystemExit, KeyboardInterrupt):
raise
except Exception, e:
logger.error('Failed to open file', exc_info=True)
使用參數(shù) exc_info=true 調(diào)用 logger 方法, traceback 會(huì)輸出到 logger 中昏苏。你可以看到下面的結(jié)果
ERROR:__main__:Failed to open file
Traceback (most recent call last):
File "example.py", line 6, in <module>
open('/path/to/does/not/exist', 'rb')
IOError: [Errno 2] No such file or directory: '/path/to/does/not/exist'
Python 使用logging模塊記錄日志涉及四個(gè)主要類,使用官方文檔中的概括最為合適:
logger提供了應(yīng)用程序可以直接使用的接口威沫;
handler將(logger創(chuàng)建的)日志記錄發(fā)送到合適的目的輸出贤惯;
filter提供了細(xì)度設(shè)備來決定輸出哪條日志記錄;
formatter決定日志記錄的最終輸出格式棒掠。
logging模塊是在2.3新引進(jìn)的功能孵构,下面是一些常用的類和模塊級(jí)函數(shù)
模塊級(jí)函數(shù)
logging.getLogger([name]):返回一個(gè)logger對(duì)象,如果沒有指定名字將返回root logger
logging.debug()烟很、logging.info()颈墅、logging.warning()、logging.error()溯职、logging.critical():設(shè)定root logger的日志級(jí)別
logging.basicConfig():用默認(rèn)Formatter為日志系統(tǒng)建立一個(gè)StreamHandler精盅,設(shè)置基礎(chǔ)配置并加到root logger中
每個(gè)程序在輸出信息之前都要獲得一個(gè)Logger帽哑。Logger通常對(duì)應(yīng)了程序的模塊名谜酒,比如聊天工具的圖形界面模塊可以這樣獲得它的Logger:
LOG=logging.getLogger(”chat.gui”)
而核心模塊可以這樣:
LOG=logging.getLogger(”chat.kernel”)
Logger.setLevel(lel):指定最低的日志級(jí)別,低于lel的級(jí)別將被忽略妻枕。debug是最低的內(nèi)置級(jí)別僻族,critical為最高
Logger.addFilter(filt)、Logger.removeFilter(filt):添加或刪除指定的filter
Logger.addHandler(hdlr)屡谐、Logger.removeHandler(hdlr):增加或刪除指定的handler
Logger.debug()述么、Logger.info()、Logger.warning()愕掏、Logger.error()度秘、Logger.critical():可以設(shè)置的日志級(jí)別
設(shè)置logger的level, level有以下幾個(gè)級(jí)別:
NOTSET < DEBUG < INFO < WARNING < ERROR < CRITICAL
如果把looger的級(jí)別設(shè)置為INFO饵撑, 那么小于INFO級(jí)別的日志都不輸出剑梳, 大于等于INFO級(jí)別的日志都輸出
Handlers
handler對(duì)象負(fù)責(zé)發(fā)送相關(guān)的信息到指定目的地。Python的日志系統(tǒng)有多種Handler可以使用滑潘。有些Handler可以把信息輸出到控制臺(tái)垢乙,有些Logger可以把信息輸出到文件,還有些 Handler可以把信息發(fā)送到網(wǎng)絡(luò)上语卤。如果覺得不夠用追逮,還可以編寫自己的Handler酪刀。可以通過addHandler()方法添加多個(gè)多 handler
Handler.setLevel(lel):指定被處理的信息級(jí)別钮孵,低于lel級(jí)別的信息將被忽略
Handler.setFormatter():給這個(gè)handler選擇一個(gè)格式
Handler.addFilter(filt)骂倘、Handler.removeFilter(filt):新增或刪除一個(gè)filter對(duì)象
Formatters
Formatter對(duì)象設(shè)置日志信息最后的規(guī)則、結(jié)構(gòu)和內(nèi)容巴席,默認(rèn)的時(shí)間格式為%Y-%m-%d %H:%M:%S稠茂,下面是Formatter常用的一些信息
%(name)s
Logger的名字
%(levelno)s
數(shù)字形式的日志級(jí)別
%(levelname)s
文本形式的日志級(jí)別
%(pathname)s
調(diào)用日志輸出函數(shù)的模塊的完整路徑名,可能沒有
%(filename)s
調(diào)用日志輸出函數(shù)的模塊的文件名
%(module)s
調(diào)用日志輸出函數(shù)的模塊名
%(funcName)s
調(diào)用日志輸出函數(shù)的函數(shù)名
%(lineno)d
調(diào)用日志輸出函數(shù)的語(yǔ)句所在的代碼行
%(created)f
當(dāng)前時(shí)間情妖,用UNIX標(biāo)準(zhǔn)的表示時(shí)間的浮 點(diǎn)數(shù)表示
%(relativeCreated)d
輸出日志信息時(shí)的睬关,自Logger創(chuàng)建以 來的毫秒數(shù)
%(asctime)s
字符串形式的當(dāng)前時(shí)間。默認(rèn)格式是 “2003-07-08 16:49:45,896”毡证。逗號(hào)后面的是毫秒
%(thread)d
線程ID电爹。可能沒有
%(threadName)s
線程名料睛∝ぢ幔可能沒有
%(process)d
進(jìn)程ID⌒羯罚可能沒有
%(message)s
用戶輸出的消息
設(shè)置過濾器
細(xì)心的朋友一定會(huì)發(fā)現(xiàn)前文調(diào)用logging.getLogger()時(shí)參數(shù)的格式類似于“A.B.C”屎勘。采取這樣的格式其實(shí)就是為了可以配置過濾器【影牵看一下這段代碼:
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)
和前面不同的是我們?cè)贖andler上添加了一個(gè)過濾器「攀現(xiàn)在我們輸出日志信息的時(shí)候就會(huì)經(jīng)過過濾器的處理。名為“A.B”的過濾器只讓名字帶有 “A.B”前綴的Logger輸出信息喜喂∪看荩可以添加多個(gè)過濾器,只要有一個(gè)過濾器拒絕玉吁,日志信息就不會(huì)被輸出照弥。當(dāng)然名為“A”前綴的Logger會(huì)輸出信息。另外进副,在Logger中也可以添加過濾器这揣。
每個(gè)Logger可以附加多個(gè)Handler。接下來我們就來介紹一些常用的Handler:
- logging.StreamHandler
使用這個(gè)Handler可以向類似與sys.stdout或者sys.stderr的任何文件對(duì)象(file object)輸出信息影斑。它的構(gòu)造函數(shù)是:
StreamHandler([strm])
其中strm參數(shù)是一個(gè)文件對(duì)象给赞。默認(rèn)是sys.stderr - logging.FileHandler
和StreamHandler類似,用于向一個(gè)文件輸出日志信息鸥昏。不過FileHandler會(huì)幫你打開這個(gè)文件塞俱。它的構(gòu)造函數(shù)是:
FileHandler(filename[,mode])
filename是文件名,必須指定一個(gè)文件名吏垮。
mode是文件的打開方式障涯。參見Python內(nèi)置函數(shù)open()的用法罐旗。默認(rèn)是’a',即添加到文件末尾唯蝶。 - logging.handlers.RotatingFileHandler
這個(gè)Handler類似于上面的FileHandler九秀,但是它可以管理文件大小。當(dāng)文件達(dá)到一定大小之后粘我,它會(huì)自動(dòng)將當(dāng)前日志文件改名鼓蜒,然后創(chuàng)建 一個(gè)新的同名日志文件繼續(xù)輸出。比如日志文件是chat.log征字。當(dāng)chat.log達(dá)到指定的大小之后都弹,RotatingFileHandler自動(dòng)把 文件改名為chat.log.1。不過匙姜,如果chat.log.1已經(jīng)存在畅厢,會(huì)先把chat.log.1重命名為chat.log.2。氮昧。框杜。最后重新創(chuàng)建 chat.log,繼續(xù)輸出日志信息袖肥。它的構(gòu)造函數(shù)是:
RotatingFileHandler( filename[, mode[, maxBytes[, backupCount]]])
其中filename和mode兩個(gè)參數(shù)和FileHandler一樣咪辱。
maxBytes用于指定日志文件的最大文件大小。如果maxBytes為0椎组,意味著日志文件可以無限大油狂,這時(shí)上面描述的重命名過程就不會(huì)發(fā)生。
backupCount用于指定保留的備份文件的個(gè)數(shù)庐杨。比如选调,如果指定為2夹供,當(dāng)上面描述的重命名過程發(fā)生時(shí)灵份,原有的chat.log.2并不會(huì)被更名,而是被刪除哮洽。 - logging.handlers.TimedRotatingFileHandler
這個(gè)Handler和RotatingFileHandler類似填渠,不過,它沒有通過判斷文件大小來決定何時(shí)重新創(chuàng)建日志文件鸟辅,而是間隔一定時(shí)間就 自動(dòng)創(chuàng)建新的日志文件氛什。重命名的過程與RotatingFileHandler類似,不過新的文件不是附加數(shù)字匪凉,而是當(dāng)前時(shí)間枪眉。它的構(gòu)造函數(shù)是:
TimedRotatingFileHandler( filename [,when [,interval [,backupCount]]])
其中filename參數(shù)和backupCount參數(shù)和RotatingFileHandler具有相同的意義。
interval是時(shí)間間隔再层。
when參數(shù)是一個(gè)字符串贸铜。表示時(shí)間間隔的單位堡纬,不區(qū)分大小寫。它有以下取值:
S 秒
M 分
H 小時(shí)
D 天
W 每星期(interval==0時(shí)代表星期一)
midnight 每天凌晨 - logging.handlers.SocketHandler
- logging.handlers.DatagramHandler
以上兩個(gè)Handler類似蒿秦,都是將日志信息發(fā)送到網(wǎng)絡(luò)烤镐。不同的是前者使用TCP協(xié)議,后者使用UDP協(xié)議棍鳖。它們的構(gòu)造函數(shù)是:
Handler(host, port)
其中host是主機(jī)名炮叶,port是端口名 - logging.handlers.SysLogHandler
- logging.handlers.NTEventLogHandler
- logging.handlers.SMTPHandler
- logging.handlers.MemoryHandler
- logging.handlers.HTTPHandler
# encoding:utf-8
#import logging
#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
# 18 #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')import logging
import logging.config 33
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")
loggerError = logging.getLogger("errorLogger")
loggerError.error("Error: Hello world!")</pre>
#coding=utf-8
import logging 3 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")
# # 下面這行會(huì)同時(shí)打印在文件和終端上
#infoLogger.error("error message")
# 40 #errorLogger.error("error message")
#errorLogger.critical("critical message")
"""
Created on 2016年8月18日
@author: apple
"""
#-*- coding:utf-8 -*-
#開發(fā)出一個(gè)日志系統(tǒng),既要把日志輸出到控制臺(tái)渡处,還要寫入日志文件
import logging 11 import time
import os
import os.path
class Logger():
def __init__(self, log_name, logger_name):
'''
指定保存日志的文件路徑镜悉,日志級(jí)別以及調(diào)用文件
將日志 存入到指定的文件中
'''
#設(shè)置日志文件名稱:time.time()取得當(dāng)前時(shí)間;time.localtime()取得本地時(shí)間医瘫;time.strftime()格式化日期积瞒;
time_str = time.strftime("%Y_%m_%d_%H_%M_%S", time.localtime(time.time()))
logname = time_str + '_' + log_name + '.log'
#設(shè)置日志文件所在的路徑
log_filedir = 'Log'
if not os.path.isdir(log_filedir):
print("日志文件夾 %s 不存在,開始創(chuàng)建此文件夾" %log_filedir)
os.mkdir('Log')
else:
print("日志文件夾 %s 存在" %log_filedir)
os.chdir('Log')
#創(chuàng)建一個(gè)logger以及設(shè)置日志級(jí)別
#logging有6個(gè)日志級(jí)別:NOTSET, DEBUG, INFO, WARNING, ERROR, CRITICAL對(duì)應(yīng)的值分別為:0,10,20,30,40,50
#例如:logging.DEBUG和10是等價(jià)的表示方法
#可以給日志對(duì)象(Logger Instance)設(shè)置日志級(jí)別登下,低于該級(jí)別的日志消息將會(huì)被忽略茫孔,也可以給Hanlder設(shè)置日志級(jí)別
#對(duì)于低于該級(jí)別的日志消息, Handler也會(huì)忽略。
self.logger = logging.getLogger(logger_name)
self.logger.setLevel(logging.DEBUG)
#創(chuàng)建文件handler被芳,用于寫入日志文件并設(shè)置文件日志級(jí)別
file_handler = logging.FileHandler(logname)
file_handler.setLevel(logging.DEBUG)
#創(chuàng)建控制端輸出handler缰贝,用于輸出到控制端并設(shè)置輸出日志級(jí)別
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.DEBUG)
#在控制端handler添加過濾器,將含有chat或者gui的handler信息輸出
filter = logging.Filter("chat.gui")
console_handler.addFilter(filter)
#定義handler的輸出格式并將格式應(yīng)用到handler
formatter = logging.Formatter('%(asctime)s-%(name)s-%(levelname)s-%(message)s')
file_handler.setFormatter(formatter)
console_handler.setFormatter(formatter)
#將handler加入到logger
self.logger.addHandler(file_handler)
self.logger.addHandler(console_handler)
self.logger.debug("這個(gè)是debug日志信息")
self.logger.info("歡迎大家來到 Python的世界")
#將handler從logger中移除
self.logger.removeHandler(file_handler)
self.logger.removeHandler(console_handler)
if __name__ == '__main__':
print(os.getcwd())
Log = Logger('create_log', "chat.gui.statistic")
# 模塊級(jí)函數(shù)
#
# logging.getLogger([name]):返回一個(gè)logger對(duì)象畔濒,如果沒有指定名字將返回root logger
# logging.debug()剩晴、logging.info()、logging.warning()侵状、logging.error()赞弥、logging.critical():設(shè)定root logger的日志級(jí)別
# logging.basicConfig():用默認(rèn)Formatter為日志系統(tǒng)建立一個(gè)StreamHandler,設(shè)置基礎(chǔ)配置并加到root logger中
#
# Loggers
#
# Logger.setLevel(lel):指定最低的日志級(jí)別趣兄,低于lel的級(jí)別將被忽略绽左。debug是最低的內(nèi)置級(jí)別,critical為最高
# Logger.addFilter(filt)艇潭、Logger.removeFilter(filt):添加或刪除指定的filter
# Logger.addHandler(hdlr)拼窥、Logger.removeHandler(hdlr):增加或刪除指定的handler
# Logger.debug()、Logger.info()蹋凝、Logger.warning()鲁纠、Logger.error()、Logger.critical():可以設(shè)置的日志級(jí)別
# Handlers
#
# handler對(duì)象負(fù)責(zé)發(fā)送相關(guān)的信息到指定目的地鳍寂「暮可以通過addHandler()方法添加多個(gè)多handler
# Handler.setLevel(lel):指定被處理的信息級(jí)別,低于lel級(jí)別的信息將被忽略
# Handler.setFormatter():給這個(gè)handler選擇一個(gè)格式
# Handler.addFilter(filt)迄汛、Handler.removeFilter(filt):新增或刪除一個(gè)filter對(duì)象
# Formatters
#
# Formatter對(duì)象設(shè)置日志信息最后的規(guī)則捍壤、結(jié)構(gòu)和內(nèi)容刃唤,默認(rèn)的時(shí)間格式為%Y-%m-%d %H:%M:%S,下面是Formatter常用的一些信息
# %(name)s Logger的名字
#
# %(levelno)s 數(shù)字形式的日志級(jí)別
#
# %(levelname)s 文本形式的日志級(jí)別
%(pathname)s 調(diào)用日志輸出函數(shù)的模塊的完整路徑名白群,可能沒有
#
# %(filename)s 調(diào)用日志輸出函數(shù)的模塊的文件名
#
# %(module)s 調(diào)用日志輸出函數(shù)的模塊名
#
# %(funcName)s 調(diào)用日志輸出函數(shù)的函數(shù)名
#
# %(lineno)d 調(diào)用日志輸出函數(shù)的語(yǔ)句所在的代碼行
#
# %(created)f 當(dāng)前時(shí)間尚胞,用UNIX標(biāo)準(zhǔn)的表示時(shí)間的浮 點(diǎn)數(shù)表示
#
# %(relativeCreated)d 輸出日志信息時(shí)的,自Logger創(chuàng)建以 來的毫秒數(shù)
#
# %(asctime)s 字符串形式的當(dāng)前時(shí)間帜慢。默認(rèn)格式是 “2003-07-08 16:49:45,896”笼裳。逗號(hào)后面的是毫秒
#
# %(thread)d 線程ID×涣幔可能沒有
#
# %(threadName)s 線程名躬柬。可能沒有
#
# %(process)d 進(jìn)程ID抽减≡是啵可能沒有
#
# %(message)s 用戶輸出的消息
'''
Created on 2016年8月25日
@author: apple
'''
import logging 7
logging.basicConfig(level=logging.INFO,
format='%(asctime)s %(name)s %(levelname)s %(message)s',
datefmt='%m-%d %H:%M',
filename='./AutoUpdate.log',
filemode='w')
console = logging.StreamHandler()
console.setLevel(logging.INFO) formatter = logging.Formatter('%(name)s: %(levelname)-8s %(message)s')
console.setFormatter(formatter)
logging.getLogger('').addHandler(console)
logging.info("hello world!")</pre>