雖然日誌很重要,但並非全部的開發者都知道如何正確的記錄日誌。以前在開發過程當中往代碼裏插入print語句,而後在開發完成以後刪掉他們……可是針對以簡單的腳本時,這種方式頗有效;可是對於複雜的系統,這麼作並非一個明智的選擇。首先你不可能期望日誌裏輸出重要的信息,你可能在日誌裏看到一大堆垃圾信息,卻找不到任何有用的內融。print語句不能很好的作到控制,除非你修改源代碼。若是忘記刪除沒有用的print,全部的消息都會給輸出到stdout,終究不是記錄日誌的好習慣。
使用Python內置的標準模塊,是記錄日誌的正確姿式。logging是一個標準模塊,設計優良,易於使用,同時易於擴展。
import logging logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) logger.info('Start reading database') # read database here records = {'john': 55, 'tom': 66} logger.debug('Records: %s', records) logger.info('Updating records ...') # update records here logger.info('Finish updating records')
輸出結果以下python
INFO:main:Start reading database INFO:main:Updating records ... INFO:main:Finish updating records [Finished in 0.3s]json
與print的區別:服務器
日誌級別分爲: debug、info、warning、error、critical網絡
級別 | 數值 | 什麼時候使用 |
---|---|---|
DEBUG | 詳細信息,典型的調試問題時會感興趣 | |
INFO | 證實事情按預期工做 | |
WARNING | 報名發生了一些意外,或者不就得未來會發生的問題,可是軟件還能正常工做 | |
ERROR | 因爲嚴重的問題,異常拋出,IO操做失敗,軟件已經不能執行一些功能了 | |
CRITICAL | 嚴重錯誤,代表軟件已經不能正常運行了,內存不足,磁盤滿了 |
__name__變量在Python裏面是當前模塊的名字。例如,你在模塊 ** foo.bar.my_module ** 裏調用 logging.getLogger(__name__) ,等價於 logging.getLogger('foo.bar.my_module') 。當你須要設置 logger 的時候,設置爲 foo ,那麼 foo 包裏的全部模塊都會共享同一臺設置,能夠志短的看到這是那個木塊記錄的日誌信息。
出錯的時候記錄日誌是好的,可是沒有 traceback 也沒什麼用。在你捕獲異常時,在日誌中附加 tracebask 信息,例如:
try: open('/path/to/does/not/exist', 'rb') except (SystemExit, KeyboardInterrupt): raise except Exception, e: logger.error('Failed to open file', exc_info=True)
調用logger來記錄日誌是,指定 exc_info=True參數,traceback信息將會被保存到日誌。你也能夠調用 logger.exception(msg, *args),這跟 logger.error(msg, *args, exc_info=True) 是同樣的debug
ERROR:main:Failed to open file Traceback (most recent call last): File "example.py", line 6, in <module> open('/path/to/does/not/exist', 'rb') IOError: [Errno 2] No such file or directory: '/path/to/does/not/exist'設計
##### 除非 disable_existing_logger == False,否則不要在模塊級別獲取 logger調試
在網絡上能夠找到不少例子(這篇文章中我也特地舉了這麼一個例子),它們在模塊級別獲取 logger。看起來無害,但其實是有隱患的——Python 的 logging 模塊,在從文件中讀取設置以前,對全部 logger 都一視同仁。
你能夠在 Python 代碼中配置你的日誌系統,但彷佛不是那麼靈活。更好的方式是用配置文件。在 Python 2.7 以後,你能夠從一個字典中加載日誌配置了。這意味着你能夠從 JSON 或是 YAML 文件中讀取配置。雖然舊的 .ini 格式配置也仍是被支持,但那很難寫,閱讀也不方便。這裏舉了一個使用 JSON 或 YAML 配置的例子:
logging.json日誌
{ "version": 1, "disable_existing_loggers": false, "formatters": { "simple": { "format": "%(asctime)s - %(name)s - %(levelname)s - %(message)s" } }, "handlers": { "console": { "class": "logging.StreamHandler", "level": "DEBUG", "formatter": "simple", "stream": "ext://sys.stdout" }, "info_file_handler": { "class": "logging.handlers.RotatingFileHandler", "level": "INFO", "formatter": "simple", "filename": "info.log", "maxBytes": 10485760, "backupCount": 20, "encoding": "utf8" }, "error_file_handler": { "class": "logging.handlers.RotatingFileHandler", "level": "ERROR", "formatter": "simple", "filename": "errors.log", "maxBytes": 10485760, "backupCount": 20, "encoding": "utf8" } }, "loggers": { "my_module": { "level": "ERROR", "handlers": ["console"], "propagate": "no" } }, "root": { "level": "INFO", "handlers": ["console", "info_file_handler", "error_file_handler"] } }``` logging.yaml ---
version: 1code
disable_existing_loggers: Falseorm
formatters:
simple: format: "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
handlers:
console: class: logging.StreamHandler level: DEBUG formatter: simple stream: ext://sys.stdout info_file_handler: class: logging.handlers.RotatingFileHandler level: INFO formatter: simple filename: info.log maxBytes: 10485760 # 10MB backupCount: 20 encoding: utf8 error_file_handler: class: logging.handlers.RotatingFileHandler level: ERROR formatter: simple filename: errors.log maxBytes: 10485760 # 10MB backupCount: 20 encoding: utf8
loggers:
my_module: level: ERROR handlers: [console] propagate: no
root:
level: INFO handlers: [console, info_file_handler, error_file_handler]
...
####下面的代碼片斷展現瞭如何從 JSON 文件中讀取日誌配置: - - -
import os import json import logging.config
def setup_logging( default_path='logging.json', default_level=logging.INFO, env_key='LOG_CFG' ): """Setup logging configuration
""" path = default_path value = os.getenv(env_key, None) if value: path = value if os.path.exists(path): with open(path, 'rt') as f: config = json.load(f) logging.config.dictConfig(config) else: logging.basicConfig(level=default_level)
#####JSON 有個優點就是 json 是個標準庫,你不須要安裝就可使用。可是我的來講我更喜歡 YAML,讀寫都方便。使用以下代碼片斷能夠加載 YAML 配置: - - -
import os import logging.config
import yaml
def setup_logging( default_path='logging.yaml', default_level=logging.INFO, env_key='LOG_CFG' ): """Setup logging configuration
""" path = default_path value = os.getenv(env_key, None) if value: path = value if os.path.exists(path): with open(path, 'rt') as f: config = yaml.load(f.read()) logging.config.dictConfig(config) else: logging.basicConfig(level=default_level)
#####如今,要設置日誌記錄的話,在程序啓動的時候調用 setup_logging 就好了。它默認讀取 logging.json 或是 logging.yaml。你能夠設置 LOG_CFG 環境變量來指定從某個路徑中加載日誌配置。例如: >LOG_CFG=my_logging.json python my_server.py 或者,若是你偏好 YAML: >LOG_CFG=my_logging.yaml python my_server.py ### 輪換日誌處理器 若是你用 FileHandler 來寫日誌的話,日誌文件大小會隨時間不斷增加。總有一天磁盤會被它佔滿的。爲了不這種狀況,你應該在生產環境中使用 RotatingFileHandler 代替 FileHandler。 ### 在有多臺服務器的狀況下,設置中心日誌服務器 當你有多臺服務器和多個日誌文件的時候,你能夠設置一臺中心日誌服務器來收集全部重要的信息(大部分狀況下是警告、錯誤等)。這樣你監視起來會比較簡單,出錯的時候也更容易注意到。 ### 最後 Pythonde logging 弄快設計的很棒,並且是標準庫,很容易擴展,你能夠編寫本身的處理器和過濾器。還有一些第三方的處理器,pyzmq提供的ZeroMQ處理器,可讓你經過一個zmq套接字來發送日誌信息。