logging 模塊

 

## logging模塊

#### 什麼是logging模塊

logging模塊是python提供的用於記錄日誌的模塊

#### 爲何須要logging

咱們徹底能夠本身打開文件而後,日誌寫進去,可是這些操做重複且沒有任何技術含量,因此python幫咱們進行了封裝,有了logging後咱們在記錄日誌時 只須要簡單的調用接口便可,很是方便!

#### 日誌級別

在開始記錄日誌前還須要明確,日誌的級別

隨着時間的推移,日誌記錄會很是多,成千上萬行,如何快速找到須要的日誌記錄這就成了問題

解決的方案就是 給日誌劃分級別

logging模塊將日誌分爲了五個級別,從高到低分別是:

1.info 常規信息

2.debug 調試信息

3.warning 警告信息

4.error 錯誤信息

5.cretical 嚴重錯誤

本質上他們使用數字來表示級別的,從高到低分別是10,20,30,40,50



## logging模塊的使用

```python
#1.導入模塊
import logging

#2.輸出日誌
logging.info("info")
logging.debug("debug")
logging.warning("warning")
logging.error("error")
logging.critical("critical")

#輸出 WARNING:root:warning
#輸出 ERROR:root:error
#輸出 CRITICAL:root:critical
```

咱們發現info  和 debug都沒有輸出,這是由於它們的級別不夠,

默認狀況下:

```
logging的最低顯示級別爲warning,對應的數值爲30

日誌被打印到了控制檯

日誌輸出格式爲:級別  日誌生成器名稱  日誌消息
```

如何修改這寫默認的行爲呢?,這就須要咱們本身來進行配置

## 自定義配置

```python
import logging
logging.basicConfig()

"""可用參數
filename:用指定的文件名建立FiledHandler(後邊會具體講解handler的概念),這樣日誌會被存儲在指定的文件中。
filemode:文件打開方式,在指定了filename時使用這個參數,默認值爲「a」還可指定爲「w」。
format:指定handler使用的日誌顯示格式。 
datefmt:指定日期時間格式。 
level:設置rootlogger(後邊會講解具體概念)的日誌級別 
"""

#案例:
logging.basicConfig(
    filename="aaa.log",
    filemode="at",
    datefmt="%Y-%m-%d %H:%M:%S %p",
    format="%(asctime)s - %(name)s - %(levelname)s - %(module)s: %(message)s",
    level=10
)
```

#### 格式化所有可用名稱

```python
%(name)s:Logger的名字,並不是用戶名,詳細查看
%(levelno)s:數字形式的日誌級別
%(levelname)s:文本形式的日誌級別
%(pathname)s:調用日誌輸出函數的模塊的完整路徑名,可能沒有
%(filename)s:調用日誌輸出函數的模塊的文件名
%(module)s:調用日誌輸出函數的模塊名
%(funcName)s:調用日誌輸出函數的函數名
%(lineno)d:調用日誌輸出函數的語句所在的代碼行
%(created)f:當前時間,用UNIX標準的表示時間的浮 點數表示
%(relativeCreated)d:輸出日誌信息時的,自Logger建立以 來的毫秒數
%(asctime)s:字符串形式的當前時間。默認格式是 「2003-07-08 16:49:45,896」。逗號後面的是毫秒
%(thread)d:線程ID。可能沒有
%(threadName)s:線程名。可能沒有
%(process)d:進程ID。可能沒有
%(message)s:用戶輸出的消息
```



至此咱們已經能夠本身來配置一 寫基礎信息了,可是當咱們想要將同一個日誌輸出到不一樣位置時,這些基礎配置就沒法實現了,

例如 有一個登陸註冊的功能 須要記錄日誌,同時生成兩份 一份給程序員看,一份給老闆看,做爲程序員應該查看較爲詳細的日誌,二老闆則應該簡單一些,由於他不須要關心程序的細節

要實現這樣的須要咱們須要系統的瞭解loggin模塊

#### logging模塊的四個核心角色

1.Logger   日誌生成器 產生日誌

2.Filter    日誌過濾器  過濾日誌

3.Handler 日誌處理器 對日誌進行格式化,並輸出到指定位置(控制檯或文件)

4.Formater 處理日誌的格式



#### 一條日誌完整的生命週期

1.由logger 產生日誌  -> 2.交給過濾器判斷是否被過濾 -> 3.將日誌消息分發給綁定的全部處理器 -> 4處理器按照綁定的格式化對象輸出日誌

其中 第一步 會先檢查日誌級別 若是低於設置的級別則不執行

第二步 使用場景很少  須要使用面嚮對象的技術點 後續用到再講

第三步 也會檢查日誌級別,若是獲得的日誌低於自身的日誌級別則不輸出 

```
生成器的級別應低於句柄不然給句柄設置級別是沒有意義的,

例如  handler設置爲20  生成器設置爲30 

30如下的日誌壓根不會產生
```

