[TOC]
## 日志模塊四大組件
### 組件功能
| 組件名稱 | 對應類名 | 功能描述 |
| --- | --- | --- |
| 日志器 | Logger | 提供了應用程序可一直使用的接口 |
| 處理器 | Handler | 將logger創建的日志記錄發送到合適的目的輸出 |
| 過濾器 | Filter | 提供了更細粒度的控制工具來決定輸出哪條日志記錄,丟棄哪條日志記錄 |
| 格式器 | Formatter | 決定日志記錄的最終輸出格式 |
### 組件間關系:
* 日志器(logger)需要通過處理器(handler)將日志信息輸出到目標位置,如:文件、sys.stdout、網絡等;
* 不同的處理器(handler)可以將日志輸出到不同的位置;
* 日志器(logger)可以設置多個處理器(handler)將同一條日志記錄輸出到不同的位置;
* 每個處理器(handler)都可以設置自己的過濾器(filter)實現日志過濾,從而只保留感興趣的日志;
* 每個處理器(handler)都可以設置自己的格式器(formatter)實現同一條日志以不同的格式輸出到不同的地方。
**總結:**
日志器(logger)是入口,真正干活兒的是處理器(handler),處理器(handler)還可以通過過濾器(filter)和格式器(formatter)對要輸出的日志內容做過濾和格式化等處理操作。

