摘要:的繼承關系使用做日志輸出時,首先我們需要一個創建一個對象。再設計多級別的日志系統時,尤其要注意這點。當然,這樣做其實是有悖于的本意的。是什么是一個程序內全局唯一的,所有對象的祖先。因此,直接修改是危險的。
0x00 python logging的繼承關系
使用python做日志輸出時,首先我們需要一個創建一個Logger對象:import logging; logger = logging.getLogger()。然后,我們就可以用logger.info/debug/error(msg)來輸出日志了。
如果只是單純地打印日志,這樣做和丑陋的print沒有任何區別。我們期望log能有一定的格式,這時你就會用到logging.Formatter;我們還希望日志不僅在console中輸出,還要向文件輸出;這樣你需要給我們的logger添加handler,一個handler指向標準輸出流,一個handler指向文件handler。logging.handlers提供了一些這些常用的handler。
然后,你希望對這些不同的輸出流進行精準的控制,比如:在console中只輸出某些高級別的日志,而在文件日志中輸出所有日志。在console中,使用一種輸出formatter,在文件輸出中使用另一種formatter。你不滿足于python提供的DEBUG/INFO/WARNING/ERROR/CRITICAL的控制粒度,想要更精細地控制日志。你就需要理解日志是如何流轉、繼承地。
這是python官方提供的一張log輸出圖
也就是說,我們有可以從如下幾個層面控制日志的輸出:
Logger 輸出級別控制
Logger 的filter控制
Handler 的級別控制
Handler 的filter控制
此外,我們要注意到日志的輸出是流式的,只要有一個地方日志被過濾掉了,他就不能輸出了。再設計多級別的日志系統時,尤其要注意這點。如果我們要設置過濾條件,要在上圖所示的日志流中,逐漸提高level級別。
0x01 為日志增加默認屬性python日志支持的默認字段比較少:
其實Filter隱含了一個比較dirty的接口,讓你能夠修改logRecord的屬性。讓你能夠給日志增加一個新的字段。代碼如下:
class ContextFilter(logging.Filter): hostname = os.getenv("HOSTNAME") def filter(self, record): record.hostname = self.hostname return True
將ContextFilter添加到你的某個handler,然后給這個handler增加一個這樣的formatter: [%(asctime)s] [%(levelname)s] [HOSTNAME: %(hostname)s] %(message)s;這個handler就可以輸出主機名了。當然,這樣做其實是有悖于fileter的本意的。不過我還沒有找到更好的辦法。
0x02 更細的日志粒度python的日志粒度如下:
如果我們向定義一個比debug級別更低的日志怎么辦呢?代碼如下:
VERBOSE_LOG_LEVEL = 5 def VERBOSE(self, message, *args, **kwargs): if self.isEnabledFor(VERBOSE_LOG_LEVEL): self._log(VERBOSE_LOG_LEVEL, message, args, **kwargs) logging.Logger.VERB = VERBOSE
這樣我們就定義了一個級別為5的輸出。這樣做的好處是,比如有些特別瑣碎的、系統級別的輸出,你不希望框架使用者看到,而只是作為日志分析用。你可以定義一個非常低的日志級別。然后把絕大多數的handler的控制級別設置的都比5高,只留一個接口給日志收集者。這樣,就可以大大提升框架使用者的體驗。
0x03 一個小bug偶然的原因復現了別人的一個bug.
觸發錯誤的代碼很簡單:
import requests import logging logger = logging.getLogger() logStdOut = logging.StreamHandler() LOGFORMATCNSL=logging.Formatter("%(asctime)s %(message)s %(aVar)s %(bVar)s") logStdOut.setFormatter(LOGFORMATCNSL) logStdOut.setLevel(logging.DEBUG) logger.setLevel(logging.NOTSET) logger.addHandler(logStdOut) def tryThis(): logger.error("deneme", extra={"aVar": "aVal", "bVar": "bVal"}) conn = requests.get("http://www.google.com") conn.close() tryThis()
錯誤最終定位到了在urllib3/connectionpool.py下的日志打印命令
log.debug("%s://%s:%s "%s %s %s" %s %s", self.scheme, self.host, self.port, method, url, http_version, httplib_response.status, httplib_response.length)
通過debug模式,我們可以看到,在這里,RootLogger被賦予了一個formatter, "%(asctime)s %(message)s %(aVar)s %(bVar)s"。
RootLogger是什么?是一個python程序內全局唯一的,所有Logger對象的祖先。它是怎么產生的呢?
logger = logging.getLogger(),這個logger就是RootLogger。我們對logger的設定,自然會影響到所有的日志輸出。
因此,直接修改RootLogger是危險的。而如果我們給getLogger傳一個參數,它會生成一個非root的Logger。問題就解決了。
修正后的代碼如下:
import requests import logging # code with out bug logger = logging.getLogger("__abc__") # code will trigger the keyError bug # logger = logging.getLogger() logStdOut = logging.StreamHandler() print(isinstance(logger, logging.RootLogger)) LOGFORMATCNSL=logging.Formatter("%(asctime)s %(message)s %(aVar)s %(bVar)s") logStdOut.setFormatter(LOGFORMATCNSL) logStdOut.setLevel(logging.DEBUG) logger.setLevel(logging.NOTSET) logger.addHandler(logStdOut) def tryThis(): logger.error("deneme", extra={"aVar": "aVal", "bVar": "bVal"}) conn = requests.get("http://www.baidu.com") conn.close() tryThis()
尬聊
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/42109.html
摘要:一概念通常的程序的構架是指將一個程序分割為源代碼文件的集合以及將這些部分連接在一起的方法。的程序構架可表示為一個程序就是一個模塊的系統。它有一個頂層文件啟動后可運行程序以及多個模塊文件用來導入工具庫。導入是中程序結構的重點所在。 一、概念 通常的Python程序的構架是指:將一個程序分割為源代碼文件的集合以及將這些部分連接在一起的方法。 Python的程序構架可表示為: showImg...
摘要:底層淺析簡介是官方提供的接口,同時也是中的一個程序。這里一提,對于大部分機器學習算法,你都會看到模塊與模塊都提供了接口,它們的區別在于模塊接受格式的數據而模塊接受格式的數據。 pyspark底層淺析 pyspark簡介 pyspark是Spark官方提供的API接口,同時pyspark也是Spark中的一個程序。 在terminal中輸入pyspark指令,可以打開python的she...
摘要:安卓滲透框架架構淺析架構組成和自定義模塊標簽空格分隔簡介是開發的一款針對系統的安全測試框架。感興趣的可以閱讀的相關源碼地址是一個安裝在測試安卓機上輕量級,并且只申請一個權限,是為了用來和進行連接的。 安卓滲透框架-Drozer架構淺析--架構組成和自定義模塊 標簽(空格分隔): Drozer Android Security 1. Drozer 簡介 Drozer是MWR Labs開...
摘要:淺析參數說明對于所有列表里提到的純模塊做處理需要在腳本里有一個包名到目錄的映射。闡明包名到目錄的映射,見鍵代表了包的名字,空的包名則代表不在任何包中的頂層包。最終會在下生成可執行文件,調用制定的函數實例分析 python setup.py 淺析 setuptools.setup() 參數說明 packages 對于所有 packages 列表里提到的純 Python 模塊做處理 需要...
閱讀 3638·2021-11-25 09:43
閱讀 636·2021-09-22 15:59
閱讀 1743·2021-09-06 15:00
閱讀 1769·2021-09-02 09:54
閱讀 689·2019-08-30 15:56
閱讀 1176·2019-08-29 17:14
閱讀 1839·2019-08-29 13:15
閱讀 880·2019-08-28 18:28