第四步 若是不指定格式則按照默認格式



#### logging各角色的使用(瞭解)

```python
# 生成器
logger1 = logging.getLogger("日誌對象1")

# 文件句柄
handler1 = logging.FileHandler("log1.log",encoding="utf-8")
handler2 = logging.FileHandler("log2.log",encoding="utf-8")

# 控制檯句柄
handler3 = logging.StreamHandler()


# 格式化對象
fmt1 = logging.Formatter(
    fmt="%(asctime)s - %(name)s - %(levelname)s:  %(message)s",
    datefmt="%m-%d %H:%M:%S %p")
fmt2 = logging.Formatter(
    fmt="%(asctime)s - %(levelname)s :  %(message)s",
    datefmt="%Y/%m/%d %H:%M:%S")

# 綁定格式化對象與文件句柄
handler1.setFormatter(fmt1)
handler2.setFormatter(fmt2)
handler3.setFormatter(fmt1)

# 綁定生成器與文件句柄
logger1.addHandler(handler1)
logger1.addHandler(handler2)
logger1.addHandler(handler3)

# 設置日誌級別
logger1.setLevel(10)    #生成器日誌級別
handler1.setLevel(20)   #句柄日誌級別

# 測試
logger1.debug("debug msessage")
logger1.info("info msessage")
logger1.warning("warning msessage")
logger1.critical("critical msessage")
```

到此咱們已經能夠實現上述的需求了,可是這並非咱們最終的實現方式,由於每次都要編寫這樣的代碼是很是痛苦的

#### logging的繼承(瞭解)

能夠將一個日誌指定爲另外一個日誌的子日誌 或子孫日誌

當存在繼承關係時 子孫級日誌收到日誌時會將該日誌向上傳遞

指定繼承關係:

```python
import  logging

log1 = logging.getLogger("mother")
log2 = logging.getLogger("mother.son")
log3 = logging.getLogger("mother.son.grandson")

# handler
fh = logging.FileHandler(filename="cc.log",encoding="utf-8")
# formatter
fm = logging.Formatter("%(asctime)s - %(name)s -%(filename)s - %(message)s")

# 綁定
log1.addHandler(fh)
log2.addHandler(fh)
log3.addHandler(fh)
# 綁定格式
fh.setFormatter(fm)
# 測試
# log1.error("測試")
# log2.error("測試")
log3.error("測試")
# 取消傳遞
log3.propagate = False
# 再次測試
log3.error("測試")
```



#### 經過字典配置日誌模塊(重點)

每次都要編寫代碼來配置很是麻煩 ,咱們能夠寫一個完整的配置保存起來,以便後續直接使用

```python
import logging.config
logging.config.dictConfig(LOGGING_DIC)
logging.getLogger("aa").debug("測試")
```

**LOGGING_DIC模板**

```python
standard_format = '[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s][%(filename)s:%(lineno)d]' \
                  '[%(levelname)s][%(message)s]' #其中name爲getlogger指定的名字

simple_format = '[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d]%(message)s'

id_simple_format = '[%(levelname)s][%(asctime)s] %(message)s'
logfile_path = "配置文件路徑"

LOGGING_DIC = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'standard': {
            'format': standard_format
        },
        'simple': {
            'format': simple_format
        },
    },
    'filters': {},
    'handlers': {
        #打印到終端的日誌
        'console': {
            'level': 'DEBUG',
            'class': 'logging.StreamHandler',  # 打印到屏幕
            'formatter': 'simple'
        },
        #打印到文件的日誌,收集info及以上的日誌
        'default': {
            'level': 'DEBUG',
            'class': 'logging.handlers.RotatingFileHandler',  # 保存到文件
            'formatter': 'standard',
            'filename': logfile_path,  # 日誌文件
            'maxBytes': 1024*1024*5,  # 日誌大小 5M
            'backupCount': 5, #日誌文件最大個數
            'encoding': 'utf-8',  # 日誌文件的編碼
        },
    },
    'loggers': {
        #logging.getLogger(__name__)拿到的logger配置
        'aa': {
            'handlers': ['default', 'console'],  # 這裏把上面定義的兩個handler都加上,即log數據既寫入文件又打印到屏幕
            'level': 'DEBUG',
            'propagate': True,  # 向上(更高level的logger)傳遞
        },
    },
}
```

補充:

getLogger參數就是對應字典中loggers的key , 若是沒有匹配的key 則返回系統默認的生成器,咱們能夠在字典中經過空的key來將一個生成器設置爲默認的

```python
'loggers': {
        # 把key設置爲空
        '': {
            'handlers': ['default', 'console'],  # 這裏把上面定義的兩個handler都加上,即log數據既寫入文件又打印到屏幕
            'level': 'DEBUG',
            'propagate': True,  # 向上(更高level的logger)傳遞
        },
    },
```

,日後在使用時能夠這調用模塊提供的函數,來輸出日誌

logging.info("測試信息!")

