【问题标题】:Can structured logging be done with Pythons standard library?可以使用 Python 标准库完成结构化日志记录吗?
【发布时间】:2018-06-18 15:14:46
【问题描述】:

我最近读到了结构化日志记录 (here)。这个想法似乎不是通过将简单的字符串作为一行附加到日志文件来记录,而是通过 JSON 对象来记录。这使得通过自动工具分析日志文件成为可能。

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

【问题讨论】:

  • 我真的不记得我为什么不喜欢structlog。我认为主要的一点是它与标准日志库有点不同,因此可能比下面的其他解决方案更锁定。
  • 更新:遇到了 structlog 的问题 - 一个第 3 方项目已经在配置处理程序。使用 structlog,我无法删除它们。使用 pythonjsonlogger 没问题。

标签: python logging structured-logging


【解决方案1】:

您是否看过 python docs site section describing Implementing structured logging 来解释如何将 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))

这会导致以下日志。

message 1 >>> {"fnum": 123.456, "num": 123, "bar": "baz", "foo": "bar"}

希望这会有所帮助。

【讨论】:

  • 使用_ 对我来说似乎很不安全,因为如果您需要变量名,它是标准变量,但不打算这样做(例如,用于解压缩foo, _, bar = ('interesting', 'boring', 'important')
  • @MartinThomas,我同意,但是在我的辩护中,我只是在python docs 上提供了示例示例。我现在修改了答案并将_ 替换为变量m。希望这会有所帮助。
【解决方案2】:

如果您安装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

例外情况

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

Can I make Python output exceptions in one line / via logging?

【讨论】:

    【解决方案3】:

    截至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,
      )
      return record
    logging.setLogRecordFactory(record_factory)
    
    

    打电话给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"}}

    【讨论】:

      猜你喜欢
      • 2017-01-31
      • 1970-01-01
      • 2011-10-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-03-24
      • 2021-12-27
      • 2013-08-16
      相关资源
      最近更新 更多