2009-06-10 36 views
11

所以現在我們有很多python腳本,我們正在嘗試整合它們並修復和裁減。我們試圖做的一件事是確保所有的sys.stdout/sys.stderr進入python日誌記錄模塊。將sys.stdout重定向到python logging

現在最主要的是,我們希望達到以下打印出來:

[<ERROR LEVEL>] | <TIME> | <WHERE> | <MSG> 

現在所有sys.stdout的/ sys.stderr封郵件幾乎在所有的蟒蛇錯誤消息在[LEVEL的格式] - MSG,全部使用sys.stdout/sys.stderr寫入。我可以解析罰款,在我的sys.stdout包裝和sys.stderr包裝。然後根據解析的輸入調用相應的日誌記錄級別。

所以基本上我們有一個名爲foo的包和一個名爲log的子包。在__init__.py我們定義如下:

def initLogging(default_level = logging.INFO, stdout_wrapper = None, \ 
       stderr_wrapper = None): 
    """ 
     Initialize the default logging sub system 
    """ 
    root_logger = logging.getLogger('') 
    strm_out = logging.StreamHandler(sys.__stdout__) 
    strm_out.setFormatter(logging.Formatter(DEFAULT_LOG_TIME_FORMAT, \ 
              DEFAULT_LOG_TIME_FORMAT)) 
    root_logger.setLevel(default_level) 
    root_logger.addHandler(strm_out) 

    console_logger = logging.getLogger(LOGGER_CONSOLE) 
    strm_out = logging.StreamHandler(sys.__stdout__) 
    #strm_out.setFormatter(logging.Formatter(DEFAULT_LOG_MSG_FORMAT, \ 
    #          DEFAULT_LOG_TIME_FORMAT)) 
    console_logger.setLevel(logging.INFO) 
    console_logger.addHandler(strm_out) 

    if stdout_wrapper: 
     sys.stdout = stdout_wrapper 
    if stderr_wrapper: 
     sys.stderr = stderr_wrapper 


def cleanMsg(msg, is_stderr = False): 
    logy = logging.getLogger('MSG') 
    msg = msg.rstrip('\n').lstrip('\n') 
    p_level = r'^(\s+)?\[(?P<LEVEL>\w+)\](\s+)?(?P<MSG>.*)$' 
    m = re.match(p_level, msg) 
    if m: 
     msg = m.group('MSG') 
     if m.group('LEVEL') in ('WARNING'): 
      logy.warning(msg) 
      return 
     elif m.group('LEVEL') in ('ERROR'): 
      logy.error(msg) 
      return 
    if is_stderr: 
     logy.error(msg) 
    else: 
     logy.info(msg) 

class StdOutWrapper: 
    """ 
     Call wrapper for stdout 
    """ 
    def write(self, s): 
     cleanMsg(s, False) 

class StdErrWrapper: 
    """ 
     Call wrapper for stderr 
    """ 
    def write(self, s): 
     cleanMsg(s, True) 

現在,我們將調用這個在我們的腳本之一,例如:

import foo.log 
foo.log.initLogging(20, foo.log.StdOutWrapper(), foo.log.StdErrWrapper()) 
sys.stdout.write('[ERROR] Foobar blew') 

這將被轉換爲錯誤日誌消息。像:

[ERROR] | 20090610 083215 | __init__.py | Foobar Blew 

現在的問題是,當我們做到這一點,其中記錄了該錯誤消息,現在該模塊是__init__(對應於foo.log.__init__.py文件),這違背了整個目的。

我試着做一個stderr/stdout對象的deepCopy/shallowCopy,但是什麼都不做,它仍然說在__init__.py中發生了這個消息。我怎麼能這樣做,這不會發生?

回答

6

的問題是,日誌模塊正在單層調用棧找到誰把它稱爲,但現在你的函數是在這一點上的中間層(雖然我已經預料到報告cleanMsg,不__init__,因爲這是你在調用log())的地方。相反,您需要將其升級到兩個級別,否則將您的調用者的姓名傳遞給記錄的消息。您可以通過自己檢查堆棧框架並獲取調用函數並將其插入消息來完成此操作。

要找到自己的人生使命框架,你可以使用檢查模塊:

import inspect 
f = inspect.currentframe(N) 

將查找N幀,並返回幀指針。即你的直接調用者是當前幀(1),但如果這是stdout.write方法,你可能不得不去另一個幀。 一旦你有調用框架,你可以得到正在執行的代碼對象,並查看與之關聯的文件和函數名稱。例如:

code = f.f_code 
caller = '%s:%s' % (code.co_filename, code.co_name) 

您還可能需要把一些代碼來處理非Python代碼調用到您(即C函數或內置函數),因爲這些可能缺乏f碼的對象。

或者,跟在mikej's answer之後,您可以在繼承自logging.Logger的自定義Logger類中使用相同方法,該類重寫findCaller以向上導航幾個幀,而不是一個。

2

我認爲問題是,現在正在由cleanMsglogy.errorlogy.info調用創建您的實際日誌消息,因此該方法是日誌消息的來源,並且看到此爲__init__.py

如果您查看Python的源代碼lib/logging/__init__.py,您將看到一個名爲findCaller的方法,這是記錄模塊用來派生記錄請求的調用者的方法。
也許你可以重寫這個在你的日誌記錄對象來定製行爲?