3

我在一个项目中工作,该项目使用 python 的日志记录模块来显示消息。我有自己的项目,该项目使用来自公共存储库的模块,因此我不想更改该部分代码中的任何日志记录语句。

但是,内存使用在我的程序中似乎是一个相当大的问题,所以我想在每个日志语句中记录内存使用情况,以及时间和消息,如下所示:

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

有没有使用日志模块的简单方法来做到这一点?

我认为要走的路是使用过滤器添加上下文信息(参见http://docs.python.org/howto/logging-cookbook.html#filters-contextual),但似乎没有办法一次将此过滤器添加到所有级别和记录器的所有实例。文档建议将过滤器添加到处理程序而不是记录器,否则它不会在外部模块中使用过滤器。以最明显的方式执行此操作(创建一个处理程序,添加过滤器,然后将处理程序附加到根记录器)但是给了我意想不到的行为。我要么根本没有输出,也没有错误消息,或者(首先使用 basicConfig 时)我得到了正确的行为,只是它也给出了错误消息。我怀疑在最后一种情况下,我实际上得到了两个处理程序,其中一个工作正常,另一个工作错误。

到目前为止,我已经提出了以下解决方案,我认为它不是很优雅(感谢https://stackoverflow.com/a/938800/819110)。丑陋的部分是我必须手动从 Logger 中提取 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 问题。

4

2 回答 2

1

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

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

于 2012-06-01T10:17:04.903 回答
1

如果您使用的是 Python 2.7 或更高版本,则可以使用dictConfig(),它确实支持配置中的过滤器。如果您使用的是较早版本的 Python,则可以使用该logutils项目为您提供dictConfig()功能。logutils如果您不希望对dictConfig().

于 2012-06-01T12:11:19.503 回答