可以使用Pythons标准库完成结构化日志记录吗?

Mar*_*oma 4 python logging structured-logging

我最近阅读了有关结构化日志的信息(此处)。这个想法似乎不是通过将简单的字符串作为一行添加到日志文件中来进行记录,而是通过JSON对象进行记录。这样就可以通过自动工具分析日志文件。

Pythons logging库可以进行结构化日志记录吗?如果不是,是否有“主流”解决方案(例如numpy / scipy是科学计算的主流解决方案)?我找到了structlog,但是我不确定它的普及程度。

use*_*679 17

py3.2开始,可以使用标准库来执行此操作,不需要外部依赖项:

from datetime import datetime
import json
import logging
import traceback


APP_NAME = 'hello world json logging'
APP_VERSION = 'git rev-parse HEAD'
LOG_LEVEL = logging._nameToLevel['INFO']


class JsonEncoderStrFallback(json.JSONEncoder):
  def default(self, obj):
    try:
      return super().default(obj)
    except TypeError as exc:
      if 'not JSON serializable' in str(exc):
        return str(obj)
      raise


class JsonEncoderDatetime(JsonEncoderStrFallback):
  def default(self, obj):
    if isinstance(obj, datetime):
      return obj.strftime('%Y-%m-%dT%H:%M:%S%z')
    else:
      return super().default(obj)


logging.basicConfig(
  format='%(json_formatted)s',
  level=LOG_LEVEL,
  handlers=[
    # if you wish to also log to a file:
    # logging.FileHandler(log_file_path, 'a'),
    logging.StreamHandler(sys.stdout),
  ],
)


_record_factory_bak = logging.getLogRecordFactory()
def record_factory(*args, **kwargs) -> logging.LogRecord:
  record = _record_factory_bak(*args, **kwargs)
  
  record.json_formatted = json.dumps(
    {
      'level': record.levelname,
      'unixtime': record.created,
      'thread': record.thread,
      'location': '{}:{}:{}'.format(
        record.pathname or record.filename,
        record.funcName,
        record.lineno,
      ),
      'exception': record.exc_info,
      'traceback': (
        traceback.format_exception(*record.exc_info)
        if record.exc_info
        else None
      ),
      'app': {
        'name': APP_NAME,
        'releaseId': APP_VERSION,
        'message': record.getMessage(),
      },
    },
    cls=JsonEncoderDatetime,
  )

  # clear exc data since it is included in the json format
  # without clearing this, logging.exception will print the
  # traceback across multiple lines, which is not json formatted
  record.exc_info = None
  record.exc_text = None

  return record
logging.setLogRecordFactory(record_factory)

Run Code Online (Sandbox Code Playgroud)

打电话logging.info('HELLO %s', 'WORLD')...

... 结果是{"level": "INFO", "unixtime": 1623532882.421775, "thread": 4660305408, "location": "<ipython-input-3-abe3276ceab4>:<module>:1", "exception": null, "traceback": null, "app": {"name": "hello world json logging", "releaseId": "git rev-parse HEAD", "message": "HELLO WORLD"}}


Ani*_*l_M 6

您是否看过python docs网站部分,该部分描述了实施结构化日志记录,该节解释了如何将python内置记录器用于结构化日志记录?

下面是上面站点中列出的一个简单示例。

import json
import logging

class StructuredMessage(object):
    def __init__(self, message, **kwargs):
        self.message = message
        self.kwargs = kwargs

    def __str__(self):
        return '%s >>> %s' % (self.message, json.dumps(self.kwargs))

m = StructuredMessage   # optional, to improve readability

logging.basicConfig(level=logging.INFO, format='%(message)s')
logging.info(m('message 1', foo='bar', bar='baz', num=123, fnum=123.456))
Run Code Online (Sandbox Code Playgroud)

这将导致以下日志。

message 1 >>> {"fnum": 123.456, "num": 123, "bar": "baz", "foo": "bar"}
Run Code Online (Sandbox Code Playgroud)

希望这可以帮助。

  • @MartinThomas,我同意,但是在我的辩护中,我只是在 `python` 文档中提供了示例示例。我现在修改了答案并用变量 `m` 替换了 `_`。希望这可以帮助。 (2认同)

Mar*_*oma 5

如果您安装python-json-logger(288 个星,70 个分支)并具有如下所示的日志记录配置 (YAML),您将获得一个结构化日志文件。

version: 1
formatters:
    detailed:
        class: logging.Formatter
        format: '[%(asctime)s]:[%(levelname)s]: %(message)s'
    json:
        class: pythonjsonlogger.jsonlogger.JsonFormatter
        format: '%(asctime)s %(levelname)s %(message)s'
handlers:
    console:
        class: logging.StreamHandler
        level: INFO
        formatter: detailed
    file:
        class: logging.FileHandler
        filename: logfile.log
        level: DEBUG
        formatter: json
root:
    level: DEBUG
    handlers:
        - console
        - file
Run Code Online (Sandbox Code Playgroud)

例外情况

您可能还想使用结构化格式进行异常/回溯。

请参阅我可以在一行中/通过日志记录生成 Python 输出异常吗?