Как мне обрабатывать чередующиеся исключения из разных вилок Gunicorn?

У меня есть приложение Flask, работающее в разветвленной среде Gunicorn, но трассировки стека чередуются в файле журнала. Может ли каждая вилка иметь свой собственный файл журнала? или каждый регистратор может иметь эксклюзивный доступ при записи в журнал?


person rbp    schedule 06.06.2013    source источник
comment
Не уверен, поможет ли это, мы работали с ведением журнала Gunicorns исключительно в stdout. Наша система управления процессами обеспечивает запись stdout каждого приложения, такого как Gunicorn, в реальный файл журнала, который можно вращать/удалять и т. д. И приложения Gunicorn совершенно не в курсе всего этого ... Никогда не наблюдал чередования трасс треков в нашей настройке.   -  person Pavel Repin    schedule 07.06.2013


Ответы (2)


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

Да, хотя вам, вероятно, это не нужно или не нужно. Самый простой способ сделать это — просто вставить os.getpid() где-нибудь в имени файла.

или каждый регистратор может иметь эксклюзивный доступ при записи в журнал?

Есть несколько способов сделать это, но самый очевидный — просто заменить threading.RLock по умолчанию в logging на multiprocessing.RLock.

Согласно документам, вы делаете это, переопределяя createLock, acquire и release. Так:

class CrossProcessFileHandler(logging.FileHandler):
    def createLock(self):
        self.lock = multiprocessing.RLock()
    def acquire(self):
        self.lock.acquire()
    def release(self):
        self.lock.release()

А теперь просто используйте это вместо FileHandler.

Просто убедитесь, что регистратор инициализирован в родительском процессе; если каждый потомок создаст свою отдельную межпроцессную блокировку, это ничему не поможет.


Обратите внимание, что если вы заботитесь о межплатформенной переносимости, очевидный тривиальный код может работать так, как ожидается, в POSIX, но не в Windows. (Я недостаточно знаю, как gunicorn работает в Windows, чтобы догадаться…) Но вы можете справиться с этим, просто не блокируя в Windows, потому что по умолчанию FileHandler открывает файл для монопольного доступа, записывает и закрывает, то есть файловая система уже делает вашу блокировку за вас. (Этот трюк не работает в POSIX, потому что нет такой вещи, как эксклюзивный доступ в стиле Windows, или, скорее, есть эквиваленты на большинстве платформ и файловых систем, но они не переносимы, и вам нужно выйти из способ сделать это вместо того, чтобы получать его по умолчанию, хотите вы этого или нет.)


Реализация acquire и release для всех встроенных обработчиков для CPython с 2.3 по 3.3 и все альтернативные реализации всегда были такими:

if self.lock:
    self.lock.acquire()

Итак, вы увидите код, который обманывает, переопределяя только createLock. Я сам делал это несколько раз и видел это в различных сторонних проектах. Но на самом деле документация не гарантирует этого, поэтому вы должны переопределить и два других.

person abarnert    schedule 06.06.2013

Решение @abarnert работает очень хорошо, однако требует подкласса каждого обработчика, используемого в проекте. Это может быть упрощено декоратором класса:

def multiprocess_handler(cls):
    class MultiProcessHandler(cls):
        def createLock(self):
            self.lock = multiprocessing.RLock()
    return MultiProcessHandler

MFileHandler = multiprocess_handler(logging.FileHandler)
MRotatingFileHandler = multiprocess_handler(logging.handlers.RotatingFileHandler)
# etc.
person xmedeko    schedule 25.09.2016