另外咱們在第一次使用日誌時並無指定生成器,但也能夠使用,這是由於系統有默認的生成器名稱就叫root



最後來完成以前的需求:

有一個登陸註冊的功能 須要記錄日誌,同時生成兩份 一份給程序員看,一份給老闆看,做爲程序員應該查看較爲詳細的日誌,二老闆則應該簡單一些,由於他不須要關心程序的細節

```python
# 程序員看的格式
standard_format = '[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s][%(filename)s:%(lineno)d]' \
                  '[%(levelname)s][%(message)s]' #其中name爲getlogger指定的名字
logfile_path1 = "coder.log"

# 老闆看的格式
simple_format = '[%(levelname)s][%(asctime)s]%(message)s'
logfile_path2 = "boss.log"


LOGGING_DIC = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'standard': {
            'format': standard_format
        },
        'simple': {
            'format': simple_format
        },
    },
    'filters': {},
    'handlers': {
        #打印到終端的日誌
        'console': {
            'level': 'DEBUG',
            'class': 'logging.StreamHandler',  # 打印到屏幕
            'formatter': 'simple'
        },
        #打印到文件的日誌,收集info及以上的日誌
        'std': {
            'level': 'DEBUG',
            'class': 'logging.handlers.RotatingFileHandler',  # 保存到文件
            'formatter': 'standard',
            'filename': logfile_path1,  # 日誌文件
            'maxBytes': 1024*1024*5,  # 日誌大小 5M
            'backupCount': 5, #日誌文件最大個數
            'encoding': 'utf-8',  # 日誌文件的編碼
        },
        'boss': {
            'level': 'DEBUG',
            'class': 'logging.handlers.RotatingFileHandler',  # 保存到文件
            'formatter': 'simple',
            'filename': logfile_path2,  # 日誌文件
            'maxBytes': 1024 * 1024 * 5,  # 日誌大小 5M
            'backupCount': 5,  # 日誌文件最大個數
            'encoding': 'utf-8',  # 日誌文件的編碼
        }
    },
    'loggers': {
        #logging.getLogger(__name__)拿到的logger配置
        'aa': {
            'handlers': ['std', 'console',"boss"],  # 這裏把上面定義的handler都加上,即log數據會同時輸出到三個位置
            'level': 'INFO',
            'propagate': True,  # 向上(更高level的logger)傳遞
        },
    },
}
```
,日後在使用時能夠這調用模塊提供的函數,來輸出日誌

logging.info("測試信息!")

另外咱們在第一次使用日誌時並無指定生成器,但也能夠使用,這是由於系統有默認的生成器名稱就叫root



最後來完成以前的需求:

有一個登陸註冊的功能 須要記錄日誌,同時生成兩份 一份給程序員看,一份給老闆看,做爲程序員應該查看較爲詳細的日誌,二老闆則應該簡單一些,由於他不須要關心程序的細節

```python
# 程序員看的格式
standard_format = '[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s][%(filename)s:%(lineno)d]' \
                  '[%(levelname)s][%(message)s]' #其中name爲getlogger指定的名字
logfile_path1 = "coder.log"

# 老闆看的格式
simple_format = '[%(levelname)s][%(asctime)s]%(message)s'
logfile_path2 = "boss.log"


LOGGING_DIC = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'standard': {
            'format': standard_format
        },
        'simple': {
            'format': simple_format
        },
    },
    'filters': {},
    'handlers': {
        #打印到終端的日誌
        'console': {
            'level': 'DEBUG',
            'class': 'logging.StreamHandler',  # 打印到屏幕
            'formatter': 'simple'
        },
        #打印到文件的日誌,收集info及以上的日誌
        'std': {
            'level': 'DEBUG',
            'class': 'logging.handlers.RotatingFileHandler',  # 保存到文件
            'formatter': 'standard',
            'filename': logfile_path1,  # 日誌文件
            'maxBytes': 1024*1024*5,  # 日誌大小 5M
            'backupCount': 5, #日誌文件最大個數
            'encoding': 'utf-8',  # 日誌文件的編碼
        },
        'boss': {
            'level': 'DEBUG',
            'class': 'logging.handlers.RotatingFileHandler',  # 保存到文件
            'formatter': 'simple',
            'filename': logfile_path2,  # 日誌文件
            'maxBytes': 1024 * 1024 * 5,  # 日誌大小 5M
            'backupCount': 5,  # 日誌文件最大個數
            'encoding': 'utf-8',  # 日誌文件的編碼
        }
    },
    'loggers': {
        #logging.getLogger(__name__)拿到的logger配置
        'aa': {
            'handlers': ['std', 'console',"boss"],  # 這裏把上面定義的handler都加上,即log數據會同時輸出到三個位置
            'level': 'INFO',
            'propagate': True,  # 向上(更高level的logger)傳遞
        },
    },
}
```

 

---恢復內容結束---python

相關文章
相關標籤/搜索