一個適用於 Python 學習的 Log 工廠類

浮生若夢的程式設計發表於2017-12-03

前言: 本文所指的「Python 學習」涉及的範圍大致是「平時工作業餘,學習or測試一些Python特性,或者庫程式碼邏輯時寫的 toy program」

Python 的 logging 標準庫

標準庫的實現,已經很完備了,總的來說,幾個基本目標已經達到(一個合格的日誌庫必須做到的):

  1. 執行緒安全
  2. 單例 + 工廠

而且,對 Logger/ Hanlder/ Formatter 等概念也封裝得很完備,我們一般只需要少許工作便能得到一個能用的 logger。

我的初衷

為啥我還要來封裝呢?仍然是為了方便。

不管是:

  1. 直接使用 logging.info
  2. 還是使用 fileConfig (官方已經不推薦)
  3. 還是 dictConfig (Django專案中就是用這個)

都還是需要做不少工作的(深入使用過的人都知道),況且2和3都是針對正式專案的做法,我們大可不必如何“繁文縟節”。

另外我們平時寫 toy program,免不了要debug/log,我的初衷便是針對這種場景。

另外,我個人還有如下習慣:

  1. 拒絕在任何語言中使用任何形式的print,來作為除錯工具,其功能性太過於孱弱,僅止步於“夠用”是不夠的
  2. 極少使用 debugger 進行單步除錯(不管是IDE的debugger,亦或是pdb,或者各種加強版的xxdb)
  3. log 一般是我的首選除錯手段
  4. 我一般期望利用 log 看到更多資訊

實現

import logging
from logging import StreamHandler
from logging.handlers import RotatingFileHandler


class LogUtils(object):
    VERBOSE_FMT = ('%(levelname)s %(asctime)s %(name)s %(module)s %(process)d %(thread)d '
                   '%(filename)s_%(lineno)s_%(funcName)s  %(message)s')

    @classmethod
    def get_rotating_file_logger(cls, logger_name, filename, max_bytes=100 * 1024 * 1024, backup_count=10):
        """生成一個基於輪轉檔案的logger
        
        :param logger_name: 
        :param filename: 
        :param max_bytes: 
        :param backup_count: 
        :return:
        """
        logger = logging.getLogger(logger_name)

        fmtter = logging.Formatter(fmt=cls.VERBOSE_FMT)

        handler = RotatingFileHandler(filename, mode='a', maxBytes=max_bytes,
                                      backupCount=backup_count, encoding='utf-8')

        return cls._make_logger(fmtter, handler, logger)

    @classmethod
    def get_stream_logger(cls, logger_name='CONSOLE_DEBUGGER'):
        """生成一個定向到標準輸出的logger, 可作為除錯使用

        :param logger_name:
        :return:
        """
        logger = logging.getLogger(logger_name)

        formatter = logging.Formatter(fmt=cls.VERBOSE_FMT)

        handler = StreamHandler()

        return cls._make_logger(formatter, handler, logger)

    @classmethod
    def _make_logger(cls, formatter, handler, logger):
        handler.setFormatter(formatter)
        handler.setLevel(logging.INFO)

        logger.addHandler(handler)
        logger.setLevel(logging.INFO)

        return logger
複製程式碼

使用

# 我一般放在專案的 utils/log_util.py 中 :)
from log_util import LogUtils

LOGGER = LogUtils.get_stream_logger()

def func(a, b, c):
    LOGGER.info('====func start. %s ====' % [a, b, c])
    
    # 複雜的程式碼邏輯
    
    LOGGER.info('==== func end. ====')
    
    return something
複製程式碼

主要應用

  1. 除錯包含非同步邏輯的程式碼,典型如回撥,async,協程程式
  2. 多執行緒程式碼,學習多執行緒的使用
  3. 除錯多執行緒死鎖和資源競爭問題

相關文章