Может ли структурированное ведение журнала выполняться с помощью стандартной библиотеки Pythons?

Я недавно читал о структурированной регистрации ( здесь). Идея состоит в том, чтобы вести журнал не путем добавления простых строк в виде строки в файл журнала, а вместо этого в виде объектов JSON. Это позволяет анализировать файл журнала автоматическими инструментами.

Могут питоны logging в библиотеке делать структурированные логи? Если нет, то есть ли "основное" решение для этого (например, как numpy/scipy является основным решением для научных расчетов)? я нашел structlog, но я не уверен, насколько это широко распространено.

3 ответа

Решение

Если вы установите 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

Вы смотрели на раздел сайта 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))

Что приводит к следующему журналу.

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

Надеюсь это поможет.

Что касается 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"}}

Другие вопросы по тегам