摘要:每一條日志記錄也包含級(jí)別,代表對(duì)應(yīng)消息的嚴(yán)重程度。即格式化器,主要功能是確定最終輸出的形式和內(nèi)容。最好是日志能夠按自然天進(jìn)行記錄和分割。
上一章學(xué)習(xí)了自動(dòng)化測(cè)試,很好,現(xiàn)在我們可以絞盡腦汁寫(xiě)出一份全面的測(cè)試,來(lái)保證代碼永遠(yuǎn)健康了。
話(huà)雖如此,但是作為一個(gè)獨(dú)立開(kāi)發(fā)者很難寫(xiě)出真正全面的測(cè)試代碼。這是因?yàn)橛脩?hù)在使用你的網(wǎng)站時(shí)可不會(huì)循規(guī)蹈矩,而是會(huì)以各種怪異的姿勢(shì)瀏覽網(wǎng)頁(yè)、上傳數(shù)據(jù)。但這也不是壞事,用戶(hù)就是天然的測(cè)試人員,他們會(huì)很可愛(ài)的幫你找出一大堆的bug,陪你度過(guò)難眠的夜晚(伴隨著編程能力的提升)。
現(xiàn)在的問(wèn)題是,開(kāi)發(fā)者如何得知用戶(hù)到底遇到了哪些問(wèn)題?用戶(hù)們大部分都與你素昧平生,分部在世界各地。更糟糕的是,部署在線(xiàn)上時(shí)由于配置了DEBUG = False,出錯(cuò)時(shí)并不會(huì)出現(xiàn)報(bào)錯(cuò)頁(yè)面,連用戶(hù)自己都不清楚到底是哪里有bug。
Django給你的答案:日志。
日志的組成日志是指程序在運(yùn)行過(guò)程中,對(duì)狀態(tài)、時(shí)間、錯(cuò)誤等的記錄。即把運(yùn)行過(guò)程中產(chǎn)生的信息輸出或保存起來(lái),供開(kāi)發(fā)者查閱。
Django使用Python內(nèi)置的logging模塊處理日志。關(guān)于該模塊的使用,Python文檔里有非常詳細(xì)的討論。如果你從未用過(guò),本文提供一個(gè)快速入門(mén)。
日志事件的信息流程如下:
這個(gè)圖看不懂也沒(méi)關(guān)系。以后你需要深度使用日志時(shí),會(huì)回來(lái)仔細(xì)研究它的。
一份日志配置由Loggers、Handlers、Filters、Formatters四部分組成。
LoggersLogger即記錄器,是日志系統(tǒng)的入口。它有三個(gè)重要的工作:
向應(yīng)用程序(也就是你的項(xiàng)目)公開(kāi)幾種方法,以便運(yùn)行時(shí)記錄消息
根據(jù)傳遞給Logger的消息的嚴(yán)重性,確定出需要處理的消息
將需要處理的消息傳遞給所有感興趣的處理器(Handler)
每一條寫(xiě)入logger的消息都是一條日志記錄。每一條日志記錄也包含級(jí)別,代表對(duì)應(yīng)消息的嚴(yán)重程度。常用的級(jí)別如下:
DEBUG:排查故障時(shí)使用的低級(jí)別系統(tǒng)信息,通常開(kāi)發(fā)時(shí)使用
INFO:一般的系統(tǒng)信息,并不算問(wèn)題
WARNING:描述系統(tǒng)發(fā)生的小問(wèn)題的信息,但通常不影響功能
ERROR:描述系統(tǒng)發(fā)生的大問(wèn)題的信息,可能會(huì)導(dǎo)致功能不正常
CRITICAL:描述系統(tǒng)發(fā)生嚴(yán)重問(wèn)題的信息,應(yīng)用程序有崩潰風(fēng)險(xiǎn)
當(dāng)logger處理一條消息時(shí),會(huì)將自己的日志級(jí)別和這條消息的日志級(jí)別做對(duì)比。如果消息的級(jí)別匹配或者高于logger的日志級(jí)別,它就會(huì)被進(jìn)一步處理;否則這條消息就會(huì)被忽略掉。
當(dāng)logger確定了一條消息需要處理之后,會(huì)把它傳給Handler。
HandlersHandler即處理器,它的主要功能是決定如何處理logger中每一條消息,比如把消息輸出到屏幕、文件或者Email中。
和logger一樣,handler也有級(jí)別的概念。如果一條日志記錄的級(jí)別不匹配或者低于handler的日志級(jí)別,則會(huì)被handler忽略。
一個(gè)logger可以有多個(gè)handler,每一個(gè)handler可以有不同的日志級(jí)別。這樣就可以根據(jù)消息的重要性不同,來(lái)提供不同類(lèi)型的輸出。例如,你可以添加一個(gè)handler把ERROR和CRITICAL消息發(fā)到你的Email,再添加另一個(gè) handler把所有的消息(包括ERROR和CRITICAL消息)保存到文件里。
FiltersFilter即過(guò)濾器。在日志記錄從logger傳到handler的過(guò)程中,使用Filter來(lái)做額外的控制。例如只允許某個(gè)特定來(lái)源的ERROR消息輸出。
Filter還被用來(lái)在日志輸出之前對(duì)日志記錄做修改。例如當(dāng)滿(mǎn)足一定條件時(shí),把日志記錄從 ERROR 降到 WARNING 級(jí)別。
Filter在logger和handler中都可以添加;多個(gè)filter可以鏈接起來(lái)使用,來(lái)做多重過(guò)濾操作。
FormattersFormatter即格式化器,主要功能是確定最終輸出的形式和內(nèi)容。
日志配置示例說(shuō)了這么多腦殼都說(shuō)暈了,接下來(lái)看兩個(gè)示例。
簡(jiǎn)單示例在Django中可以通過(guò)字典的形式對(duì)整個(gè)項(xiàng)目的日志進(jìn)行配置,配置的位置當(dāng)然是在settings.py中了。一個(gè)簡(jiǎn)單的配置如下:
my_blog/settings.py ... import os LOGGING = { "version": 1, "disable_existing_loggers": False, "handlers": { "file": { "level": "DEBUG", "class": "logging.FileHandler", "filename": os.path.join(BASE_DIR, "logs/debug.log"), }, }, "loggers": { "django": { "handlers": ["file"], "level": "DEBUG", "propagate": True, }, }, }
字典中的version指明了配置的版本;disable_existing_loggers指明是否禁止默認(rèn)配置的記錄器。這兩項(xiàng)通常不需要去改動(dòng),重點(diǎn)看下loggers和handlers的配置:
如前面說(shuō),一條消息首先傳遞給logger。Django中內(nèi)置了幾種記錄器,比如這里用到的Django記錄器,它會(huì)接收Django層次結(jié)構(gòu)中的所有消息。然后我們定義了需要處理DEBUG以上級(jí)別的消息,并把這些消息傳遞給名叫file的處理器。"propagate": True意思是本記錄器處理過(guò)的消息其他處理器也可以繼續(xù)處理。
現(xiàn)在消息來(lái)到名叫file的handlers中了。這個(gè)處理器定義了消息處理級(jí)別仍然為DEBUG,在class中定義將消息輸出到文件中去,文件地址為項(xiàng)目目錄的logs/debug.log。
因?yàn)檫@里沒(méi)有配置filters和formatters,因此會(huì)采用默認(rèn)的設(shè)置。
需要注意的是日志的輸出文件的目錄logs/一定要提前創(chuàng)建好,并且確保項(xiàng)目擁有此目錄的寫(xiě)入權(quán)限。
這個(gè)日志系統(tǒng)就配置好了!接下來(lái)運(yùn)行項(xiàng)目,隨便刷新幾個(gè)頁(yè)面看看debug.log中有沒(méi)有寫(xiě)入消息:
logs/debug.log (0.001) SELECT name, type FROM sqlite_master WHERE type in ("table", "view") AND NOT name="sqlite_sequence" ORDER BY name; args=None (0.000) SELECT "django_migrations"."app", "django_migrations"."name" FROM "django_migrations"; args=() ... ... ...
debug.log文件中出現(xiàn)了一大堆冗長(zhǎng)的信息,因?yàn)?b>DEBUG級(jí)別會(huì)包含所有的數(shù)據(jù)庫(kù)查詢(xún)記錄。
默認(rèn)情況下,僅在調(diào)試模式下才會(huì)顯示DEBUG級(jí)別的消息日志,部署在線(xiàn)上時(shí)只會(huì)將INFO或以上的信息進(jìn)行記錄。
再試試別的。把上面代碼中記錄器和處理器的日志級(jí)別都改為INFO:
LOGGING = { ... "handlers": { "file": { "level": "INFO", ... }, }, "loggers": { "django": { "level": "INFO", ... }, }, }
再刷新幾次界面,看看輸出的內(nèi)容:
"GET /article/article-list/ HTTP/1.1" 200 14438 "GET /article/article-detail/32/ HTTP/1.1" 200 33364 "GET /accounts/login/ HTTP/1.1" 200 7180 ...
這次清爽多了,輸出的主要是頁(yè)面的拉取信息。
讓我們?cè)倏纯?b>ERROR信息長(zhǎng)什么樣的。在地址欄輸入一個(gè)不存在的文章詳情頁(yè)面地址:
http://127.0.0.1:8000/article/article-detail/9999/
很明顯這會(huì)得到一個(gè)數(shù)據(jù)不存在的報(bào)錯(cuò):
Internal Server Error: /article/article-detail/9999/ Traceback (most recent call last): File "E:django_projectenvlibsite-packagesdjangocorehandlersexception.py", line 34, in inner response = get_response(request) ... article.models.ArticlePost.DoesNotExist: ArticlePost matching query does not exist. "GET /article/article-detail/9999/ HTTP/1.1" 500 80792
ERROR日志輸出了整個(gè)bug的回溯,和你在瀏覽器中的報(bào)錯(cuò)是完全一樣的,這些信息就非常的有用了。基本上ERROR信息能夠暴露出用戶(hù)在使用你的網(wǎng)站過(guò)程中的大部分問(wèn)題;也就是說(shuō)每一個(gè)ERROR都是需要你去解決掉的。ERROR信息的錯(cuò)誤碼通常都是“500”,也就是服務(wù)器內(nèi)部錯(cuò)誤的代碼。
不過(guò)仔細(xì)想想,似乎找不到對(duì)應(yīng)的資源在很多時(shí)候并不是bug,而是用戶(hù)在輸入url時(shí)自己犯了錯(cuò)誤。所以我們把文章詳情視圖的ArticlePost.objects.get(id=id)改成get_object_or_404(ArticlePost, id=id)試試:
article/views.py ... from django.shortcuts import get_object_or_404 def article_detail(request, id): # 取出相應(yīng)的文章 # article = ArticlePost.objects.get(id=id) article = get_object_or_404(ArticlePost, id=id) ...
服務(wù)器重啟后再次刷新一個(gè)不存在的頁(yè)面,看看日志:
Not Found: /article/article-detail/9999/ "GET /article/article-detail/9999/ HTTP/1.1" 404 1780
現(xiàn)在它不是一條ERROR信息了,而是變?yōu)榱?b>WARNING,所以也沒(méi)有了錯(cuò)誤回溯(錯(cuò)誤碼也由 500 變成了 404)。這里就能看出這兩個(gè)方法的重要區(qū)別了;在項(xiàng)目中到底選擇哪個(gè)沒(méi)有定論,還是以你的具體需求決定。
復(fù)雜示例接下來(lái)再看一個(gè)復(fù)雜的日志配置:
LOGGING = { "version": 1, "disable_existing_loggers": False, "formatters": { "verbose": { "format": "{levelname} {asctime} {module} {process:d} {thread:d} {message}", "style": "{", }, "simple": { "format": "{levelname} {message}", "style": "{", }, }, "filters": { "require_debug_true": { "()": "django.utils.log.RequireDebugTrue", }, }, "handlers": { "console": { "level": "INFO", "filters": ["require_debug_true"], "class": "logging.StreamHandler", "formatter": "simple" }, "mail_admins": { "level": "ERROR", "class": "django.utils.log.AdminEmailHandler", "formatter": "verbose", }, "file": { "level": "WARNING", "class": "logging.FileHandler", "filename": os.path.join(BASE_DIR, "logs/debug.log"), "formatter": "verbose", }, }, "loggers": { "django": { "handlers": ["console"], "propagate": True, }, "django.request": { "handlers": ["file", "mail_admins"], "level": "WARNING", "propagate": False, }, } }
讓我們來(lái)分解一下此配置。
配置中定義了兩個(gè)格式化器:
verbose:詳細(xì)的格式化器,依次輸出:消息級(jí)別、發(fā)生時(shí)間、拋出模塊、進(jìn)程ID、線(xiàn)程ID、提示信息
simple:簡(jiǎn)要的格式化器,僅輸出消息級(jí)別和提示信息
一個(gè)過(guò)濾器:
require_debug_true:使用此過(guò)濾器的消息僅在調(diào)試時(shí)才會(huì)生效
三個(gè)處理器:
console:處理INFO以上級(jí)別消息,輸出簡(jiǎn)要信息到命令行中;此處理器僅在調(diào)試模式生效
mail_admins:處理ERROR以上級(jí)別消息,輸出詳細(xì)信息到Email中
file:處理WARNING以上級(jí)別消息,輸出詳細(xì)信息到文件中
兩個(gè)記錄器:
django:將django產(chǎn)生的所有消息轉(zhuǎn)交給console處理器
django.request:將網(wǎng)絡(luò)請(qǐng)求相關(guān)消息轉(zhuǎn)交給file、mail_admins這兩個(gè)處理器。注意這里的"propagate": False使得此記錄器處理過(guò)的消息就不再讓django記錄器再次處理了
讀者可以嘗試制造不同級(jí)別的消息,看看日志系統(tǒng)是否正常工作。當(dāng)然最重要的,跟Email有關(guān)的配置一定要事先把Email給設(shè)置好,即下面的內(nèi)容填成你的:
# SMTP服務(wù)器 EMAIL_HOST = "your smtp" # 郵箱名 EMAIL_HOST_USER = "your email" # 郵箱密碼 EMAIL_HOST_PASSWORD = "your password" # 發(fā)送郵件的端口 EMAIL_PORT = 25 # 是否使用 TLS EMAIL_USE_TLS = True # 默認(rèn)的發(fā)件人 DEFAULT_FROM_EMAIL = "your email"日志分割
現(xiàn)在我們已經(jīng)可以愉快的記錄日志了,接下來(lái)一個(gè)問(wèn)題是如何去分割日志?假設(shè)你的網(wǎng)站能夠有幸運(yùn)行十年時(shí)間,如果不間斷的往同一個(gè)文件中寫(xiě)日志信息,最終它會(huì)變成一個(gè)拖垮服務(wù)器的龐然大物。
最好是日志能夠按自然天進(jìn)行記錄和分割。好在這個(gè)問(wèn)題也不需要你去費(fèi)腦筋,Python幫你搞定了。
只需要把處理器稍稍改一下:
my_blog/settings.py ... LOGGING = { ... "handlers": { ... "file": { ... # 注釋掉 class # "class": "logging.FileHandler", #新增內(nèi)容 "class": "logging.handlers.TimedRotatingFileHandler", "when": "midnight", "backupCount": 30, }, }, ... }
TimedRotatingFileHandler:Python內(nèi)置的隨時(shí)間分割日志文件的模塊
when:分割時(shí)間為凌晨
backupCount:日志文件保存日期為30天
接下來(lái)把系統(tǒng)時(shí)間往后調(diào)一天,然后重新啟動(dòng)服務(wù)器:
python manage.py runserver --noreload
注意這次啟動(dòng)有點(diǎn)不一樣,后面有個(gè)--noreload后綴。這是因?yàn)橥ǔjango的調(diào)試服務(wù)器運(yùn)行時(shí)會(huì)順帶啟動(dòng)重載器,所以每當(dāng)重載器檢測(cè)到代碼有變化后,會(huì)自動(dòng)重啟服務(wù)器,相當(dāng)?shù)姆奖恪5珕?wèn)題是分割文件與重載器同時(shí)操作日志文件會(huì)產(chǎn)生沖突,因此這里一定要用--noreload暫時(shí)將重載器禁止掉。
然后就可以愉快的刷幾條消息到文件中啦。你會(huì)發(fā)現(xiàn)老日志已經(jīng)更名為debug.log.2019-07-17了,而剛刷的新消息則保存在debug.log中。
除了上面介紹的TimedRotatingFileHandler,Python還提供了一個(gè)按照文件大小分割的RotatingFileHandler。有興趣的看Python官方文檔自定義日志
內(nèi)置配置實(shí)際上已經(jīng)能夠滿(mǎn)足90%以上的日志需求了,但總有時(shí)候你想在一些奇怪的地方進(jìn)行記錄,這就需要你自己在代碼中插入自定義的日志記錄代碼了。
自定義日志用起來(lái)也是相當(dāng)方便的:
from my_blog.settings import LOGGING import logging logging.config.dictConfig(LOGGING) logger = logging.getLogger("django.request") def whatever(request): # do something logger.warning("Something went wrong!") # do something else
導(dǎo)入剛才寫(xiě)的日志框架并將django.request配置到logger對(duì)象中。然后你就可以在任何地方安插任何級(jí)別的消息了。消息內(nèi)容可以用字符串的格式化方法(str.format()),玩出各種花樣。
關(guān)于日志的入門(mén)介紹就到此為止了,想深入學(xué)習(xí)的讀者請(qǐng)繼續(xù)閱讀本文的參考文章:
Django logging
Python 3 logging
總結(jié)和上章類(lèi)似,本章的內(nèi)容也是概念偏多,希望讀者盡可能去理解,最起碼要囫圇吞棗的把日志成功移植到你的項(xiàng)目中去。獲取一份好的日志,有時(shí)候遠(yuǎn)比開(kāi)發(fā)一個(gè)無(wú)關(guān)緊要的新功能更重要。
比較起來(lái)博主認(rèn)為對(duì)博客項(xiàng)目來(lái)說(shuō),日志比測(cè)試還重要,畢竟用戶(hù)的使用體驗(yàn)是最佳的實(shí)踐。
但請(qǐng)不要誤會(huì)我的意思。測(cè)試和日志就像兩兄弟,測(cè)試解決開(kāi)發(fā)中的問(wèn)題,日志解決維護(hù)中的問(wèn)題。有機(jī)的結(jié)合起來(lái),你的項(xiàng)目才能夠長(zhǎng)期穩(wěn)定健康。
有疑問(wèn)請(qǐng)?jiān)诙刨惖膫€(gè)人網(wǎng)站留言,我會(huì)盡快回復(fù)。
或Email私信我:dusaiphoto@foxmail.com
項(xiàng)目完整代碼:Django_blog_tutorial
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/45179.html
摘要:語(yǔ)法支持再次打開(kāi)文件,在文件的最后添加指明了使用語(yǔ)法標(biāo)記,做了兩個(gè)拓展,其中表示支持語(yǔ)法高亮,包含的特性請(qǐng)參見(jiàn)相關(guān)文檔。語(yǔ)法高亮支持注意這一步必須在安裝完主題之后。 目前網(wǎng)上搭建個(gè)人博客的方案很多,雖然使用諸如 Wordpress ( PHP )、Hexo ( Node.js ) 等可以方便快速地搭建一款功能齊全的高性能個(gè)人博客,但是本文將嘗試一種更為小眾化的方案 —— 一款基于 dj...
摘要:比如,在一個(gè)博客應(yīng)用中,你可能會(huì)創(chuàng)建如下幾個(gè)視圖博客首頁(yè)展示最近的幾項(xiàng)內(nèi)容。這些需求都靠視圖來(lái)完成。首先寫(xiě)一個(gè)最簡(jiǎn)單的視圖函數(shù),在瀏覽器中打印出字符串。調(diào)用函數(shù)時(shí)會(huì)返回一個(gè)含字符串的對(duì)象。換句話(huà)說(shuō),的作用是將映射到視圖中。 Django 中的視圖的概念是「一類(lèi)具有相同功能和模板的網(wǎng)頁(yè)的集合」。比如,在一個(gè)博客應(yīng)用中,你可能會(huì)創(chuàng)建如下幾個(gè)視圖: 博客首頁(yè):展示最近的幾項(xiàng)內(nèi)容。 內(nèi)容詳情...
摘要:在里寫(xiě)一個(gè)數(shù)據(jù)庫(kù)驅(qū)動(dòng)的應(yīng)用的第一步是定義模型,也就是數(shù)據(jù)庫(kù)結(jié)構(gòu)設(shè)計(jì)和附加的其它元數(shù)據(jù)。模型元數(shù)據(jù)是任何不是字段的東西,例如排序選項(xiàng)數(shù)據(jù)庫(kù)表名單數(shù)和復(fù)數(shù)名稱(chēng)和。 Django 框架主要關(guān)注的是模型(Model)、模板(Template)和視圖(Views),稱(chēng)為MTV模式。 它們各自的職責(zé)如下: 層次 職責(zé) 模型(Model),即數(shù)據(jù)存取層 處理與數(shù)據(jù)相關(guān)的所有事務(wù): 如何存取...
摘要:而文章分類(lèi)一個(gè)重要的途徑就是設(shè)置欄目。修改文件欄目的欄目標(biāo)題創(chuàng)建時(shí)間文章欄目的一對(duì)多外鍵欄目的有兩個(gè)字段,名稱(chēng)和創(chuàng)建日期。修改文章的欄目功能,也就完成了。對(duì)個(gè)人博客來(lái)說(shuō),欄目數(shù)據(jù)的變動(dòng)通常是很少的。 博客的文章類(lèi)型通常不止一種:有時(shí)候你會(huì)寫(xiě)高深莫測(cè)的技術(shù)文章,有時(shí)候又純粹只記錄一下當(dāng)天的心情。 因此對(duì)文章的分類(lèi)就顯得相當(dāng)?shù)闹匾耍确奖悴┲鲗?duì)文章進(jìn)行分類(lèi)歸檔,也方便用戶(hù)有針對(duì)性的閱讀。...
摘要:確認(rèn)創(chuàng)建成功后,記得在中注冊(cè)因?yàn)槲覀兿腼@示發(fā)表評(píng)論的時(shí)間,修改時(shí)區(qū)設(shè)置為上海的時(shí)區(qū)。處理錯(cuò)誤請(qǐng)求發(fā)表評(píng)論僅接受請(qǐng)求。返回到一個(gè)適當(dāng)?shù)闹屑从脩?hù)發(fā)送評(píng)論后,重新定向到文章詳情頁(yè)面。總結(jié)本章實(shí)現(xiàn)了發(fā)表評(píng)論展示評(píng)論的功能。 在沒(méi)有互聯(lián)網(wǎng)的年代,我們用日記來(lái)記錄每天的心得體會(huì)。小的時(shí)候我有一個(gè)帶鎖的日記本,生怕被別人看見(jiàn)里面寫(xiě)了啥,鑰匙藏得那叫一個(gè)絕。 現(xiàn)在時(shí)代變了,網(wǎng)絡(luò)版的日記本:博客,卻巴不...
閱讀 3153·2021-11-22 13:54
閱讀 3441·2021-11-15 11:37
閱讀 3606·2021-10-14 09:43
閱讀 3502·2021-09-09 11:52
閱讀 3599·2019-08-30 15:53
閱讀 2462·2019-08-30 13:50
閱讀 2060·2019-08-30 11:07
閱讀 891·2019-08-29 16:32