天天看點

python logging子產品的作用_python中logging子產品下篇

本文承接上一篇分為如下幾個部分日志輸出

捕獲異常

配置共享

配置到檔案

使用規範

參考資料

日志輸出

我們之前都是将日志輸出到控制台,而實際項目中常常需要将日志存儲為檔案,我們直接看代碼

import logging

logging.basicConfig(level=logging.INFO,

format='%(asctime)s%(message)s',

datefmt='%a,%d%b %Y %H:%M:%S +0000',

filename='my.log')

logging.info('this is a info')

可以看到,隻要在logging.basicConfig中加入filename參數,就不會再在控制台中輸出日志,而是會将所有日志存入my.log檔案中。

需要注意的一點是:上面日志存儲方式是追加的,也就是說,上面這個代碼連續運作兩次,檔案中是會有兩行日志的。

如果需要改變輸出形式,需要調整參數如下

import logging

logging.basicConfig(level=logging.INFO,

format='%(asctime)s%(message)s',

datefmt='%a,%d%b %Y %H:%M:%S +0000',

filename='my.log', filemode='w')

logging.info('this is a info')

其他讀寫格式可以參考檔案讀寫格式。

指定日志輸出也可以不在logging.basicConfig中配置,而是單獨設定

import logging

logger = logging.getLogger(__name__)

logger.setLevel(level=logging.INFO)

handler = logging.FileHandler('my.log')

formatter = logging.Formatter('%(asctime)s%(message)s')

handler.setFormatter(formatter)

logger.addHandler(handler)

logger.critical('Critical 50')

logger.error('Error 40')

logger.warning('Warning 30')

logger.info('Info 20')

logger.debug('Debug 10')

這可以達到和剛才相同的效果。我們可以看到,不僅輸出方式可以單獨配置,而且format也可以單獨配置。

配置它們的流程是配置好的format設定到handler中

配置好的handler添加到logger中

添加多個輸出端,且不同輸出端輸出内容不一樣(level不同,format不同)

import logging

import sys

logger = logging.getLogger(__name__)

logger.setLevel(level=logging.DEBUG) # 設定基本level

# 輸出到控制台,不設定format

handler_stream = logging.StreamHandler(sys.stdout)

handler_stream.setLevel(level=logging.WARN) # 更改level

logger.addHandler(handler_stream)

# 輸出到檔案,繼承基礎level

handler_file = logging.FileHandler('my.log', 'w')

formatter = logging.Formatter('%(asctime)s%(message)s')

handler_file.setFormatter(formatter) # 設定format

logger.addHandler(handler_file)

logger.critical('Critical 50')

logger.error('Error 40')

logger.warning('Warning 30')

logger.info('Info 20')

logger.debug('Debug 10')

控制台輸出結果為

Critical 50

Error 40

Warning 30

my.log檔案輸出結果為

2018-07-01 21:03:27,164 Critical 50

2018-07-01 21:03:27,165 Error 40

2018-07-01 21:03:27,167 Warning 30

2018-07-01 21:03:27,171 Info 20

2018-07-01 21:03:27,172 Debug 10

這裡需要注意一點,基礎level要設定比較低一些,後面handler設定的level隻有比基礎高才有效。

logging子產品還提供了許多其他的日志輸出形式,詳情可以見官網

捕獲異常

我們希望将程式的報錯資訊記錄到log檔案中

import logging

logging.basicConfig(level=logging.INFO,

format='%(asctime)s%(message)s',

datefmt='%a,%d%b %Y %H:%M:%S +0000',

filename='my.log')

logging.info('this is a info')

try:

do

except Exception:

logging.error('There are something wrong', exc_info=True)

logging.info('continue')

輸出到檔案的結果如下

Sun, 01 Jul 2018 21:10:43 +0000 this is a info

Sun, 01 Jul 2018 21:10:53 +0000 this is a info

Sun, 01 Jul 2018 21:10:53 +0000 There are something wrong

Traceback (most recent call last):

File "learn.py", line 9, in

do

NameError: name 'do' is not defined

Sun, 01 Jul 2018 21:10:53 +0000 continue

其中exc_info的作用就是在日志中包含具體的報錯資訊,預設是False不包含。

配置共享

在寫一個項目時,需要編寫多個檔案,每個檔案内都要設定相同格式的日志輸出,如果每個檔案都重新配置一遍就太麻煩了,logging子產品為我們提供了一個簡單的方法。

比如在main.py檔案中編寫如下代碼

import logging

import a

logging.basicConfig(level=logging.INFO,

format='%(asctime)s%(message)s',

datefmt='%a,%d%b %Y %H:%M:%S +0000')

logger = logging.getLogger('mainlogger')

logger.info('main file log')

a.run()

在a.py檔案中編寫如下代碼

import logging

logger = logging.getLogger('mainlogger.a')

