【问题标题】:Format Airflow Logs in JSON以 JSON 格式格式化 Airflow 日志
【发布时间】:2019-03-31 15:55:32
【问题描述】:

我需要以 JSON 格式将 Apache Airflow 日志记录到标准输出。 Airflow 似乎并没有开箱即用地投射出这种能力。我找到了几个能够完成这项任务的 python 模块,但我无法让实现工作。

目前我在airflow/utils/logging.py中应用一个类来修改记录器,如下图:

from pythonjsonlogger import jsonlogger

class StackdriverJsonFormatter(jsonlogger.JsonFormatter, object):
def __init__(self, fmt="%(levelname) %(asctime) %(nanotime) %(severity) %(message)", style='%', *args, **kwargs):
    jsonlogger.JsonFormatter.__init__(self, fmt=fmt, *args, **kwargs)

def process_log_record(self, log_record):
    if log_record.get('level'):
        log_record['severity'] = log_record['level']
        del log_record['level']
    else: 
        log_record['severity'] = log_record['levelname']
        del log_record['levelname']
    if log_record.get('asctime'):
        log_record['timestamp'] = log_record['asctime']
        del log_record['asctime']
    now = datetime.datetime.now().strftime('%Y-%m-%dT%H:%M:%S.%fZ')
    log_record['nanotime'] = now
    return super(StackdriverJsonFormatter, self).process_log_record(log_record)

我在/airflow/settings.py中实现了这段代码,如下所示:

from airflow.utils import logging as logconf

def configure_logging(log_format=LOG_FORMAT):
     handler = logconf.logging.StreamHandler(sys.stdout)
     formatter = logconf.StackdriverJsonFormatter()
     handler.setFormatter(formatter)
     logging = logconf.logging.getLogger()
     logging.addHandler(handler)
''' code below was original airflow source code
     logging.root.handlers = []
     logging.basicConfig(
         format=log_format, stream=sys.stdout, level=LOGGING_LEVEL)
'''

我尝试了几种不同的变体,但无法让 python-json-logger 将日志转换为 JSON。也许我没有进入根记录器?我考虑过的另一个选择是将日志手动格式化为 JSON 字符串。也没有运气。感谢您提供任何替代的想法、提示或支持。

干杯!

【问题讨论】:

  • 不清楚是否需要自己创建一个 Airflow 进程来将日志输出到标准输出,或者它是否可以是任何其他进程将 Airflow 的日志输出到标准输出。
  • @SergiyKolesnikov - 最好的解决方案是在没有额外的日志记录层的情况下处理这个问题。想将此流发布到标准输出并由 fluentd 直接读取。在 Airflow 中实现任何自定义处理程序(以及格式化程序)时遇到问题。每当我这样做(如上面的 sn-p 中)时,它似乎都拆除了整个日志记录设置。有小费吗?我可以让configure_logging() 方法工作的唯一方法是使用logging.basicConfig(... 方法,它需要一个字符串作为格式而不是其他方法传入。

标签: python logging airflow python-jsonschema


【解决方案1】:

我不知道你是否曾经解决过这个问题,但经过一些令人沮丧的修补后,我最终得到了它与气流的配合。作为参考,我关注了这篇文章的很多内容以使其正常工作:https://www.astronomer.io/guides/logging/。主要问题是气流记录仅接受记录格式的字符串模板,json-logging 无法插入。因此,您必须创建自己的日志记录类并将其连接到自定义日志记录配置类。

  1. 将日志模板here 复制到您的$AIRFLOW_HOME/config 文件夹中,并将DEFAULT_CONFIG_LOGGING 更改为CONFIG_LOGGING。成功后,打开气流,您将在气流启动时收到一条日志消息,上面写着Successfully imported user-defined logging config from logging_config.LOGGING_CONFIG。如果这是 config 文件夹中的第一个 .py 文件,请不要忘记添加一个空白的 __init__.py 文件以让 python 拾取它

  2. 编写您的自定义 JsonFormatter 以注入您的处理程序。我是从这个one 开始的。

  3. 编写自定义日志处理程序类。由于我在寻找 JSON 日志记录,所以我的看起来像这样:

from airflow.utils.log.file_processor_handler import FileProcessorHandler
from airflow.utils.log.file_task_handler import FileTaskHandler
from airflow.utils.log.logging_mixin import RedirectStdHandler
from pythonjsonlogger import jsonlogger

class JsonStreamHandler(RedirectStdHandler):
    def __init__(self, stream):
        super(JsonStreamHandler, self).__init__(stream)
        json_formatter = CustomJsonFormatter('(timestamp) (level) (name) (message)')
        self.setFormatter(json_formatter)


class JsonFileTaskHandler(FileTaskHandler):
    def __init__(self, base_log_folder, filename_template):
        super(JsonFileTaskHandler, self).__init__(base_log_folder, filename_template)
        json_formatter = CustomJsonFormatter('(timestamp) (level) (name) (message)')
        self.setFormatter(json_formatter)


class JsonFileProcessorHandler(FileProcessorHandler):
    def __init__(self, base_log_folder, filename_template):
        super(JsonFileProcessorHandler, self).__init__(base_log_folder, filename_template)
        json_formatter = CustomJsonFormatter('(timestamp) (level) (name) (message)')
        self.setFormatter(json_formatter)


class JsonRotatingFileHandler(RotatingFileHandler):
    def __init__(self, filename, mode, maxBytes, backupCount):
        super(JsonRotatingFileHandler, self).__init__(filename, mode, maxBytes, backupCount)
        json_formatter = CustomJsonFormatter('(timestamp) (level) (name) (message)')
        self.setFormatter(json_formatter)
  1. 将它们连接到自定义 logging_config.py 文件中的日志记录配置。
'handlers': {
    'console': {
        'class': 'logging_handler.JsonStreamHandler',
        'stream': 'sys.stdout'
    },
    'task': {
        'class': 'logging_handler.JsonFileTaskHandler',
        'base_log_folder': os.path.expanduser(BASE_LOG_FOLDER),
        'filename_template': FILENAME_TEMPLATE,
    },
    'processor': {
        'class': 'logging_handler.JsonFileProcessorHandler',
        'base_log_folder': os.path.expanduser(PROCESSOR_LOG_FOLDER),
        'filename_template': PROCESSOR_FILENAME_TEMPLATE,
    }
}
...

DEFAULT_DAG_PARSING_LOGGING_CONFIG = {
    'handlers': {
        'processor_manager': {
            'class': 'logging_handler.JsonRotatingFileHandler',
            'formatter': 'airflow',
            'filename': DAG_PROCESSOR_MANAGER_LOG_LOCATION,
            'mode': 'a',
            'maxBytes': 104857600,  # 100MB
            'backupCount': 5
        }
    }
...

并且应该输出json日志,在DAG日志和输出中也是如此。

希望这会有所帮助!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-12-18
    • 1970-01-01
    相关资源
    最近更新 更多