Как я могу войти из своего приложения на python в spunk, если я использую celery в качестве планировщика задач?

У меня есть скрипт Python, работающий на сервере, который должен запускаться раз в день планировщиком сельдерея. Я хочу отправить свои логи прямо из скрипта на спленк. Я пытаюсь использовать эту библиотеку splunk_handler. Если я запускаю splunk_handler без сельдерея локально, это похоже на работу. Но если я запускаю его вместе с сельдереем, кажется, что нет журналов, которые доходят до splunk_handler. Консоль-Log:

[SplunkHandler DEBUG] Поток таймера выполнен, но полезная нагрузка недоступна для отправки

Как правильно настроить регистраторы, чтобы все журналы отправлялись в splunk_handler?

Очевидно, что сельдерей устанавливает свои собственные регистраторы и перезаписывает root-logger из python. Я попробовал несколько вещей, включая подключение сигнала setup_logging от сельдерея, чтобы он не перезаписывал регистраторы, или настройку регистратора в этом сигнале.

import logging
import os

from splunk_handler import SplunkHandler

Вот так я настраиваю регистратор в начале файла

logger = logging.getLogger(__name__)
splunk_handler = SplunkHandler(
host=os.getenv('SPLUNK_HTTP_COLLECTOR_URL'),
port=os.getenv('SPLUNK_HTTP_COLLECTOR_PORT'),
token=os.getenv('SPLUNK_TOKEN'),
index=os.getenv('SPLUNK_INDEX'),
debug=True)

splunk_handler.setFormatter(logging.BASIC_FORMAT)
splunk_handler.setLevel(os.getenv('LOGGING_LEVEL', 'DEBUG'))
logger.addHandler(splunk_handler)

Инициализация сельдерея (не уверен, если worker_hijack_root_logger должен быть установлен в False...)

app = Celery('name_of_the_application', broker=CELERY_BROKER_URL)
app.conf.timezone = 'Europe/Berlin'
app.conf.update({
    'worker_hijack_root_logger': False,
})

Здесь я подключаюсь к сигналу setup_logging из сельдерея

@setup_logging.connect()
def config_loggers(*args, **kwags):
    pass
    # logger = logging.getLogger(__name__)
    # splunk_handler = SplunkHandler(
    #     host=os.getenv('SPLUNK_HTTP_COLLECTOR_URL'),
    #     port=os.getenv('SPLUNK_HTTP_COLLECTOR_PORT'),
    #     token=os.getenv('SPLUNK_TOKEN'),
    #     index=os.getenv('SPLUNK_INDEX'),
    #     debug=True)
    #
    # splunk_handler.setFormatter(logging.BASIC_FORMAT)
    # splunk_handler.setLevel(os.getenv('LOGGING_LEVEL', 'DEBUG'))
    # logger.addHandler(splunk_handler)

Отчет

logger.info("ARBITRARY LOG MESSAGE")

При активации отладки в обработчике спленка (установите в True), обработчик ответвления выходит из системы, что полезная нагрузка недоступна, как описано выше. У кого-нибудь есть идея, что не так с моим кодом?

1 ответ

Решение

После нескольких часов выяснения, что в итоге может быть не так с моим кодом, у меня теперь есть результат, который меня удовлетворяет. Сначала я создал файл loggingsetup.py где я настроил свои логгеры python с помощью dictConfig:

LOGGING = {
    'version': 1,
    'disable_existing_loggers': True,
    'formatters': { # Sets up the format of the logging output
        'simple': {
            'format': '%(asctime)s - %(name)s - %(levelname)s - %(message)s',
             'datefmt': '%y %b %d, %H:%M:%S',
            },
        },
    'filters': {
        'filterForSplunk': { # custom loggingFilter, to not have Logs logged to Splunk that have the word celery in the name
            '()': 'loggingsetup.RemoveCeleryLogs', # class on top of this file
            'logsToSkip': 'celery' # word that it is filtered for
        },
    },
    'handlers': {
        'splunk': { # handler for splunk, level Warning. to not have many logs sent to splunk
            'level': 'WARNING',
            'class': 'splunk_logging_handler.SplunkLoggingHandler',
            'url': os.getenv('SPLUNK_HTTP_COLLECTOR_URL'),
            'splunk_key': os.getenv('SPLUNK_TOKEN'),
            'splunk_index': os.getenv('SPLUNK_INDEX'),
            'formatter': 'simple',
            'filters': ['filterForSplunk']
        },
        'console': { 
            'level': 'DEBUG',
            'class': 'logging.StreamHandler',
            'stream': 'ext://sys.stdout',
            'formatter': 'simple',
        },
    },
    'loggers': { # the logger, root is used
        '': {
            'handlers': ['console', 'splunk'],
            'level': 'DEBUG',
            'propagate': 'False', # does not give logs to other logers
        }
    }
}

Для фильтра журналирования мне пришлось создать класс, который наследуется от класса logging. Filter. Класс также опирается на файл loggingsetup.py

class RemoveCeleryLogs(logging.Filter): # custom class to filter for celery logs (to not send them to Splunk)
    def __init__(self, logsToSkip=None):
        self.logsToSkip = logsToSkip

    def filter(self, record):
        if self.logsToSkip == None:
            allow = True
        else:
            allow = self.logsToSkip not in record.name
        return allow

После этого вы можете настроить регистраторы следующим образом:

logging.config.dictConfig(loggingsetup.LOGGING)
logger = logging.getLogger('')

И так как сельдерей перенаправил свои журналы, и журналы были удвоены, мне пришлось обновить app.conf:

app.conf.update({
    'worker_hijack_root_logger': False, # so celery does not set up its loggers
    'worker_redirect_stdouts': False, # so celery does not redirect its logs
})

Следующая проблема, с которой я столкнулся, заключалась в том, что выбранная мной библиотека Splunk_Logging перепутала что-то с URL. Поэтому мне пришлось создать свой собственный класс splunk_handler, который наследуется от класса logging.Handler. Важные строки здесь следующие:

auth_header = {'Authorization': 'Splunk {0}'.format(self.splunk_key)}
json_message = {"index": str(self.splunk_index), "event": data}
r = requests.post(self.url, headers=auth_header, json=json_message)

Я надеюсь, что смогу помочь кому-то с этим ответом, кто сталкивается с подобными проблемами с регистрацией python, spunk и сельдерея!:)

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