从Python日志记录YAML配置文件

时间:2016-07-25 15:23:33

标签: python logging yaml

考虑以下Python logging YAML配置文件的片段:

version: 1
formatters:
  simple:
    format: '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
handlers:
  logfile:
    class: logging.handlers.TimedRotatingFileHandler
    level: DEBUG
    filename: some_fancy_import_name.generate_filename_called_error
    backupCount: 5
    formatter: simple

我想以这种方式加载这个YAML配置文件:

with open('logging.yaml', 'r') as fd:
    config = yaml.safe_load(fd.read())
logging.config.dictConfig(config)

特别注意filename应写入日志的handler。在普通的Python代码中,我希望some_fancy_import_name.generate_filename_called_errorlog生成字符串'error.log'。总而言之,我想说这个日志记录处理程序应该写入当前目录中的文件'error.log'

然而,事实证明,事实并非如此。当我查看当前目录时,我看到一个名为'some_fancy_import_name.generate_filename_called_errorlog'的文件。

为什么要经历所有这些麻烦?

我希望filename以编程方式确定。我已成功尝试使用普通的Python脚本以这种方式配置日志记录:

# fancy import name
from os import environ as env

# Programmatically determine filename path
log_location = env.get('OPENSHIFT_LOG_DIR', '.')
log_filename = os.path.join(log_location, 'error')
handler = logging.handlers.TimedRotatingFileHandler(log_filename)

了解如何从环境变量中推断出log_filename路径。

我想将其转换为YAML配置文件。有可能吗?

也许我可能需要挖掘dict生成的yaml.safe_load(fd.read())并做一些eval()的事情?

3 个答案:

答案 0 :(得分:4)

您可以添加自定义构造函数并使用特殊标记标记该值,以便在加载构造函数时执行它:

import yaml

def eval_constructor(loader, node):
  return eval(loader.construct_scalar(node))

yaml.add_constructor(u'!eval', eval_constructor)

some_value = '123'

config = yaml.load("""
version: 1
formatters:
  simple:
    format: '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
handlers:
  logfile:
    class: logging.handlers.TimedRotatingFileHandler
    level: DEBUG
    filename: !eval some_value
    backupCount: 5
    formatter: simple
""")

print config['handlers']['logfile']['filename']

这会打印123,因为值some_value的标记为!eval,因此加载了eval_constructor

请注意eval配置数据的安全隐患。任意Python代码都可以通过将其写入YAML文件来执行!

答案 1 :(得分:0)

解决方案

感谢flyx's answer,我就这样做了:

import logging
import yaml
from os import environ as env

def constructor_logfilename(loader, node):
    value = loader.construct_scalar(node)
    return os.path.join(env.get('OPENSHIFT_LOG_DIR', '.'), value)

yaml.add_constructor(u'!logfilename', constructor_logfilename)
with open('logging.yaml', 'r') as fd:
    config = yaml.load(fd.read())
logging.config.dictConfig(config)

logging.yaml文件中,这是重要的代码段:

...
filename: !logfilename error.log
...

答案 2 :(得分:0)

我刚才遇到了这个问题,然后以一种更无聊的方式进行了处理。我从yaml文件中删除了日志文件处理程序的filename属性,将其添加到yaml.load创建的配置字典中,如下所示:

with open('logging.yaml', 'r') as fd:
    config = yaml.load(fd.read())
config['handlers']['logfile']['filename'] = generate_logfile_name(...)
logging.config.dictConfig(config)