前言
django框架的日誌通過python
內建的logging
模組實現的,既可以記錄自定義的一些資訊描述,也可以記錄系統執行中的一些物件資料,還可以記錄包括堆疊跟蹤、錯誤程式碼之類的詳細資訊。
logging主要由4部分組成:Loggers
、Handlers
、Filters
和Formatters
settings中完整的配置
如果想自定義配置日誌資訊,我們可以在settings.py
檔案中配置,那配置的格式是怎麼樣的呢?我們可以通過from django.utils.log import DEFAULT_LOGGING
檢視Django中預設的日誌配置資訊,然後依葫蘆畫瓢即可
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'incremental':True,
'filters': {},
'formatters': {},
'handlers': {},
'loggers': {}
}
上述是預設的日誌配置資訊的格式,我們依次介紹
- version:配置資訊的版本
- disable_existing_loggers:預設為
True
,True:設定已存在的logger
失效。False:讓已存在的logger
不失效,保證日誌資訊完整。一般情況下設定為False
- incremental:預設為
False
。True:是將配置解釋為現有配置的增量。False:配置會覆蓋已有預設配置。一般此項不用配置 - filter:過濾器
- formatters:格式器
- handlers:處理器
- loggers:日誌器
Formatters
日誌記錄最終需要呈現為文字,formatter
程式描述該文字的確切格式。formatter
通常由包含LogRecord
屬性的Python
格式化字串組成 ; 但是,也可以編寫自定義formatter
來實現特定的格式化行為。
1.settings中配置:
3個引數(具體看後面的Formatter
類):
- ():指定格式器的類,不指定的話,預設使用
logging.Formattr
。一般用預設即可 - format:格式化字串
- style:樣式選擇
- datefmt:日期格式化字串,使用的是
python
中時間日期格式化符號
案例
LOGGING = {
'formatters': {
'verbose': {
'()': 'logging.Formatter',
'format': '{levelname} {asctime} {module} {process:d} {thread:d} {message}',
'style': '{',
},
'simple': {
'format': '{levelname} {message}',
'style': '{',
},
}
}
配置了2個格式器:
- simple:只輸出簡單的:日誌級別名稱 日誌訊息
- verbose:輸出:日誌級別名稱 生成日誌訊息的時間 模組 程式 執行緒 日誌訊息
2.內建格式器
Formatter:預設格式器,初始化引數:fmt=None, datefmt=None, style='%'
- fmt:格式化字串,指定輸出格式,如:
'{levelname}{process:d}{message}'
- datefmt:日期格式化字串,為
None
則使用ISO8601
格式化,如:'2010-01-01 08:03:26,870'
- style:'%','{' 或 '$',3選一:
- '%':預設是這個,使用
python
的%
格式化 , 如:%(levelname)s
- '{':使用
str.format
格式化(django框架使用這個), 如:{levelname}
- '$':使用類
string.Template
格式化,如:\$levelname
- '%':預設是這個,使用
格式化字串的種類
%(name)s:記錄器logger的名稱
%(levelno)s:日誌級別對應的數字
%(levelname)s:日誌級別名稱
%(pathname)s:日誌記錄呼叫的原始檔的完整路徑
%(filename)s:日誌記錄呼叫的原始檔名
%(module)s:模組名
%(lineno)d:日誌呼叫的行數
%(funcName)s:函式名
%(created)f:日誌建立時間,time.time()
%(asctime)s:日誌建立時間,文字型別
%(msecs)d:日誌建立時間的毫秒部分
%(relativeCreated)d:日誌建立時間 - 載入日誌模組的時間 的 毫秒數
%(thread)d:執行緒ID
%(threadName)s:執行緒名
%(process)d:程式ID
%(processName)s:程式名
%(message)s:日誌訊息
Filters
過濾器filter
用於提供對日誌記錄從logger
傳遞到handler
的附加控制
預設情況下,logger
和handler
將處理滿足日誌級別要求的任何日誌訊息,但是,通過安裝filter
,可以在日誌記錄過程中新增其他條件。例如,可以安裝僅允許ERROR
級別 來自特定源的訊息的filter。
filter還可用於在發出之前修改日誌記錄。例如,如果滿足一組特定條件,可以編寫一個過濾器,將ERROR
日誌記錄降級為WARNING
記錄。
filter可以安裝在logger
或handler
上; 可以在鏈中使用多個filter來執行多個過濾操作。
1.settings中配置
LOGGING = {
'filters': {
'require_debug_false': {
'()': 'django.utils.log.RequireDebugFalse',
},
'require_debug_true': {
'()': 'django.utils.log.RequireDebugTrue',
},
},
}
配置了2個過濾器
- require_debug_false:使用類:
RequireDebugFalse
- require_debug_true:使用類:
RequireDebugTrue
Handlers
這個類是確定logger
中訊息發生的引擎程式,描述特定的日誌記錄行為,譬如控制檯列印、寫入日誌檔案、通過網路進行傳送等
與logger
一樣,handler
也具有日誌級別,如果日誌記錄的日誌級別未達到或超過handler
的級別,則handler
將忽略該訊息。
一個logger
可以有多個handler
,每個handler
可以有不同的日誌級別和記錄方法
1.settings中配置
4個引數(如下),加上對應class
類的初始化引數
- class(必需):處理程式類的名稱
- level(可選的):處理程式的級別
- formatter(可選的):處理程式的格式化程式
- filters(可選的):處理程式的過濾器的列表
2.內建處理器
- python3的
logging
中的handler
:-
StreamHandler:輸出到
stream
,未指定則使用sys.stderr
輸出到控制檯 -
FileHandler:繼承自
StreamHandler
,輸出到檔案,預設情況下,檔案無限增長
初始化引數:filename,mode ='a',encoding = None,delay = False
delay如果為True,那麼會延遲到第一次呼叫emit寫入資料時才開啟檔案'handlers': { 'file': { 'level': 'DEBUG', 'class': 'logging.FileHandler', 'filename': '/path/to/django/app.log', #引數配置在這裡,多個引數按順序繼續配置即可, 如果要新增encoding,那麼在下面新增 encoding: 'utf-8' 即可 }, }
-
RotatingFileHandler:自動按大小切分的log檔案(
常用
)
初始化引數:filename,mode ='a',maxBytes = 0,backupCount = 0,encoding = None,delay = False
maxBytes:最大位元組數,超過時建立新的日誌檔案,如果backupCount
或maxBytes
有一個為0,那麼就一直使用一個檔案
backupCount:最大檔案個數,新檔案的副檔名是指定的檔案後加序號".1"等,譬如:backupCount=5
,基礎檔名為:app.log
,那麼達到指定maxBytes
之後,會關閉檔案app.log
,將app.log
重新命名為app.log.1
,如果app.log.1
存在,那麼就順推,先將app.log.1
重新命名為app.log.2
,再將現在的app.log
命名為app.log.1
,最大建立到app.log.5
(舊的app.log.5會被刪除),然後重新建立app.log
檔案進行日誌寫入,也就是永遠只會對app.log
檔案進行寫入。 -
TimedRotatingFileHandler:按時間自動切分的log檔案,檔案字尾
%Y-%m-%d_%H-%M-%S
初始化引數:filename, when='h', interval=1, backupCount=0, encoding=None, delay=False, utc=False, atTime=None
when:時間間隔型別,不區分大小寫'S':秒 'M':分鐘 'H':小時 'D':天 'W0'-'W6':星期幾(0 = 星期一) 'midnight':如果atTime未指定,則在 0點0分0秒 翻轉,否則在atTime時間翻轉
interval:間隔的數值
backupCount: 檔案個數
encoding:編碼
delay:True是寫入檔案時才開啟檔案,預設False,例項化時即開啟檔案
utc:False則使用當地時間,True則使用UTC時間
atTime:必須是datetime.time例項,指定檔案第一次切分的時間,when設定為S,M,H,D時,該設定會被忽略 -
SMTPHandler:通過email傳送日誌記錄訊息
初始化引數:mailhost, fromaddr, toaddrs, subject, credentials=None, secure=None, timeout=5.0
mailhost:發件人郵箱伺服器地址(預設25埠)或地址和指定埠的元組,如:('smtp.163.com', 25)
fromaddr:發件人郵箱
toaddrs:收件人郵箱列表
subject:郵件標題
credentials:如果郵箱伺服器需要登入,則傳遞(username, password)
元組
secure:使用TLS加密協議
-
Loggers
1.settings中配置
通過在settings
中配置LOGGING
配置項實現日誌配置,共4個配置項(都是可選的,不過一般會指定handler):
- level:指定記錄日誌的級別,沒有配置則處理所有級別的日子
- propagate:設定該記錄器的日誌是否傳播到父記錄器,不設定則是True
- filters:指定過濾器列表
- handlers:指定處理器列表
示例如下:
LOGGING = {
'version': 1, # 固定值,現在只有這一個版本
'disable_existing_loggers': False, # 設定已存在的logger不失效
'loggers': {
'': {
'handlers': ['console'],
},
'django': {
'handlers': ['console'],
'propagate': True,
},
'django.request': {
'handlers': ['mail_admins'],
'level': 'ERROR',
'propagate': False,
},
'myproject.custom': {
'handlers': ['console', 'mail_admins'],
'level': 'INFO',
'filters': ['special']
}
}
}
配置了4個 logger, 分別對應2個不同的handler(console輸出日誌到控制檯,mail_admins輸出日誌到郵件)
- '':預設的記錄器,不指定特定名稱,那麼就是使用這個記錄器,沒有配置level,那麼就是處理所有級別的日誌,傳遞所有級別的日誌到
console
控制器 - django:傳遞所有級別的日誌到console控制器
- django.request:django記錄器的子記錄器,處理ERROR級別及以上的日誌,propagate設定為 False,表明不傳播日誌給 "django",該logger傳遞日誌到mail_admins控制器
- myproject.custom:處理INFO級別及以上的日誌,應用了一個 special 的過濾器來過濾日誌,傳遞日誌到2個控制器(['console', 'mail_admins'])處理
注意
django框架有個預設的配置:DEFAULT_LOGGING
,一旦配置了自己的LOGGING
後,那麼所有的預設的LOGGER
全部都失效,失效不等於沒有記錄器了,而是說記錄器不起作用了,即不會記錄日誌,也不會將日誌傳播給父記錄器。因此你應該非常小心使用,因為你會感覺你丟了日誌一樣,可以手動設定同名的logger
實現覆蓋,如:
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'loggers': {
# 覆蓋了 django 記錄器,所有django的記錄日誌最後全部寫入到檔案中
'django': {
'handlers': ['file'],
'level': 'DEBUG',
'propagate': True,
},
},
}
2.django內建的logger
內建的logger
在django專案執行中會自動記錄日誌,與我們手動建立的logger
的執行沒有關係,除非我們也建立相同的logger
django框架呼叫的地方在:django.core.servers.basehttp中(如WSGIRequestHandler)
- django:django框架中所有訊息的記錄器,一般使用它的子記錄器,而不是它釋出訊息,因為預設情況下子記錄器的日誌會傳播到根記錄器django,除非設定 'propagate': False
- django.request:記錄與請求處理相關的訊息。5XX響應作為
ERROR
訊息; 4XX響應作為WARNING
訊息引發。記錄到django.security
記錄器的請求不會記錄到django.request
中
傳送給此記錄器的訊息具有以下額外上下文:
-
status_code:與請求關聯的HTTP響應程式碼
-
request:生成日誌訊息的請求物件。
-
django.server:記錄與
runserver
命令呼叫的伺服器接收的請求的處理相關的訊息。5XX響應記錄為ERROR
訊息,4XX響應記錄為WARNING
訊息,其他所有響應記錄為INFO
。
傳送給此記錄器的訊息具有以下額外上下文:- status_code:與請求關聯的HTTP響應程式碼
- request:生成日誌訊息的請求物件。
-
django.template:記錄與模板呈現相關的訊息
-
django.db.backends:記錄程式碼和資料庫互動相關的訊息
-
django.security.*:記錄任何
SuspiciousOperation
和其他安全相關錯誤(django.security.csrf )
的訊息 -
django.db.backends.schema:記錄資料庫遷移過程中的日誌,但是不記錄執行的查詢SQL語句等,傳送給此記錄器的訊息具有以下額外上下文:
- sql:已執行的SQL語句。
- params:SQL呼叫中使用的引數
實戰案例
如果你對以上的介紹覺得寫得很亂又複雜,沒關係,下面直接教你在專案中如何使用,基本就3種用法
- 通過檔案分割日誌
- 通過時間分割日誌
- 通過郵箱傳送日誌
案例1:通過檔案分割日誌
首先配置settings.py
中的logging
,程式碼如下
BASE_LOG_DIR = os.path.join(BASE_DIR, 'log')
LOGGING = {
'version': 1,
'disable_existing_loggers': False, # 設定已存在的logger不失效
'filters': {},
'formatters': {
'standard': {
'format': '[%(asctime)s][%(levelname)s][%(filename)s:%(lineno)d:%(funcName)s]:%(message)s',
'datefmt': '%Y-%m-%d %H:%M:%S'
},
'simple':{
'format':'[%(asctime)s][%(levelname)s]:%(message)s',
'datefmt': '%Y-%m-%d %H:%M:%S'
}
},
'handlers': {
'console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler',
'formatter': 'simple'
},
'default': {
'level': 'DEBUG',
'class': 'logging.handlers.RotatingFileHandler',
'filename': os.path.join(BASE_LOG_DIR, 'debug.log'),
'maxBytes': 1024 * 1024 * 50, # 日誌大小50M
'backupCount': 5,
'formatter': 'standard',
'encoding': 'utf-8',
},
},
'loggers': {
'django': {
'handlers': ['console', 'default'],
'level': 'INFO',
'propagate': True
},
},
}
接下來在views.py
和urls.py
函式中寫入函式,程式碼如下
# urls.py
urlpatterns = [
path('', views.index, name="index"),
]
# views.py
logger = logging.getLogger('django')
def index(request):
logger.debug('debug 測試')
logger.info('info 測試')
logger.warning('warning 測試')
logger.error('error 測試')
return HttpResponse('success')
然後我們訪問127.0.0.1/logging/
,我們可以看到控制檯的程式碼
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
[2021-05-30 15:03:09][INFO]:info 測試
[2021-05-30 15:03:09][WARNING]:warning 測試
[2021-05-30 15:03:09][ERROR]:error 測試
[2021-05-30 15:03:09][INFO]:"GET /logging/ HTTP/1.1" 200 7
這是因為我們在django
記錄器中配置了console
控制器,格式要求也是符合我們所寫的,接著檢視專案的log
目錄下會新增了一個debug.log
這樣一個日誌檔案,檔案內容如下
[2021-05-30 15:03:04][INFO][autoreload.py:578:run_with_reloader]:Watching for file changes with StatReloader
[2021-05-30 15:03:09][INFO][views.py:12:index]:info 測試
[2021-05-30 15:03:09][WARNING][views.py:13:index]:warning 測試
[2021-05-30 15:03:09][ERROR][views.py:14:index]:error 測試
[2021-05-30 15:03:09][INFO][basehttp.py:154:log_message]:"GET /logging/ HTTP/1.1" 200 7
debug.log
日誌輸出格式更加詳細,這是因為我們在default
控制器中,使用的standard
格式器。
總結:以上就是我們最常用的一種日誌配置---檔案日誌,當中的細節例如格式啊等等的可以自己更改
案例2:時間分割日誌
程式碼設定如下:
'time_handler': {
'level': 'INFO',
'class': 'logging.handlers.TimedRotatingFileHandler',
'filename': os.path.join(BASE_LOG_DIR, "time.log"),
'when': 'S',
'interval': 10,
'backupCount': 5,
'formatter': 'standard',
'encoding': 'utf-8',
}
以上設定為間隔10秒,生成一個日誌檔案
案例3:日誌郵箱傳送
程式碼設定如下:
'email_handler': {
'level': 'ERROR',
'class': 'logging.handlers.SMTPHandler',
'formatter': 'standard',
'mailhost': ('smtp.163.com', 25),
'fromaddr': 'xxxx@163.com',
'toaddrs': ['xxx@qq.com'],
'subject': 'test',
'credentials': ('郵箱使用者名稱', '郵箱密碼'),
},
接下來出現ERROR級別的日誌,就會傳送郵件,如果你出現報錯程式碼為550
,那麼就是你郵箱的許可權沒有開通,到郵箱的設定中開啟SMTP
服務即可