## 每個組件的主要功能
### Logger組件
#### logger任務
Logger對象有3個任務要做:
* 1)向應用程序代碼暴露幾個方法,使應用程序可以在運行時記錄日志消息;
* 2)基于日志嚴重等級(默認的過濾設施)或filter對象來決定要對哪些日志進行后續處理;
* 3)將日志消息傳送給所有感興趣的日志handlers。
> Logger對象最常用的方法分為兩類:配置方法 和 消息發送方法
#### 常用配置方法
| 方法 | 描述 |
| --- | --- |
| Logger.setLevel() | 設置日志器將會處理的日志消息的最低嚴重級別 |
| Logger.addHandler() 和 Logger.removeHandler() | 為該logger對象添加 和 移除一個handler對象 |
| Logger.addFilter() 和 Logger.removeFilter() | 為該logger對象添加 和 移除一個filter對象 |
#### 常用消息發送方法
| 方法 | 描述 |
| --- | --- |
| Logger.debug(), Logger.info(), Logger.warning(), Logger.error(), Logger.critical() | 創建一個與它們的方法名對應等級的日志記錄 |
| Logger.exception() | 創建一個類似于Logger.error()的日志消息 |
| Logger.log() | 需要獲取一個明確的日志level參數來創建一個日志記錄 |
> Logger.exception()與Logger.error()的區別在于:Logger.exception()將會輸出堆棧追蹤信息,另外通常只是在一個exception handler中調用該方法。
#### 獲取logger對象
一種方式是通過Logger類的實例化方法創建一個Logger類的實例,更通常的方法是用`logging.getLogger()`方法。
logging.getLogger()方法有一個可選參數name,該參數表示將要返回的日志器的名稱標識,如果不提供該參數,則其值為'root'。若以相同的name參數值多次調用getLogger()方法,將會返回指向同一個logger對象的引用。
* 聊天工具的圖形界面模塊可以這樣獲得它的Logger:
```
LOG=logging.getLogger(”chat.gui”)`
```
* 核心模塊可以這樣:
```
LOG=logging.getLogger(”chat.kernel”)
```
#### logger的層級結構與有效等級
* 層級結構
logger的名稱是一個以'.'分割的層級結構,每個'.'后面的logger都是'.'前面的logger的children
* 有效等級(effective level)
如果一個logger上沒有被明確設置一個level,那么該logger就是使用它parent的level,直到找到個一個明確設置了level的祖先為止。root logger總是會有一個明確的level設置(默認為 WARNING)。當決定是否去處理一個已發生的事件時,logger的有效等級將會被用來決定是否將該事件傳遞給該logger的handlers進行處理。
* 繼承關系
child loggers在完成對日志消息的處理后,默認會將日志消息傳遞給與它們的祖先loggers相關的handlers。因此不必所有loggers定義和配置handlers,只需要為一個頂層的logger配置handlers,然后按照需要創建child loggers就可足夠了。可以通過將一個logger的propagate屬性設置為False來關閉這種傳遞機制。
### **handler組件**
#### Handler組件用途
Handler對象的作用是(基于日志消息的level)將消息分發到handler指定的位置(文件、網絡、郵件等)。Logger對象可以通過addHandler()方法為自己添加0個或者更多個handler對象。比如,一個應用程序可能想要實現以下幾個日志需求
* 1)把所有日志都發送到一個日志文件中;
* 2)把所有嚴重級別大于等于error的日志發送到stdout(標準輸出);
* 3)把所有嚴重級別為critical的日志發送到一個email郵件地址。
>這種場景就需要3個不同的handlers,每個handler負責發送一個特定級別的日志到一個特定的位置。
#### handler的配置方法
| 方法 | 描述 |
| --- | --- |
| Handler.setLevel() | 設置handler將會處理的日志消息的最低嚴重級別 |
| Handler.setFormatter() | 為handler設置一個格式器對象 |
| Handler.addFilter() 和 Handler.removeFilter() | 為handler添加 和 刪除一個過濾器對象 |
>應用程序代碼不應該直接實例化和使用Handler實例。因為Handler是一個基類,它只定義了所有handlers都應該有的接口
#### 常用的Handler
| Handler | 描述 |
| --- | --- |
| logging.StreamHandler | 將日志消息發送到輸出到Stream,如std.out, std.err或任何file-like對象。 |
| logging.FileHandler | 將日志消息發送到磁盤文件,默認情況下文件大小會無限增長 |
| logging.handlers.RotatingFileHandler | 將日志消息發送到磁盤文件,并支持日志文件按大小切割 |
| logging.hanlders.TimedRotatingFileHandler | 將日志消息發送到磁盤文件,并支持日志文件按時間切割 |
| logging.handlers.HTTPHandler | 將日志消息以GET或POST的方式發送給一個HTTP服務器 |
| logging.handlers.SMTPHandler | 將日志消息發送給一個指定的email地址 |
| logging.NullHandler | 該Handler實例會忽略error messages |
#### RotatingFileHandler按大小切割
當文件達到一定大小之后,自動將當前日志文件改名,然后創建一個新的同名日志文件繼續輸出。
>比如日志文件是chat.log。當chat.log達到指定的大小之后,RotatingFileHandler自動把文件改名為chat.log.1。若chat.log.1已經存在,會先把chat.log.1重命名為chat.log.2。。。最后重新創建 chat.log,繼續輸出日志信息。
```
RotatingFileHandler( filename[, mode[, maxBytes[, backupCount]]])
```
* maxBytes用于指定日志文件的最大文件大小。如果maxBytes為0,意味著日志文件可以無限大,這時上面描述的重命名過程就不會發生。
* backupCount用于指定保留的備份文件的個數。比如,如果指定為2,當上面描述的重命名過程發生時,原有的chat.log.2并不會被更名,而是被刪除。
#### TimedRotatingFileHandler按時間切割
間隔一定時間就自動創建新的日志文件。重命名的過程與RotatingFileHandler類似,不過新的文件不是附加數字,而是當前時間。
```
TimedRotatingFileHandler( filename [,when [,interval [,backupCount]]])
```
* `interval`是時間間隔。
* `when`參數是一個字符串。
表示時間間隔的單位,不區分大小寫。有以下取值:
* S 秒
* M 分
* H 小時
* D 天
* W 每星期(interval==0時代表星期一)
* midnight 每天凌晨
### Formater組件
#### 用途和語法
日志的formatter是個獨立的組件,可以跟handler組合。Formater對象用于配置日志信息的最終順序、結構和內容。
Formatter類的構造方法定義如下:
```
logging.Formatter.__init__(fmt=None, datefmt=None, style='%')
```
* fmt:指定消息格式化字符串,如果不指定該參數則默認使用message的原始值
* datefmt:指定日期格式字符串,如果不指定該參數則默認使用"%Y-%m-%d %H:%M:%S"
* style:可取值為 '%', '{'和 '$',如果不指定該參數則默認使用'%'
#### 與handler組合舉例
```
fh = logging.FileHandler("access.log")
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
fh.setFormatter(formatter) #把formmater綁定到fh上
```
### Filter組件
#### 用途和語法
Filter可以被Handler和Logger用來做比level更細粒度的、更復雜的過濾功能。Filter是一個過濾器基類,它只允許某個logger層級下的日志事件通過過濾。該類定義如下:
```
class logging.Filter(name='A.B')
filter(record)
```
>一個filter實例化時傳遞的name參數值為'A.B',那么該filter將只允許名稱為類似`'A.B','A.B,C','A.B.C.D','A.B.D'`的loggers產生的日志記錄通過過濾。如果name為空字符串,則允許所有的日志事件通過過濾。
filter方法用于具體控制傳遞的record記錄是否能通過過濾,如果該方法返回值為0表示不能通過過濾,返回值為非0表示可以通過過濾。
> 若需要,可以在filter(record)方法內部改變該record,比如添加、刪除或修改一些屬性。
> 還可以通過filter做一些統計工作,如計算一個特殊的logger或handler所處理的record數量等。
#### 自定義filter舉例
自定義一個filter對日志內容進行過濾
```
class IgnoreBackupLogFilter(logging.Filter):
"""忽略帶db backup 的日志"""
def filter(self, record): #固定寫法
return "db backup" not in record.getMessage()
```
> 注意filter函數會返加True or False,logger根據此值決定是否輸出此日志
然后把這個filter添加到logger中
```
logger.addFilter(IgnoreBackupLogFilter())
```
下面的日志就會把符合filter條件的過濾掉
```
logger.debug("test ....")
logger.info("test info ....")
logger.warning("start to run db backup job ....")
logger.error("test error ....")
```
## 完整案例
### 同時輸出到屏幕和文件帶filter
* 代碼
```python
import logging
class IgnoreBackupLogFilter(logging.Filter):
"""忽略帶db backup 的日志"""
def filter(self, record): #固定寫法
return "db backup" not in record.getMessage()
# 1.定義兩個handler,分別輸出到標準流和文件
hand_c=logging.StreamHandler()
hand_f=logging.FileHandler('mysql.log')
hand_c.setLevel(logging.INFO)
# 2.定義formatter并與handler綁定
format1=logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
hand_c.setFormatter(format1)
hand_f.setFormatter(format1)
# 3.定義logger并設置日志級別
logg1=logging.getLogger('mysql')
logg1.setLevel(logging.DEBUG)
# 4.logger添加自定義的handler
logg1.addHandler(hand_f)
logg1.addHandler(hand_c)
# 5.添加自定義的filter
logg1.addFilter(IgnoreBackupLogFilter())
# 6.寫入日志
logg1.debug("1. test ....")
logg1.info("2. test info ....")
logg1.warning("3. start to run db backup job ....")
logg1.error("4. test error ....")
```
* 輸出結果
```
#屏幕輸出
2019-01-28 11:42:23,266 - mysql - INFO - 2. test info ....
2019-01-28 11:42:23,266 - mysql - ERROR - 4. test error ....
#文件輸出
2019-01-28 11:42:23,266 - mysql - DEBUG - 1. test ....
2019-01-28 11:42:23,266 - mysql - INFO - 2. test info ....
2019-01-28 11:42:23,266 - mysql - ERROR - 4. test error ....
# 解釋
logger中定義了日志級別,會向下基礎,所以文件中就用的logger定義的級別,而屏幕上的輸出由于還有handler定義的級別,導致第一條被過濾了掉了
```
### 文件自動截斷例子
```
import logging
logger = logging.getLogger(__name__)
log_file = "timelog.log"
fh = logging.handlers.TimedRotatingFileHandler(filename=log_file,when="S",interval=5,backupCount=3)
formatter = logging.Formatter('%(asctime)s %(module)s:%(lineno)d %(message)s')
fh.setFormatter(formatter)
logger.addHandler(fh)
logger.warning("test1")
logger.warning("test12")
logger.warning("test13")
logger.warning("test14")
```
- 基礎部分
- 基礎知識
- 變量
- 數據類型
- 數字與布爾詳解
- 列表詳解list
- 字符串詳解str
- 元組詳解tup
- 字典詳解dict
- 集合詳解set
- 運算符
- 流程控制與循環
- 字符編碼
- 編的小程序
- 三級菜單
- 斐波那契數列
- 漢諾塔
- 文件操作
- 函數相關
- 函數基礎知識
- 函數進階知識
- lambda與map-filter-reduce
- 裝飾器知識
- 生成器和迭代器
- 琢磨的小技巧
- 通過operator函數將字符串轉換回運算符
- 目錄規范
- 異常處理
- 常用模塊
- 模塊和包相關概念
- 絕對導入&相對導入
- pip使用第三方源
- time&datetime模塊
- random隨機數模塊
- os 系統交互模塊
- sys系統模塊
- shutil復制&打包模塊
- json&pickle&shelve模塊
- xml序列化模塊
- configparser配置模塊
- hashlib哈希模塊
- subprocess命令模塊
- 日志logging模塊基礎
- 日志logging模塊進階
- 日志重復輸出問題
- re正則表達式模塊
- struct字節處理模塊
- abc抽象類與多態模塊
- requests與urllib網絡訪問模塊
- 參數控制模塊1-optparse-過時
- 參數控制模塊2-argparse
- pymysql數據庫模塊
- requests網絡請求模塊
- 面向對象
- 面向對象相關概念
- 類與對象基礎操作
- 繼承-派生和組合
- 抽象類與接口
- 多態與鴨子類型
- 封裝-隱藏與擴展性
- 綁定方法與非綁定方法
- 反射-字符串映射屬性
- 類相關內置方法
- 元類自定義及單例模式
- 面向對象的軟件開發
- 網絡-并發編程
- 網絡編程SOCKET
- socket簡介和入門
- socket代碼實例
- 粘包及粘包解決辦法
- 基于UDP協議的socket
- 文件傳輸程序實戰
- socketserver并發模塊
- 多進程multiprocessing模塊
- 進程理論知識
- 多進程與守護進程
- 鎖-信號量-事件
- 隊列與生產消費模型
- 進程池Pool
- 多線程threading模塊
- 進程理論和GIL鎖
- 死鎖與遞歸鎖
- 多線程與守護線程
- 定時器-條件-隊列
- 線程池與進程池(新方法)
- 協程與IO模型
- 協程理論知識
- gevent與greenlet模塊
- 5種網絡IO模型
- 非阻塞與多路復用IO實現
- 帶著目標學python
- Pycharm基本使用
- 爬蟲
- 案例-爬mzitu美女
- 案例-爬小說
- beautifulsoup解析模塊
- etree中的xpath解析模塊
- 反爬對抗-普通驗證碼
- 反爬對抗-session登錄
- 反爬對抗-代理池
- 爬蟲技巧-線程池
- 爬蟲對抗-圖片懶加載
- selenium瀏覽器模擬