# 日志
## 日志快速入門
Django 使用Python 內建的[`logging`](https://docs.python.org/3/library/logging.html#module-logging) 模塊打印日志。該模塊的用法在Python 本身的文檔中有詳細的討論。如果你從來沒有使用過Python 的logging 框架(或者即使使用過),請參見下面的快速導論。
### logging 的組成
Python 的logging 配置由四個部分組成:
* [Loggers](#topic-logging-parts-loggers)
* [Handlers](#topic-logging-parts-handlers)
* [Filters](#topic-logging-parts-filters)
* [Formatters](#topic-logging-parts-formatters)
#### Loggers
Logger 為日志系統的入口。每個logger 是一個具名的容器,可以向它寫入需要處理的消息。
每個logger 都有一個_日志級別_。日志級別表示該logger 將要處理的消息的嚴重性。Python 定義以下幾種日志級別:
* `DEBUG`:用于調試目的的底層系統信息
* `INFO`:普通的系統信息
* `WARNING`:表示出現一個較小的問題。
* `ERROR`:表示出現一個較大的問題。
* `CRITICAL`:表示出現一個致命的問題。
寫入logger 的每條消息都是一個_日志記錄_。每個日志記錄也具有一個_日志級別_,它表示對應的消息的嚴重性。每個日志記錄還可以包含描述正在打印的事件的有用元信息。這些元信息可以包含很多細節,例如回溯棧或錯誤碼。
當給一條消息給logger 時,會將消息的日志級別與logger 的日志級別進行比較。如果消息的日志級別大于等于logger 的日志級別,該消息將會往下繼續處理。如果小于,該消息將被忽略。
Logger 一旦決定消息需要處理,它將傳遞該消息給一個_Handler_。
#### Handlers
Handler 決定如何處理logger 中的每條消息。它表示一個特定的日志行為,例如將消息寫到屏幕上、寫到文件中或者寫到網絡socket。
與logger 一樣,handler 也有一個日志級別。如果消息的日志級別小于handler 的級別,handler 將忽略該消息。
Logger 可以有多個handler,而每個handler 可以有不同的日志級別。利用這種方式,可以根據消息的重要性提供不同形式的處理。例如,你可以用一個handler 將`ERROR` 和 `CRITICAL` 消息發送給一個頁面服務,而用另外一個hander 將所有的消息(包括 `ERROR` 和`CRITICAL` 消息)記錄到一個文件中用于以后進行分析。
#### Filters
Filter 用于對從logger 傳遞給handler 的日志記錄進行額外的控制。
默認情況下,滿足日志級別的任何消息都將被處理。通過安裝一個filter,你可以對日志處理添加額外的條件。例如,你可以安裝一個filter,只允許處理來自特定源的`ERROR` 消息。
Filters 還可以用于修改將要處理的日志記錄的優先級。例如,如果日志記錄滿足特定的條件,你可以編寫一個filter 將日志記錄從`ERROR` 降為`WARNING`。
Filters 可以安裝在logger 上或者handler 上;多個filter 可以串聯起來實現多層filter 行為。
#### Formatters
最后,日志記錄需要轉換成文本。Formatter 表示文本的格式。Fomatter 通常由包含[_日志記錄屬性_](https://docs.python.org/3/library/logging.html#logrecord-attributes)的Python 格式字符串組成;你也可以編寫自定義的fomatter 來實現自己的格式。
## 使用logging
配置好logger、handler、filter 和formatter 之后,你需要在代碼中放入logging 調用。使用logging 框架非常簡單。下面是個例子:
```
# import the logging library
import logging
# Get an instance of a logger
logger = logging.getLogger(__name__)
def my_view(request, arg1, arg):
...
if bad_mojo:
# Log an error message
logger.error('Something went wrong!')
```
就是這樣!每次滿足`bad_mojo` 條件,將寫入一條錯誤日志記錄。
### 命名logger
[`logging.getLogger()`](https://docs.python.org/3/library/logging.html#logging.getLogger) 調用獲取(如有必要則創建)一個logger 的實例。Logger 實例通過名字標識。Logger 使用名稱的目的是用于標識其配置。
Logger 的名稱習慣上通常使用`__name__`,即包含該logger 的Python 模塊的名字。這允許你基于模塊filter 和handle 日志調用。如果你想使用其它方式組織日志消息,可以提供點號分隔的名稱來標識你的logger:
```
# Get an instance of a specific named logger
logger = logging.getLogger('project.interesting.stuff')
```
點號分隔的logger 名稱定義一個層級。`project.interesting` logger 被認為是 `project.interesting.stuff` logger 的上一級;`project` logger 是`project.interesting` logger 的上一級。
層級為何如此重要?因為可以設置logger _傳播_它們的logging 調用給它們的上一級。利用這種方式,你可以在根logger 上定義一系列的handler,并捕獲子logger 中的所有logging 調用。在`project`命名空間中定義的handler 將捕獲`project.interesting` 和`project.interesting.stuff` logger 上的所有日志消息。
這種傳播行為可以基于每個logger 進行控制。如果你不想讓某個logger 傳播消息給它的上一級,你可以關閉這個行為。
### logging 調用
Logger 實例為每個默認的日志級別提供一個入口方法:
* `logger.debug()`
* `logger.info()`
* `logger.warning()`
* `logger.error()`
* `logger.critical()`
還有另外兩個調用:
* `logger.log()`:打印消息時手工指定日志級別。
* `logger.exception()`:創建一個`ERROR` 級別日志消息,它封裝當前異常棧的幀。
## 配置logging
當然,只是將logging 調用放入你的代碼中還是不夠的。你還需要配置logger、handler、filter 和formatter 來確保日志的輸出是有意義的。
Python 的logging 庫提供幾種配置logging 的技術,從程序接口到配置文件。默認情況下,Django 使用[dictConfig 格式](https://docs.python.org/library/logging.config.html#configuration-dictionary-schema)。
為了配置logging,你需要使用[`LOGGING`](../ref/settings.html#std:setting-LOGGING) 來定義字典形式的logging 設置。這些設置描述你的logging 設置的logger、handler、filter 和formatter,以及它們的日志等級和其它屬性。
默認情況下,[`LOGGING`](../ref/settings.html#std:setting-LOGGING) 設置與[_Django 的默認logging 配置_](#default-logging-configuration)進行合并。
如果[`LOGGING`](../ref/settings.html#std:setting-LOGGING) 中的`disable_existing_loggers` 鍵為`True`(默認值),那么默認配置中的所有logger 都將禁用。Logger 的禁用與刪除不同;logger 仍然存在,但是將默默丟棄任何傳遞給它的信息,也不會傳播給上一級logger。所以,你應該非常小心使用`'disable_existing_loggers': True`;它可能不是你想要的。你可以設置`disable_existing_loggers` 為`False`,并重新定義部分或所有的默認loggers;或者你可以設置[`LOGGING_CONFIG`](../ref/settings.html#std:setting-LOGGING_CONFIG) 為 `None`,并 [_自己處理logging 配置_](#disabling-logging-configuration)。
Logging 的配置屬于Django `setup()` 函數的一部分。所以,你可以肯定在你的項目代碼中logger 是永遠可用的。
### 示例
[dictConfig 格式](https://docs.python.org/library/logging.config.html#configuration-dictionary-schema)的完整文檔是logging 字典配置最好的信息源。但是為了讓你嘗嘗,下面是幾個例子。
首先,下面是一個簡單的配置,它將來自[_django.request_](#django-request-logger) logger 的所有日志請求寫入到一個本地文件:
```
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'file': {
'level': 'DEBUG',
'class': 'logging.FileHandler',
'filename': '/path/to/django/debug.log',
},
},
'loggers': {
'django.request': {
'handlers': ['file'],
'level': 'DEBUG',
'propagate': True,
},
},
}
```
如果你使用這個示例,請確保修改`'filename'` 路徑為運行Django 應用的用戶有權限寫入的一個位置。
其次,下面這個示例演示如何讓日志系統將Django 的日志打印到控制臺。`django.request` 和`django.security` 不會傳播日志給上一級。它在本地開發期間可能有用。
默認情況下,這個配置只會將`INFO` 和更高級別的日志發送到控制臺。Django 中這樣的日志信息不多。可以設置環境變量`DJANGO_LOG_LEVEL=DEBUG` 來看看Django 的debug 日志,它包含所有的數據庫查詢所以非常詳盡。
```
import os
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'console': {
'class': 'logging.StreamHandler',
},
},
'loggers': {
'django': {
'handlers': ['console'],
'level': os.getenv('DJANGO_LOG_LEVEL', 'INFO'),
},
},
}
```
最后,下面是相當復雜的一個logging 設置:
```
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'verbose': {
'format': '%(levelname)s %(asctime)s %(module)s %(process)d %(thread)d %(message)s'
},
'simple': {
'format': '%(levelname)s %(message)s'
},
},
'filters': {
'special': {
'()': 'project.logging.SpecialFilter',
'foo': 'bar',
}
},
'handlers': {
'null': {
'level': 'DEBUG',
'class': 'logging.NullHandler',
},
'console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler',
'formatter': 'simple'
},
'mail_admins': {
'level': 'ERROR',
'class': 'django.utils.log.AdminEmailHandler',
'filters': ['special']
}
},
'loggers': {
'django': {
'handlers': ['null'],
'propagate': True,
'level': 'INFO',
},
'django.request': {
'handlers': ['mail_admins'],
'level': 'ERROR',
'propagate': False,
},
'myproject.custom': {
'handlers': ['console', 'mail_admins'],
'level': 'INFO',
'filters': ['special']
}
}
}
```
這個logging 配置完成以下事情:
* 以‘dictConfig version 1’格式解析配置。目前為止,這是dictConfig 格式唯一的版本。
* 定義兩個formatter:
* `simple`,它只輸出日志的級別(例如,`DEBUG`)和日志消息。
`format` 字符串是一個普通的Python 格式化字符串,描述每行日志的細節。輸出的完整細節可以在[formatter 文檔](https://docs.python.org/library/logging.html#formatter-objects)中找到。
* `verbose`,它輸出日志級別、日志消息,以及時間、進程、線程和生成日志消息的模塊。
* 定義filter —— `project.logging.SpecialFilter`,并使用別名`special`。如果filter 在構造時要求額外的參數,可以在filter 的配置字段中用額外的鍵提供。在這個例子中,在實例化`SpecialFilter` 時,`foo` 參數的值將使用`bar`。
* 定義三個handler:
* `null`,一個NullHandler,它傳遞`DEBUG`(和更高級)的消息給`/dev/null`。
* `console`,一個StreamHandler,它將打印`DEBUG`(和更高級)的消息到stderr。這個handler 使用`simple` 輸出格式。
* `mail_admins`,一個AdminEmailHandler,它將用郵件發送`ERROR`(和更高級)的消息到站點管理員。這個handler 使用`special` filter。
* 配置三個logger:
* `django`,它傳遞所有`INFO` 和更高級的消息給`null` handler。
* `django.request`,它傳遞所有`ERROR` 消息給`mail_admins` handler。另外,標記這個logger _不_ 向上傳播消息。這表示寫入`django.request` 的日志信息將不會被`django` logger 處理。
* `myproject.custom`,它傳遞所有`INFO` 和更高級的消息并通過`special` filter 的消息給兩個handler —— `console`和`mail_admins`。這表示所有`INFO`(和更高級)的消息將打印到控制臺上;`ERROR` 和`CRITICAL` 消息還會通過郵件發送出來。
### 自定義logging 配置
如果你不想使用Python 的dictConfig 格式配置logger,你可以指定你自己的配置模式。
[`LOGGING_CONFIG`](../ref/settings.html#std:setting-LOGGING_CONFIG) 設置定義一個可調用對象,將它用來配置Django 的logger。默認情況下,它指向Python 的[`logging.config.dictConfig()`](https://docs.python.org/3/library/logging.config.html#logging.config.dictConfig) 函數。但是,如果你想使用不同的配置過程,你可以使用其它只接受一個參數的可調用對象。配置logging 時,將使用[`LOGGING`](../ref/settings.html#std:setting-LOGGING) 的內容作為參數的值。
### 禁用logging 配置
如果你完全不想配置logging(或者你想使用自己的方法手工配置logging),你可以設置[`LOGGING_CONFIG`](../ref/settings.html#std:setting-LOGGING_CONFIG) 為`None`。這將禁用[_Django 默認logging_](#default-logging-configuration) 的配置過程。下面的示例禁用Django 的logging 配置,然后手工配置logging:
settings.py
```
LOGGING_CONFIG = None
import logging.config
logging.config.dictConfig(...)
```
設置[`LOGGING_CONFIG`](../ref/settings.html#std:setting-LOGGING_CONFIG) 為`None` 只表示禁用自動配置過程,而不是禁用logging 本身。如果你禁用配置過程,Django 仍然執行logging 調用,只是調用的是默認定義的logging 行為。
## Django’s logging extensions
Django 提供許多工具用于處理在網站服務器環境中獨特的日志需求。
### Loggers
Django 提供幾個內建的logger。
#### django
`django` 是一個捕獲所有信息的logger。消息不會直接提交給這個logger。
#### django.request
記錄與處理請求相關的消息。5XX 響應作為`ERROR` 消息;4XX 響應作為`WARNING` 消息。
這個logger 的消息具有以下額外的上下文:
* `status_code`:請求的HTTP 響應碼。
* `request`:生成日志信息的請求對象。
#### django.db.backends
與數據庫交互的代碼相關的消息。例如,HTTP請求執行應用級別的SQL 語句將以`DEBUG` 級別記錄到該logger。
這個logger 的消息具有以下額外的上下文:
* `duration`:執行SQL 語句花費的時間。
* `sql`:執行的SQL 語句。
* `params`:SQL 調用中用到的參數。
由于性能原因,SQL的日志只在`設置`之后開啟。DEBUG 設置為`True`,無論日志級別或者安裝的處理器是什么。
這里的日志不包含框架級別的的初始化(例如,`SET TIMEZONE`)和事務管理查詢(例如,`BEGIN`、`COMMIT` 和`ROLLBACK`)。如果你希望看到所有的數據庫查詢,可以打開數據庫中的查詢日志。
#### django.security.*
Security logger 將收到任何出現[`SuspiciousOperation`](../ref/exceptions.html#django.core.exceptions.SuspiciousOperation "django.core.exceptions.SuspiciousOperation") 的消息。SuspiciousOperation 的每個子類型都有一個子logger。日志的級別取決于異常處理的位置。大部分情況是一個warning 日志,而如果`SuspiciousOperation` 到達WSGI handler 則記錄為一個error。例如,如果請求中包含的HTTP `Host` 頭部與[`ALLOWED_HOSTS`](../ref/settings.html#std:setting-ALLOWED_HOSTS) 不匹配,Django 將返回400 響應,同時將記錄一個error 消息到`django.security.DisallowedHost` logger。
默認情況下只會配置`django.security` logger,其它所有的子logger 都將傳播給上一級logger。`django.security` logger 的配置與`django.request` logger 相同,任何error 消息將用郵件發送給站點管理員。由于`SuspiciousOperation` 導致400 響應的請求不會在`django.request` logger 中記錄日志,而只在`django.security` logger 中記錄日志。
若要默默丟棄某種類型的SuspiciousOperation,你可以按照下面的示例覆蓋其logger:
```
'loggers': {
'django.security.DisallowedHost': {
'handlers': ['null'],
'propagate': False,
},
},
```
#### django.db.backends.schema
New in Django 1.7\.
當[_遷移框架_](migrations.html)執行的SQL 查詢會改變數據庫的模式時,則記錄這些SQL 查詢。注意,它不會記錄[`RunPython`](../ref/migration-operations.html#django.db.migrations.operations.RunPython "django.db.migrations.operations.RunPython") 執行的查詢。
### Handlers
在Python logging 模塊提供的handler 基礎之上,Django 還提供另外一個handler。
_class _`AdminEmailHandler`(_include_html=False_, _email_backend=None_)[[source]](../_modules/django/utils/log.html#AdminEmailHandler)
這個handler 將它收到的每個日志信息用郵件發送給站點管理員。
如果日志記錄包含`request` 屬性,該請求的完整細節都將包含在郵件中。
如果日志記錄包含棧回溯信息,該棧回溯也將包含在郵件中。
`AdminEmailHandler` 的`include_html` 參數用于控制郵件中是否包含HTML 附件,這個附件包含[`DEBUG`](../ref/settings.html#std:setting-DEBUG) 為`True` 時的完整網頁。若要在配置中設置這個值,可以將它包含在`django.utils.log.AdminEmailHandler` handler 的定義中,像下面這樣:
```
'handlers': {
'mail_admins': {
'level': 'ERROR',
'class': 'django.utils.log.AdminEmailHandler',
'include_html': True,
}
},
```
注意,郵件中的HTML 包含完整的回溯棧,包括棧每個層級局部變量的名稱和值以及你的Django 設置。這些信息可能非常敏感,你也許不想通過郵件發送它們。此時可以考慮使用類似[Sentry](https://pypi.python.org/pypi/sentry) 這樣的東西,回溯棧的完整信息和安全信息_不會_ 通過郵件發送。你還可以從錯誤報告中顯式過濾掉特定的敏感信息 —— 更多信息參見[_過濾錯誤報告_](../howto/error-reporting.html#filtering-error-reports)。
通過設置`AdminEmailHandler` 的`email_backend` 參數,可以覆蓋handler 使用的[_email backend_](email.html#topic-email-backends),像這樣:
```
'handlers': {
'mail_admins': {
'level': 'ERROR',
'class': 'django.utils.log.AdminEmailHandler',
'email_backend': 'django.core.mail.backends.filebased.EmailBackend',
}
},
```
默認情況下,將使用[`EMAIL_BACKEND`](../ref/settings.html#std:setting-EMAIL_BACKEND) 中指定的郵件后端。
`send_mail`(_subject_, _message_, _*args_, _**kwargs_)[[source]](../_modules/django/utils/log.html#AdminEmailHandler.send_mail)
New in Django 1.8\.
發送郵件給管理員用戶。若要自定它的行為,可以子類化[`AdminEmailHandler`](#django.utils.log.AdminEmailHandler "django.utils.log.AdminEmailHandler") 類并覆蓋這個方法。
### Filters
在Python logging 模塊提供的過濾器的基礎之上,Django 還提供兩個過濾器。
_class _`CallbackFilter`(_callback_)[[source]](../_modules/django/utils/log.html#CallbackFilter)
這個過濾器接受一個回調函數(它接受一個單一參數,也就是要記錄的東西),并且對每個傳遞給過濾器的記錄調用它。如果回調函數返回False,將不會進行記錄的處理。
例如,要從admin郵件中過濾掉[`UnreadablePostError`](../ref/exceptions.html#django.http.UnreadablePostError "django.http.UnreadablePostError")(只在用戶取消上傳時產生),你可以創建一個過濾器函數:
```
from django.http import UnreadablePostError
def skip_unreadable_post(record):
if record.exc_info:
exc_type, exc_value = record.exc_info[:2]
if isinstance(exc_value, UnreadablePostError):
return False
return True
```
然后把它添加到logger的配置中:
```
'filters': {
'skip_unreadable_posts': {
'()': 'django.utils.log.CallbackFilter',
'callback': skip_unreadable_post,
}
},
'handlers': {
'mail_admins': {
'level': 'ERROR',
'filters': ['skip_unreadable_posts'],
'class': 'django.utils.log.AdminEmailHandler'
}
},
```
_class _`RequireDebugFalse`[[source]](../_modules/django/utils/log.html#RequireDebugFalse)
這個過濾器只在設置后傳遞記錄。DEBUG 為 False。
這個過濾器遵循[`LOGGING`](../ref/settings.html#std:setting-LOGGING) 默認的配置,以確保[`AdminEmailHandler`](#django.utils.log.AdminEmailHandler "django.utils.log.AdminEmailHandler")只在[`DEBUG`](../ref/settings.html#std:setting-DEBUG)為`False`的時候發送錯誤郵件。
```
'filters': {
'require_debug_false': {
'()': 'django.utils.log.RequireDebugFalse',
}
},
'handlers': {
'mail_admins': {
'level': 'ERROR',
'filters': ['require_debug_false'],
'class': 'django.utils.log.AdminEmailHandler'
}
},
```
_class _`RequireDebugTrue`[[source]](../_modules/django/utils/log.html#RequireDebugTrue)
這個過濾器類似于[`RequireDebugFalse`](#django.utils.log.RequireDebugFalse "django.utils.log.RequireDebugFalse"),除了記錄只在[`DEBUG`](../ref/settings.html#std:setting-DEBUG) 為 `True`時傳遞的情況。
## Django’s default logging configuration
默認情況下,Django 的logging 配置如下:
當[`DEBUG`](../ref/settings.html#std:setting-DEBUG) 為`True` 時:
* `django`的全局logger會向控制臺發送級別等于或高級`INFO`的所有消息。Django在這個時候并不會做任何日志調用(所有在`DEBUG`級別上的日志,或者被`django.request` 和 `django.security`處理的日志)。
* `py.warnings` logger,它處理來自`warnings.warn()`的消息,會向控制臺發送消息。
當[`DEBUG`](../ref/settings.html#std:setting-DEBUG) 為`False` 時:
* `django.request` 和`django.security` loggers 向[`AdminEmailHandler`](#django.utils.log.AdminEmailHandler "django.utils.log.AdminEmailHandler")發送帶有`ERROR` 或 `CRITICAL`級別的消息。這些logger 會忽略任何級別等于或小于`WARNING`的信息,被記錄的日志不會傳遞給其他logger(它們不會傳遞給`django`的全局 logger,即使`DEBUG` 為 `True`)。
另見[_配置日志_](#configuring-logging)來了解如何補充或者替換默認的日志配置。
> 譯者:[Django 文檔協作翻譯小組](http://python.usyiyi.cn/django/index.html),原文:[Logging](https://docs.djangoproject.com/en/1.8/topics/logging/)。
>
> 本文以 [CC BY-NC-SA 3.0](http://creativecommons.org/licenses/by-nc-sa/3.0/cn/) 協議發布,轉載請保留作者署名和文章出處。
>
> [Django 文檔協作翻譯小組](http://python.usyiyi.cn/django/index.html)人手緊缺,有興趣的朋友可以加入我們,完全公益性質。交流群:467338606。
- 新手入門
- 從零開始
- 概覽
- 安裝
- 教程
- 第1部分:模型
- 第2部分:管理站點
- 第3部分:視圖和模板
- 第4部分:表單和通用視圖
- 第5部分:測試
- 第6部分:靜態文件
- 高級教程
- 如何編寫可重用的應用
- 為Django編寫首個補丁
- 模型層
- 模型
- 模型語法
- 元選項
- 模型類
- 查詢集
- 執行查詢
- 查找表達式
- 模型的實例
- 實例方法
- 訪問關聯對象
- 遷移
- 模式編輯器
- 編寫遷移
- 高級
- 管理器
- 原始的SQL查詢
- 聚合
- 多數據庫
- 自定義查找
- 條件表達式
- 數據庫函數
- 其它
- 遺留的數據庫
- 提供初始數據
- 優化數據庫訪問
- 視圖層
- 基礎
- URL配置
- 視圖函數
- 快捷函數
- 裝飾器
- 參考
- 內建的視圖
- TemplateResponse 對象
- 文件上傳
- 概覽
- File 對象
- 儲存API
- 管理文件
- 自定義存儲
- 基于類的視圖
- 概覽
- 內建顯示視圖
- 內建編輯視圖
- API參考
- 分類索引
- 高級
- 生成 CSV
- 生成 PDF
- 中間件
- 概覽
- 內建的中間件類
- 模板層
- 基礎
- 面向設計師
- 語言概覽
- 人性化
- 面向程序員
- 表單
- 基礎
- 概覽
- 表單API
- 內建的Widget
- 高級
- 整合媒體
- 開發過程
- 設置
- 概覽
- 應用程序
- 異常
- 概覽
- django-admin 和 manage.py
- 添加自定義的命令
- 測試
- 介紹
- 部署
- 概述
- WSGI服務器
- 部署靜態文件
- 通過email追蹤代碼錯誤
- Admin
- 管理操作
- 管理文檔生成器
- 安全
- 安全概述
- 說明Django中的安全問題
- 點擊劫持保護
- 加密簽名
- 國際化和本地化
- 概述
- 本地化WEB UI格式化輸入
- “本地特色”
- 常見的網站應用工具
- 認證
- 概覽
- 使用認證系統
- 密碼管理
- 日志
- 分頁
- 會話
- 數據驗證
- 其它核心功能
- 按需內容處理
- 重定向
- 信號
- 系統檢查框架