Django(37)配置django日誌
前言
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 //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
服務即可