2012-06-01 64 views
2

我使用python的日誌記錄模塊來顯示消息。我有我自己的項目,它使用來自公共存儲庫的模塊,所以我不想更改該部分代碼中的任何日誌語句。使用Python日誌記錄內存使用情況統計信息

但是,內存使用情況似乎是相當一個問題在我的計劃,所以我想登錄內存使用情況在每個日誌陳述,旁邊的時間和消息,就像這樣:

YYYY-MM-DD HH:MM:SS,mmm NAME LEVEL MemTotal:#M,Swap:#M MESSAGE 

是有一個簡單的方法來使用日誌記錄模塊來做到這一點?

我想通過使用Filter來添加上下文信息(參見http://docs.python.org/howto/logging-cookbook.html#filters-contextual),但似乎沒有辦法將此Filter同時添加到所有級別和所有記錄器實例。 該文檔建議將過濾器添加到處理程序而不是記錄器,因爲否則它將不使用外部模塊中的過濾器。以最明顯的方式執行此操作(創建Handler,添加Filter,然後將Handler附加到根Logger)卻給我帶來了意想不到的行爲。我根本沒有輸出,也沒有錯誤消息,或者(當首先使用basicConfig時)我得到了正確的行爲,除了它也給出了錯誤消息。我懷疑在最後一個案例中,我確實得到了兩個處理者,其中一個處理正確,另一個錯誤地處理。

到目前爲止,我已經想出了以下解決方案,我不認爲這是相當高雅的 (感謝https://stackoverflow.com/a/938800/819110)。醜陋的部分是我必須手工從記錄器中提取Handler,並將Filter添加到它。我似乎無法添加一個過濾器到配置文件,這會更方便。儘管如此,這種方法似乎可行(在Linux上),儘管我懷疑應該有一種更直接的方法來做到這一點。

import logging 
import external_module 

class MemuseFilter(logging.Filter): 
    def filter(self, record): 
     """ This function overrides logging.Filter, adds memuse as a field 
     """ 
     record.memuse = self.str_mem() 
     return True 

    # Following code from https://stackoverflow.com/a/938800/819110: 
    _proc_status = '/proc/%d/status' % os.getpid() 
    _scale = {'kB': 1024.0, 'mB': 1024.0*1024.0, 
       'KB': 1024.0, 'MB': 1024.0*1024.0} 

    def _VmB(self,VmKey): 
     """Private. 
     """ 
     # get pseudo file /proc/<pid>/status 
     try: 
      t = open(self._proc_status) 
      v = t.read() 
      t.close() 
     except: 
      return 0.0 # non-Linux? 
     # get VmKey line e.g. 'VmRSS: 9999 kB\n ...' 
     i = v.index(VmKey) 
     v = v[i:].split(None, 3) # whitespace 
     if len(v) < 3: 
      return 0.0 # invalid format? 
     # convert Vm value to bytes 
     return float(v[1]) * self._scale[v[2]] 

    def memory(self,since=0.0): 
     """Return memory usage in bytes. 
     """ 
     return self._VmB('VmSize:') - since 

    def swapsize(self,since=0.0): 
     """Return swap size in bytes. 
     """ 
     return self._VmB('VmSwap:') - since 

    def byte_to_mb(self,byte): 
     """return size in MB (being lazy) 
     """ 
     return byte/(1024*1024) 

    def str_mem(self): 
     """Return a string with the total memuse and swap size in MB 
     """ 
     return "MemTotal:%.0fM,Swap:%.0fM"%(\ 
      self.byte_to_mb(self.memory()),self.byte_to_mb(self.swapsize())) 

if __name__ == '_main__': 
    logging.config.fileConfig('logging.conf') # Get basic config 
    log = logging.getLogger('')    # Get root logger 
    f = MemuseFilter()      # Create filter 
    log.handlers[0].addFilter(f)   # The ugly part:adding filter to handler 
    log.warning("Foo") 
    function_from_module_using_logging() 

external_module讀這樣的事情:

log = logging.getLogger(__name__) 
def function_from_module_using_logging(): 
    log.warning("Bar") 

logging.conf是這樣的:

[loggers] 
keys=root 

[handlers] 
keys=fileHandler 

[formatters] 
keys=memuseFormatter 

[logger_root] 
level=DEBUG 
handlers=fileHandler 

[handler_fileHandler] 
class=FileHandler 
level=DEBUG 
formatter=memuseFormatter 
args=('some.log','w') 

[formatter_memuseFormatter] 
format=%(asctime)-15s %(name)-5s %(levelname)-8s %(memuse)-22s %(message)s 
datefmt= 

更好的解決方案將是最歡迎!

編輯:引用了錯誤的SO問題。

+0

爲什麼'_scale'字典中'1024.0'而不是'1024'?看起來整數比浮點更適合。 – Matt

+0

我承認我很懶,爲此只需複製http://stackoverflow.com/a/938800/819110。我想你是對的。 雖然(用於這個)只需要從Vm中獲取的字符串並且無需轉換即可通過(僅刪除換行符),這已經足夠了 – mmvdv

回答

1

如果您使用Python 2.7或更高版本,則可以使用dictConfig(),它支持配置中的過濾器。如果您使用的是早期版本的Python,則可以使用logutils項目爲您提供dictConfig()功能。如果你不想在外部依賴logutils,你可以將dictConfig()的內容複製到你自己的代碼中(就像Django做的那樣)。

+0

謝謝。很高興知道'dictConfig()'涵蓋過濾器。我被綁定到Python 2.6,所以我去'logutils'。 – mmvdv

1

首先,讓我們來清理一下誤解:記錄器不是添加此信息的正確位置 - 記錄器只不過是在字典中查找日誌級別的關鍵字。沒有必要爲每個記錄器添加內存使用情況,因爲格式化程序不會在那裏查看。

正確的解決方案是編寫自己的格式化程序(和您一樣)並實現一個新變量。沒有更好的方法來做到這一點。例如,您不想獲取永不寫入日誌文件的記錄器調用的內存信息。

相關問題