我在一个使用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),但似乎没有办法将此过滤器添加到所有级别和所有记录器实例中一旦。 文档建议将过滤器添加到Handler而不是Logger,因为否则它将不会在外部模块中使用过滤器。以最明显的方式执行此操作(创建处理程序,添加Filter,然后将Handler附加到根Logger)但是给了我意想不到的行为。我要么根本没有输出,也没有错误消息,或者(首先使用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问题。
答案 0 :(得分:1)
首先,让我们清理一个误解:记录器不是添加此信息的正确位置 - 记录器只是在dict中查找日志级别的键。没有必要为每个记录器添加内存使用量,因为格式化程序不会在那里查看。
正确的解决方案是编写自己的格式化程序(就像你一样)并实现一个新变量。没有更好的方法来做到这一点。例如,您不希望获取永远不会写入日志文件的记录器调用的内存信息。
答案 1 :(得分:1)
如果您使用的是Python 2.7或更高版本,则可以使用dictConfig()
,它支持配置中的过滤器。如果您使用的是早期版本的Python,则可以使用logutils
项目为您提供dictConfig()
功能。如果您不希望对logutils
有外部依赖,可以将dictConfig()
内容从那里复制到您自己的代码中(如Django所做的那样)。