def run():

logger.info('a file log')

運作main.py檔案,輸出結果如下

Sun, 01 Jul 2018 21:19:57 +0000 main file log

Sun, 01 Jul 2018 21:19:57 +0000 a file log

我們可以看到,在a.py檔案中隻是将logger名稱設定為以main.py檔案中的logger名稱開頭,就可以繼承main.py檔案中的配置了,甚至不需要在a.py檔案中導入main.py檔案。

配置到檔案

我們不僅可以通過python代碼進行logging配置,而且可以通過寫一個yaml檔案進行配置,每次需要用logging時隻要調用這個檔案就配置完成。

config.yaml檔案内容如下

version: 1

formatters:

simple:

format: "%(message)s"

more:

format: "%(asctime)s - %(levelname)s - %(message)s"

handlers:

console:

class : logging.StreamHandler

formatter: simple

level: INFO

stream: ext://sys.stdout

file:

class: logging.FileHandler

formatter: more

level: DEBUG

filename: debug.log

loggers:

mainlogger:

level: DEBUG

handlers: [console, file]

root:

level: DEBUG

handlers: [console]

main.py檔案中編寫代碼如下

import logging

import logging.config

import yaml

import a

with open('config.yaml', 'r', encoding='utf-8') as f:

config = yaml.load(f)

logging.config.dictConfig(config)

logging.info('main file log')

a.run()

a.py檔案中編寫代碼如下

import logging

logger = logging.getLogger('mainlogger')

def run():

logger.info('a file log')

運作main.py結果在控制台中輸出如下

a file log

INFO:mainlogger:a file log

在debug.log檔案中輸出如下

2018-07-01 21:45:37,673 - INFO - a file log

對于這樣的輸出結果,我們回到yaml檔案理一下思路,對這個檔案從下往上看首先對于不同的logger名稱,如果名稱是mainlogger,則使用[console, file]這兩個handler,如果未指定(使用logging.info進行輸出)則對應root隻按照console輸出

對于不同的handler,名稱為console的handler使用logging.StreamHandler輸出到控制台,調用simple的format,而file則輸出到檔案,使用more的format

對于不同的format格式則在最上面的formatters中定義

通過檔案配置的更多内容可以參考官網

使用規範

1.輸出字元串的規範

import logging

logging.basicConfig(level=logging.INFO,

format='%(asctime)s%(levelname)s%(message)s')

info = 'apple'

# bad

logging.info('this is a{}'.format(info))

# good

logging.info('this is a%s', info)

二者輸出皆為

2018-07-01 21:56:12,969 INFO this is a apple

2.異常處理規範

import logging

logging.basicConfig(level=logging.INFO,

format='%(asctime)s%(message)s',

datefmt='%a,%d%b %Y %H:%M:%S +0000')

logging.info('this is a info')

try:

do

except Exception as e:

# bad

logging.error('There are something wrong:%s', e)

# good

logging.error('There are something wrong', exc_info=True)

# good

logging.exception('There are something wrong')

logging.info('continue')

第一種輸出異常結果會是

Sun, 01 Jul 2018 21:58:52 +0000 There are something wrong: name 'do' is not defined

後兩種輸出異常結果會是

Sun, 01 Jul 2018 21:58:52 +0000 There are something wrong

Traceback (most recent call last):

File "learn.py", line 8, in

do

NameError: name 'do' is not defined

3.日志級别設定

這裡列出這篇文章中認為的日志級别使用方法最細微的資訊記錄到debug中,這個級别就是用來debug的,看程式在哪一次疊代中發生了錯誤,比如每次循環都輸出一些東西用debug級别

info級别用于routines,也就是輸出start finish 狀态改變等資訊

warn輸出一些相對重要,但是不是程式bug的資訊,比如輸入了錯誤的密碼,或者連接配接較慢

error輸出程式bug,列印異常資訊

critical用于處理一些非常糟糕的事情,比如記憶體溢出、磁盤已滿,這個一般較少使用

4.建議使用RotatingFileHandler而不是FileHandler

因為日志檔案寫入是不斷增加的過程,如果用FileHandler,久了日志檔案會非常大,不利于讀寫和管理。

用RotatingFileHandler設定日志檔案大小,比如10M。則如果日志檔案達到10M,就會自動将這個檔案重命名,然後建立一個新的日志檔案繼續寫入

也可以使用TimedRotatingFileHandler,這是根據時間,每隔一段時間會自動建立新的日志檔案

5.get_logger位置

在一個被import的檔案中get_logger的代碼要放在函數裡面,因為如果放在函數外面,在主檔案import這個檔案時,就會運作這個初始化。而在import的時候,主檔案一般還沒有讀入yaml檔案,是以事先初始化的log就不會正常工作。

參考資料

專欄資訊

專欄目錄:目錄