Захватите все stdout / stderr в structlog для создания журналов JSON - PullRequest
0 голосов
/ 18 марта 2019

В настоящее время я пытаюсь отойти от print () и начать с централизованного сбора журналов, используя стек ELK и модуль structlog для генерации структурированных строк журнала json.Это прекрасно работает для модулей, которые я написал сам, используя модуль loggingHelper, который я могу импортировать и использовать с

logger = Logger()

в других модулях и скриптах.Это класс модуля loggingHelper:

class Logger:
    """
    Wrapper Class to import within other modules and scripts
    All the config and log binding (script
    """
    def __init__(self):
        self.__log = None
        logging.basicConfig(level=logging.DEBUG, format='%(message)s')
        structlog.configure(logger_factory=LoggerFactory(),
                            processors=[structlog.stdlib.add_log_level,
                            structlog.processors.TimeStamper(fmt="iso"),
                            structlog.processors.JSONRenderer()])
        logger = structlog.get_logger()
        main_script = os.path.basename(sys.argv[0]) if sys.argv[0] else None
        frame = inspect.stack()[1]
        log_invocation = os.path.basename(frame[0].f_code.co_filename)
        user = getpass.getuser()

        """
        Who executed the __main__, what was the executed __main__ file, 
        where did the log event happen?
        """
        self.__log = logger.bind(executedScript = main_script,
                                 logBirth = log_invocation,
                                 executingUser = user)

    def info(self, msg, **kwargs):
        self.__log.info(msg, **kwargs)

    def debug(self, msg, **kwargs):
        self.__log.debug(msg, **kwargs)

    def error(self, msg, **kwargs):
        self.__log.error(msg, **kwargs)

    def warn(self, msg, **kwargs):
        self.__log.warning(msg, **kwargs)

Это производит красиво отформатированный вывод (один JSON на строку), который filebeat может читать и пересылать в Elasticsearch.Однако сторонние библиотекари полностью разбивают хорошо отформатированные журналы.

{"executingUser": "xyz", "logBirth": "efood.py", "executedScript": "logAlot.py", "context": "SELECT displayname FROM point_of_sale WHERE name = '123'", "level": "debug", "timestamp": "2019-03-15T12:52:42.792398Z", "message": "querying local"}
{"executingUser": "xyz", "logBirth": "efood.py", "executedScript": "logAlot.py", "level": "debug", "timestamp": "2019-03-15T12:52:42.807922Z", "message": "query successful: got 0 rows"}
building service object
auth version used is: v4
Traceback (most recent call last):
  File "logAlot.py", line 26, in <module>
    ef.EfoodDataControllerMerchantCenter().get_displayname(123)
  File "/home/xyz/src/toolkit/commons/connectors/efood.py", line 1126, in get_displayname
    return efc.select_from_local(q)['displayname'].values[0]
IndexError: index 0 is out of bounds for axis 0 with size 0

Как видно, сообщения информационного уровня и уровня ошибок от сторонних библиотек (googleapiclient) печатаются без прохождения через процессоры журналирования..

Каков наилучший (и самый питонический) способ захвата и форматирования всего, что происходит при выполнении одного скрипта, с помощью написанного мной модуля loggingHelper?Является ли это даже лучшей практикой?

Редактировать: В настоящее время регистратор действительно выполняет запись в сам stdout, который затем перенаправляется в файл в crontab, используя >> и 2> & 1.Мне кажется, что это плохая практика, если я хочу перенаправить все, что записано в stdout / stderr, путем сторонней регистрации в библиотеке, потому что это приведет к циклу, правильно?Таким образом, моя цель - не перенаправление, а захват всего в моем процессоре регистрации.Соответственно изменил название.

Кроме того, вот приблизительный обзор того, чего я пытаюсь достичь.Я очень открыт для общей критики и предложений, которые отличаются от этого.enter image description here

Ответы [ 2 ]

1 голос
/ 18 марта 2019

Настройка модуля logging

Как вы уже поняли, structlog требует настройки функция ведения журнала уже существует в python.

http://www.structlog.org/en/stable/standard-library.html

logging.basicConfig поддерживает опции для stream и filename здесь

https://docs.python.org/3/library/logging.html#logging.basicConfig.

Либо вы указываете имя файла, к которому регистратор создаст дескриптор и направит весь свой вывод. В зависимости от того, как вы настроены, возможно, это будет файл, который вы обычно перенаправляете на

import logging

logging.basicConfig(level=logging.DEBUG, format='%(message)s', filename='output.txt')

или вы можете передать объект StringIO в конструктор, который вы позже сможете прочитать, а затем перенаправить к желаемому месту назначения вывода

import logging
import io

stream = io.StringIO()

logging.basicConfig(level=logging.DEBUG, format='%(message)s', stream=stream)

Подробнее о StringIO можно прочитать здесь

https://docs.python.org/3/library/io.html#io.TextIOBase

Как указал @ bruno в своем ответе, не делайте этого в __init__, так как вы можете в конечном итоге вызвать этот фрагмент кода несколько раз в одном и том же процессе.

0 голосов
/ 18 марта 2019

Перво-наперво: вам НЕ следует выполнять настройку логгера (logging.basicConfig, logging.dictConfig и т. Д.) В инициализаторе класса - конфигурирование логирования должно выполняться один раз и только один раз при запуске процесса. Весь смысл модуля logging состоит в том, чтобы полностью отделить запись вызовов

Второй момент: я не structlog эксперт (и это преуменьшение - фактически я впервые слышу об этом пакете), но в результате вы получите то, чего можно было ожидать от вашего фрагмента кода: только ваш в собственном коде используется structlog, все остальные библиотеки (stdlib или 3rd part) будут по-прежнему использовать регистратор stdlib и генерировать обычные текстовые журналы.

Из того, что я видел в structlog doc, похоже, что есть какой-то способ обернуть регистраторы stdlib, используя structlog.stdlib.LoggerFactory и , добавив определенные средства форматирования, чтобы иметь более согласованный вывод . Я не проверял это (пока), и официальный документ немного скудный и не имеет практического примера (по крайней мере, я не смог его найти), но эта статья , кажется, имеет более явный пример ( адаптированы к вашему собственному контексту и, конечно, в случае использования).

CAVEAT : как я уже говорил, я никогда не использовал structlog (впервые слышу об этой библиотеке), поэтому я мог неправильно понять некоторые вещи, и вам, конечно, придется поэкспериментировать, чтобы выяснить, как правильно настроить все это, чтобы заставить его работать как положено.

В качестве примечания: в Unix-подобных системах stdout предполагается для результатов программы (я имею в виду «ожидаемый результат» => фактические результатов программы ), а все ошибки / отчеты / сообщения отладки принадлежат stderr. Если у вас нет веских причин поступить иначе, вы должны попытаться придерживаться этого соглашения (по крайней мере, для инструментов командной строки, чтобы вы могли связывать / транслировать их в Unix).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...