您好,登錄后才能下訂單哦!
本篇內容主要講解“Django中log日志源碼分析”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“Django中log日志源碼分析”吧!
以下是一個簡單的 logging 模塊示例,可以先預覽一下,接下來會詳細介紹各個模塊的具體功能:
LOGGING = { 'version': 1, 'disable_existing_loggers': False, 'formatters': { 'verbose': { 'format': '%(levelname)s %(message)s', } }, 'handlers': { 'file_1': { 'level': 'INFO', 'filename': '/Users/hunter/python/log_path/file_1.log', 'formatter': 'verbose', 'class': 'logging.FileHandler', }, 'file_2': { 'level': 'INFO', 'class': 'logging.FileHandler', 'filename': '/Users/hunter/python/log_path/file_2.log', 'formatter': 'verbose', }, 'custom_file': { 'level': 'INFO', 'class': 'logging.FileHandler', 'filename': '/Users/hunter/python/log_path/custome_file.log', 'formatter': 'verbose', } }, 'loggers': { '': { 'handlers': ['file_1'], 'level': 'INFO', 'propagate': False, }, 'django': { 'handlers': ['file_2'], 'level': 'INFO', 'propagate': True, }, 'custom': { 'handlers': ['custom_file'], 'level': 'INFO', 'propagate': False, } } }
在 Django 系統中,日志的記錄也可以在 setting.py 中配置,key 為 logging,然后下面有幾個主要的模塊:
loggers、handlers、filters、formatters
系統接收到日志信息后,進入 logger,然后根據指定的 handler 列表發送到 handler 中
根據 handler 的處理方式,將信息寫入文件、發送郵件或者其他方式
這些信息可以經過 filter 進行進一步的過濾,根據 formatter 的信息組織形式通過 handler 的處理方式進行處理
Loggers 是學習日志系統的一個切入點,每個 logger 都是一個命名的桶,處理的信息可以作為日志寫入到 logger 里
每一個 logger 都可以被配置一個日志等級,日志等級描述了 logger 記錄的信息的嚴重程度,python 定義了如下幾種日志等級:
DEBUG:低的、基于調試目的的系統信息
INFO:一般系統消息
WARNING:發生了小問題的信息
ERROR:發生了大問題的信息
CRITICAL:發生了嚴重的問題的信息
每個被寫入 logger 的消息都被稱為是一個 Log Record(日志記錄)。
每個日志記錄在被發送到 logger 的時候都有一個日志等級來表示信息的嚴重程度
比如:
logger.info("xxx")
這些日志記錄應該包含一些有用的、包含了問題產生原因的信息
當一條消息被發送到 logger,消息的等級會和 logger 的日志等級做一個比較,只有當消息的等級大于或等于 logger 的記錄等級時,消息才會被當前 logger 進行更多的處理
如果這條消息被 logger 接收,那么它會被發送到 Handlers
我們可以理解 handler 是一個處理器,用來決定每天發送到 logger 的信息應該怎么處理,也就是日志的記錄形式
比如說寫入一個文件,發送郵件等
跟 Logger 一樣,handler也有一個日志等級,只有當發送到 handler 的日志等級大于等于 handler 的日志記錄時,handler 才會處理信息
一個 Logger 可以有多個 handler 處理器,每個 handler 都可以有自己的日志等級,因此可以根據信息的重要程度來決定不同的輸出
比如你可以用一個 handler 把 ERROR 和 CRITICAL 等級的信息轉發到服務頁面,另一個 handler 記錄所有的信息到一個文件,用作后續的分析
過濾器常被用來提供額外的控制,處理從 logger 到 handler 的日志記錄
理論上來說,任何日志消息只要滿足了日志等級的要求,都會被發送到 handler 處理,如果加了一個 filter 過濾器,你可以在日志處理上添加額外的標準
比如說你可以添加一個過濾器,只允許某個特定來源的 ERROR 等級的信息被處理
filter 也可以用來修改消息的嚴重等級,比如一些特定的條件被滿足的情況下,你可以將ERROR等級的日志降級為 WARNING
在本篇筆記中,將不介紹 filter 的使用方法,因為能簡單就簡單一點,暫時不用那么多配置
格式化,一個日志記錄需要被渲染成一個文本,formatter 提供了一些格式器的屬性,格式化器由一些 LogRecord 的屬性值組成,你也可以自己定義一個屬性
當你配置了 loggers,handlers,filters 和 formatters 之后,你可以先獲取一個 logger 的實例,然后通過 logger 來記錄日志
以下是使用示例:
import logging logger = logging.getLogger(__name__) def my_view(request): logger.info("this is a log")
這個在調用 my_view 的時候,系統就會記錄一條日志
如果是其他等級的日志記錄,則會是:
logger.debug() logger.info() logger.warning() logger.error() logger.critical()
以下是對日志的記錄流程匯總一下:
當有一條日志信息需要被記錄,然后會被發送到對應的 logger
然后 logger 根據指定的 handler 被發送到對應的 handler 處理器
在 handler 中會根據日志的等級或者定義的 filter 進行一定的過濾
最終將符合條件的日志信息根據 formatter 的格式定義,將最終形成的日志信息,進行 console 操作、記錄到文件、或者發送郵件等操作
在筆記開篇的 logging 示例中,日志的配置在這個 dict 里編寫的順序和消息處理的順序是相反的
這里沒有設置 filter 的操作,所以消息的處理就是從 logger 到 handler 再到 formatter
我們手動在接口里寫入一個日志消息,分別在 urls.py 和 views.py 里如下定義:
# blog/urls.py from django.urls.conf import path from blog.views import time_view urlpatterns = [ path("curr_time", time_view), ]
# blog/views.py import datetime from django.http import HttpResponse import logging logger = logging.getLogger(__name__) def time_view(request): now = datetime.datetime.now() html = "<h2>now: %s</h2>" % now logger.info("this is a log !") return HttpResponse(html)
啟動系統后,在瀏覽器中訪問 http://localhost:9898/blog/curr_time,可以看到定義的日志目錄下已經寫入了數據:file_1.log 和 file_2.log
打開這兩個日志文件,可以看到 loggers 下空字符串指定的 logger 對應的處理器寫入的 file_1.log 寫入的內容如下:
INFO this is a log ! xxxx INFO "GET /blog/curr_time HTTP/1.1" 200 40 xxxx
其中包含接口訪問信息和我們在接口里自定義的 'this is a log !' 信息
在 file_2.log 中,則只有接口的訪問信息:
INFO 200 40 xxxx
在實例化 logger 的時候,如果不指定 logger 的名稱,那么則會默認寫入我們定義的空字符串下的 logger
不指定 logger 名稱的意思即為,getLogger 的時候不指定 logger 的參數:
logger = logging.getLogger(__name__)
在這里 loggers 里設置兩個 key,一個為空字符串,一個是 django。
我們可以理解 key 為 django' 這個 logger 是一個固定的值,會接收到所有來自系統的日志信息,比如一些接口的請求信息,但是不包括用戶自定的 logger 輸出。
空字符串這里的 logger,可以接收到用戶自定義的 logger 輸出,也可以接收到一些接口的請求信息,但是這個需要 propagate 的配置
在 loggers 的配置里面:
'loggers': { '': { 'handlers': ['file_1'], 'level': 'INFO', 'propagate': False, }, 'django': { 'handlers': ['file_2'], 'level': 'INFO', 'propagate': True, } }
有如下幾個參數:
handlers 是指定消息處理器的,value 是一個列表,可以指定多個處理器,比如說一條消息,你可以同時指定寫入文件和發送郵件,或者寫入不同的文件
level 參數表示日志的等級,這里設置的是 INFO 等級,如果接收到的消息的等級小于 INFO,那么就會不處理,大于等于 INFO 才會被發送到 handler 處理器中處理
propagate 參數意義可以理解為是否傳遞傳遞,在這兩個 logger 里,如果 django 這個 logger 的 propagate 的值設為了 True,django 這個 logger 的消息是可以向 空字符串設置的 logger 傳遞的
換句話說,django 接收到的所有消息都會給空字符串的 logger 再發一遍,使用它的 logger 再進行一遍處理,
如果 propagate 設為了 False,那么空字符串的 logger 僅能接收到用戶自定義的消息
當一條消息從 logger 被發送到 handler,handlers 參數也可以定義多個,通過不同的 key 來區分
在每個 handler 下我們這里設置了四個值:
level 設置了 handler 處理的日志等級,只有當發送過來的日志的等級大于等于該等級時,這個 handler 才會處理
class 設置了日志處理的方式,這里我們的值為 logging.FileHandler,表示是文件處理方式
此外還有比如 StreamHandler 輸出到 Stream 打印到標準輸出,還有 HttpHandler 通過HTTP 協議向服務器發送 log, 還有 SMTPHandler 會通過 email 發送log
filename 指定輸出的日志地址,前面我們的 class 定義為向文件輸出,那么這里的 filename 就定義了輸出的文件的地址
formatter 則是指定下一級日志文本的輸出格式處理的 formatter
日志文件處理策略
對于日志文件,如果系統一直運行,那么則會存在一個問題,那就是日志文件越來越大,這個對于系統的存儲和我們查找日志都是不合適的
因此接下來我們新增幾個參數用來制定日志文件的處理策略
maxBytes,這個定義了一個日志文件最大的字節數,如果寫滿了就會新開一個文件繼續寫而不是繼續在原有文件繼續增加內容
如果我們需要設置一個文件最大為5M,就可以設為 5 * 1024 * 1024
backupCount,最大的日志文件數量,當文件的個數超出了我們定義的,則會刪除最早的日志文件,只保留 backupCount 個日志文件
但是使用上面這兩個參數的話,class 的值就得換成 logging.handlers.RotatingFileHandler
接下來介紹幾種 handler 的信息處理方式
rotate 的是定期調換位子,輪換的意思
RotatingFileHandler 的作用是根據文件的大小決定是否寫入新文件,以下是一個示例:
'custom_file': { 'level': 'INFO', 'filename': '/home/hunter/python/log_path/custom.log', 'class': 'logging.handlers.RotatingFileHandler', 'formatter': 'verbose', 'maxBytes': 5 * 1024 * 1024, 'backupCount': 10 }
這個示例表示是將日志寫入文件,每個文件最大容量為 5 * 1024 * 1024,即 5M,當日志寫入一個文件滿了 5M 之后,將會新開一個文件繼續寫入日志信息,文件夾下保留最新的 10 個文件。
這里新增了兩個配置項
backupCount 表示最多保留日志文件的個數
maxBytes 表示每個日志文件最大的存儲容量
TimedRotatingFileHandler 表示是根據時間間隔來決定是否寫入新文件,以下是示例:
'time_file': { 'level': 'INFO', 'filename': '/home/hunter/python/log_path/custom.log', 'class': 'logging.handlers.TimedRotatingFileHandler', # 記錄時間 'formatter': 'verbose', 'backupCount': 3, 'when': 'M', 'interval': 3, }
當 handler 的 class 的值為這個的時候,表示的是根據時間來決定是否寫入新文件,上一個是根據文件的容量大小來定的
這里新增了兩個配置項,
一個是 when,表示的時間間隔的單位,S為秒,M為分鐘,H為小時,D或者 MIDNIGHT為天,W0-W6為從周一到周日某個周幾開始間隔一周
另一個是 interval,間隔時間的倍數
日志換新文件繼續寫入的時間為 when * interval
這個配置表示是如果來了需要處理的日志消息就調用一個 HTTP 接口,這里我們可以只做一個示例:
'http_handler': { 'level': 'INFO', 'class': 'logging.handlers.HTTPHandler', 'formatter': 'verbose', 'host': '192.168.1.8:9898', 'url': '/test_url', 'method': 'POST', },
這個地方,多了幾個配置項
host 表示需要調用接口的 ip 和 端口
url 表示調用的接口路徑
method 表示調用的方法
這個配置用于發送郵件,如果日志消息發送到這個配置的 handler,系統會根據郵件的配置系統發送郵件給指定的郵箱
以下是一個使用示例:
'email': { 'level': 'WARNING', 'class': 'logging.handlers.SMTPHandler', 'mailhost': ('smtp.163.com', 25), 'fromaddr': 'xxxxxxxx@163.com', 'toaddrs': 'xxxxxxx@qq.com', 'subject': '系統出錯啦!!!', 'credentials': ('xxxxxxx@163.com', 'JBD******'), 'timeout': 20 },
在這個配置中,多的配置項的介紹如下:
mailhost 是系統發送郵件的郵箱的主機和端口,這里我們配置的是 163 郵箱
fromaddr 是從哪個郵箱發出來,我們可以創建一個163郵箱然后指定該值
toaddrs 是發送到哪個郵箱,即日志消息的郵件接收地址
subject 是我們發送郵件的標題,而郵件的正文內容即為我們在 logger.warning("報錯信息") 中輸入的信息
credentials 是163郵箱的驗證信息,兩個值,前一個值與 fromaddr 保持一致,后面的是一串驗證碼,是163郵箱開啟 SMTP 服務之后163郵箱系統頁面給我們的一串授權密碼,這個可以自己去了解一下
這樣配置好之后,在 logger 的 handler 列表指定這個 handler,然后通過 logger.warning("報錯信息") 即可觸發這個郵件發送的功能
這個配置也是用于日志發送郵件,但是是復用 Django 的默認郵箱的功能
在 logging 中的配置是:
'mail_admins': { 'level': 'WARNING', 'class': 'django.utils.log.AdminEmailHandler', 'include_html': True, },
但是這個還需要一些額外的在 settings.py 中的郵箱配置,相當于是復用 Django 系統的功能
以下是 settings.py 中郵箱的配置項:
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' EMAIL_HOST = 'smtp.163.com' # 163 郵箱的配置地址 EMAIL_PORT = 465 # SMTP 端口 EMAIL_HOST_USER = 'xxxxxx@163.com' #這個是用來發送郵件的郵箱,與最后一個填寫的郵箱地址一致 EMAIL_HOST_PASSWORD = 'JBDM******' #這里就是前面提到的授權密碼 EMAIL_USE_SSL = True EMAIL_FROM = SERVER_EMAIL = 'xxxxxxx@163.com' # 這個是發送郵件的地址,與上面的 163郵箱相同即可 ADMINS = [ ['Hunter', 'xxxxxx@qq.com'], ] # 郵件接收地址
上面的參數都配置好之后也可以日志觸發郵件了。
formatter 的參數就簡單一點,通過不同的 key 來區分不同的 formatter,其下設置一個 format 參數即可對信息進行格式化處理
'formatters': { 'verbose': { 'format': '%(levelname)s %(message)s', } },
在示例中只設置了 levelname 和 message 兩個參數,levelname 即為該日志消息的等級,message為消息內容
對于請求接口的 message 信息,返回的內容是固定的,比如前面的示例:
"GET /blog/curr_time HTTP/1.1" 200 40
前面是接口的請求方式、接口路徑和HTTP協議,然后是接口返回的狀態碼,這里是 200,后面跟著的 40 這個數字則是接口返回的字符長度
如果是用戶在系統里手動寫入的 message,則是定義的什么內容,輸出的就是什么內容
對于 format 的定義的參數還有很多,以下是幾個常用的匯總:
參數名稱 | 參數用法 | 含義 |
---|---|---|
levelname | %(levelname)s | 日志等級 |
message | %(message)s | 消息內容 |
asctime | %(asctime)s | 時間,格式為'2022-01-01 00:00:00,000' |
pathname | %(pathname)s | 日志輸出所在文件的全路徑 |
filename | %(filename)s | 日志輸出所在的文件名 |
module | %(module)s | 日志輸出所在的模塊,可以理解成不帶后綴的文件名 |
name | %(name)s | 調用日志使用的名稱,logging.getLogger(name)時為從模塊到函數,比如 blog.views |
funcName | %(funcName)s | 日志輸出的函數名稱 |
lineno | %(lineno)d | 日志輸出所在的文件的行數 |
process | %(process)d | 進程id |
processName | %(processName)s | 進程名稱 |
thread | %(thread)d | 線程id |
threadName | %(threadName)s | 線程名稱 |
之前我們設定的用戶手動輸入的日志被傳送給了 key 為空字符串下的 logger,如果我們想把某一些日志信息專門輸出到某個文件怎么處理呢?
在獲取 logger 的時候就需要根據 logger 的 key 來指定對應的 logger,比如我們新建一個名為 custom 的 logger 和 對應的 handler,然后輸出的地方指定即可,如下:
'custom': { 'handlers': ['custom_file'], 'level': 'INFO', 'propagate': False, }
指定 logger 輸出:
import datetime from django.http import HttpResponse import logging custom_logger = logging.getLogger("custom") # 對應 logging 配置中的 key 為 custom 的 logger def time_view(request): now = datetime.datetime.now() html = "<h2>now: %s</h2>" % now custom_logger.info("this is a custom log") return HttpResponse(html)
這樣在對應的地方就可以實現專門的日志輸出到專門的文件了。
接下來我們實現這樣一個日志配置的功能:
實現用戶所有普通的手動輸出都寫入一個 manual.log 文件
所有接口的請求數據都輸入到一個 request.log 文件
設置一個單獨的日志輸出,可以輸出到指定文件
所有 INFO 級別的日志都輸出到文件,高于 INFO 的都發送郵件通知指定聯系人
對于日志文件要求每個文件最大容量為 50M,且文件夾下每個類型的日志最多只有10個
日志的信息結構為:日志等級-時間-日志輸出所在文件名-日志輸出所在函數名-日志輸出所在文件的行數-消息內容
以下是實現上面這個功能的 logging 配置:
LOGGING = { 'version': 1, 'disable_existing_loggers': False, 'formatters': { 'verbose': { 'format': '%(levelname)s %(asctime)s %(filename)s %(funcName)s %(lineno)d %(message)s', } }, 'handlers': { 'manual_file': { 'level': 'INFO', 'filename': '/Users/hunter/python/log_path/manual.log', 'formatter': 'verbose', 'class': 'logging.handlers.RotatingFileHandler', 'maxBytes': 5 * 1024 * 1024, 'backupCount': 10 }, 'request_file': { 'level': 'INFO', 'filename': '/Users/hunter/python/log_path/request.log', 'class': 'logging.handlers.RotatingFileHandler', 'formatter': 'verbose', 'maxBytes': 5 * 1024 * 1024, 'backupCount': 10 }, 'custom_file': { 'level': 'INFO', 'filename': '/Users/hunter/python/log_path/custom.log', 'class': 'logging.handlers.RotatingFileHandler', 'formatter': 'verbose', 'maxBytes': 5 * 1024 * 1024, 'backupCount': 10 }, 'email': { 'level': 'WARNING', 'class': 'logging.handlers.SMTPHandler', 'mailhost': ('smtp.163.com', 25), 'fromaddr': 'xxxxxx@163.com', 'toaddrs': 'xxxxxxx@qq.com', 'subject': '系統出錯啦!!!', 'credentials': ('xxxxxx@163.com', 'JBD*******'), 'timeout': 20 }, }, 'loggers': { '': { 'handlers': ['manual_file', 'email'], 'level': 'INFO', 'propagate': False, }, 'django': { 'handlers': ['request_file'], 'level': 'INFO', 'propagate': True, }, 'custom': { 'handlers': ['custom_file'], 'level': 'INFO', 'propagate': False, }, }, }
然后我們定義一個接口內容:
import datetime from django.http import HttpResponse, JsonResponse import logging logger = logging.getLogger(__name__) custom_logger = logging.getLogger("custom") def time_view(request): now = datetime.datetime.now() html = "<h2>now: %s</h2>abc\nabc" % now logger.info("this is a log !") custom_logger.info("this is a custom log") logger.warning("報錯啦!!!") return HttpResponse(html)
調用這個接口即可發現實現了想要的功能
到此,相信大家對“Django中log日志源碼分析”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。