Может ли структурированное ведение журнала выполняться с помощью стандартной библиотеки 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"}}