Django(37)配置django日誌

Silent丿丶黑羽發表於2021-05-30

前言

  django框架的日誌通過python內建的logging模組實現的,既可以記錄自定義的一些資訊描述,也可以記錄系統執行中的一些物件資料,還可以記錄包括堆疊跟蹤、錯誤程式碼之類的詳細資訊。
  logging主要由4部分組成:LoggersHandlersFiltersFormatters
 

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的附加控制
​預設情況下,loggerhandler將處理滿足日誌級別要求的任何日誌訊息,但是,通過安裝filter,可以在日誌記錄過程中新增其他條件。例如,可以安裝僅允許ERROR級別 來自特定源的訊息的filter。
​filter還可用於在發出之前修改日誌記錄。例如,如果滿足一組特定條件,可以編寫一個過濾器,將ERROR日誌記錄降級為WARNING記錄。
​filter可以安裝在loggerhandler上; 可以在鏈中使用多個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.內建處理器

  1. 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:最大位元組數,超過時建立新的日誌檔案,如果backupCountmaxBytes有一個為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.pyurls.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服務即可

相關文章