2012-06-01 60 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

首先,让我们来清理一下误解:记录器不是添加此信息的正确位置 - 记录器只不过是在字典中查找日志级别的关键字。没有必要为每个记录器添加内存使用情况,因为格式化程序不会在那里查看。

正确的解决方案是编写自己的格式化程序(和您一样)并实现一个新变量。没有更好的方法来做到这一点。例如,您不想获取永不写入日志文件的记录器调用的内存信息。

相关问题