首页 > 解决方案 > 使用 python 记录为 JSON 行

问题描述

我开发了一个函数来使用 python 创建日志文件到 JSON 行文件中:

import logging
import sys
from datetime import datetime


def log_start(log_prefix):
    now = datetime.now()
    log_id = str(now).replace(':', '').replace(' ', '').replace('.', '').replace('-', '')[:14]
    log_name = '/mnt/jarvis/logs/{}_{}.txt'.format(log_prefix, log_id)

    root = logging.getLogger()
    if root.handlers:
        root.handlers = []

    logging.basicConfig(level=logging.INFO, filename=log_name, filemode='a+',
                        format='''{{"log_id":"{}", "created_date":"%(asctime)s.%(msecs)03d", "action_text":"%(message)s"}}'''.format(
                            log_id),
                        datefmt="%Y-%m-%dT%H:%M:%S")
    root = logging.getLogger()
    root.setLevel(logging.INFO)

    handler = logging.StreamHandler(sys.stdout)
    handler.setLevel(logging.INFO)
    formatter = logging.Formatter(
        '''{{"log_id":"{}", "created_date":"%(asctime)s.%(msecs)03d", "action_text":"%(message)s"}}'''.format(
            log_id),
        datefmt="%Y-%m-%dT%H:%M:%S")
    handler.setFormatter(formatter)
    root.addHandler(handler)

    return log_name, log_id

它工作得很好。但是,如果日志消息中有换行符或双引号之类的消息,我会遇到问题,它不再是有效的 JSON。有没有办法让%(message)be 字符串成为“有效的 JSON 字符串”,而无需我每次都对其进行更正?

编辑

这个问题的一个例子是,我想在日志中查看回溯,所以像这样的回溯会因为引号和 \n 字符而导致问题:

Traceback (most recent call last):
  File "/var/task/lego/bricks/tableau/workbook.py", line 65, in refresh_extracts
    output = subprocess.check_output(command, shell=True)
  File "/var/lang/lib/python3.6/subprocess.py", line 356, in check_output
    **kwargs).stdout
  File "/var/lang/lib/python3.6/subprocess.py", line 438, in run
    output=stdout, stderr=stderr)
subprocess.CalledProcessError: Command '***REDACTED***' returned non-zero exit status 1.

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/var/task/jarvis/event_triggered/refresh_dashboard.py", line 21, in refresh_dashboard
    workbook.refresh_extracts()
  File "/var/task/lego/bricks/tableau/workbook.py", line 73, in refresh_extracts
    traceback.format_exc()))
  File "/var/task/lego/power_functions/error_handling/graceful_fail.py", line 16, in graceful_fail
    raise RuntimeError('This is a graceful fail, notifications sent based on attributes.')
RuntimeError: This is a graceful fail, notifications sent based on attributes.

标签: pythonpython-3.xlogging

解决方案


您可以根据需要有条件地测试和格式化您的字符串到/来自 JSON。在这里,我正在检查JSONDecodeError在这种情况下经常抛出的具体情况,但您可以捕获任何您喜欢的异常,或者其中的许多异常。考虑使用这样的检查来构建您的消息字符串:

import json

bad_json = '''
Traceback (most recent call last):
  File "/var/task/lego/bricks/tableau/workbook.py", line 65, in refresh_extracts
    output = subprocess.check_output(command, shell=True)
  File "/var/lang/lib/python3.6/subprocess.py", line 356, in check_output
    **kwargs).stdout
  File "/var/lang/lib/python3.6/subprocess.py", line 438, in run
    output=stdout, stderr=stderr)
subprocess.CalledProcessError: Command '***REDACTED***' returned non-zero exit status 1.

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/var/task/jarvis/event_triggered/refresh_dashboard.py", line 21, in refresh_dashboard
    workbook.refresh_extracts()
  File "/var/task/lego/bricks/tableau/workbook.py", line 73, in refresh_extracts
    traceback.format_exc()))
  File "/var/task/lego/power_functions/error_handling/graceful_fail.py", line 16, in graceful_fail
    raise RuntimeError('This is a graceful fail, notifications sent based on attributes.')
RuntimeError: This is a graceful fail, notifications sent based on attributes.
'''
try:
    test_message = json.loads(bad_json) # this fails in the above case
except json.decoder.JSONDecodeError:
    good_json = json.dumps({"message": bad_json})
    test_message = json.loads(good_json)

print(test_message)

json.dumps(your_message_string)结果(如果您愿意,可以将其转储为更具可读性的文本):

{'message': '\nTraceback (most recent call last):\n  File "/var/task/lego/bricks/tableau/workbook.py", line 65, in refresh_extracts\n    output = subprocess.check_output(command, shell=True)\n  File "/var/lang/lib/python3.6/subprocess.py", line 356, in check_output\n    **kwargs).stdout\n  File "/var/lang/lib/python3.6/subprocess.py", line 438, in run\n    output=stdout, stderr=stderr)\nsubprocess.CalledProcessError: Command \'***REDACTED***\' returned non-zero exit status 1.\n\nDuring handling of the above exception, another exception occurred:\n\nTraceback (most recent call last):\n  File "/var/task/jarvis/event_triggered/refresh_dashboard.py", line 21, in refresh_dashboard\n    workbook.refresh_extracts()\n  File "/var/task/lego/bricks/tableau/workbook.py", line 73, in refresh_extracts\n    traceback.format_exc()))\n  File "/var/task/lego/power_functions/error_handling/graceful_fail.py", line 16, in graceful_fail\n    raise RuntimeError(\'This is a graceful fail, notifications sent based on attributes.\')\nRuntimeError: This is a graceful fail, notifications sent based on attributes.\n'}

这可能是您通过日志格式化程序传递的函数、lambda 等。


推荐阅读