Регистрация Python от нескольких процессов - PullRequest
5 голосов
/ 06 декабря 2011

Возможно, у меня есть долго работающая программа, которая в настоящее время имеет 4 процесса, но может быть настроена на большее.Я исследовал протоколирование из нескольких процессов с использованием Python's logging и использую подход SocketHandler, обсуждаемый здесь .У меня никогда не было проблем с одним логгером (без сокетов), но из того, что я прочитал, мне сказали, что он в конечном итоге потерпит неудачу.Насколько я понимаю, неизвестно, что произойдет, если вы попытаетесь записать в один и тот же файл одновременно.Мой код по существу выполняет следующее:

import logging
log = logging.getLogger(__name__)

def monitor(...):
    # Spawn child processes with os.fork()
    # os.wait() and act accordingly

def main():
    log_server_pid = os.fork()
    if log_server_pid == 0:
        # Create a LogRecordSocketServer (daemon)
        ...
        sys.exit(0)
    # Add SocketHandler to root logger
    ...
    monitor(<configuration stuff>)

if __name__ == "__main__":
    main()

Итак, мои вопросы: Нужно ли создавать новый log объект после каждого os.fork()?Что происходит с существующим глобальным log объектом?

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

Кроме того, каким образом выполняется метод "normal" (one log= expression)входа в один файл из нескольких процессов не удается?Это вызывает IOError / OSError?Или это просто не полностью записывает данные в файл?

Если бы кто-то мог предоставить ответ или ссылки, чтобы выручить меня, это было бы здорово.Спасибо.

FYI : я тестирую на Mac OS X Lion, и код, вероятно, будет работать на CentOS 6 VM на компьютере с Windows (если это имеет значение).Какое бы решение я не использовал, оно не должно работать в Windows, но должно работать в системе на основе Unix.

ОБНОВЛЕНИЕ: Этот вопрос начал отходить от ведения журнала конкретного поведения и больше касаетсясфера действия linux с файловыми дескрипторами во время форков.Я вытащил один из моих учебников для колледжа, и кажется, что если вы откроете файл в режиме добавления из двух процессов (не перед разветвлением), они оба смогут правильно записать в файл, если ваша запись не превышаетфактический буфер ядра (хотя может потребоваться использовать строковую буферизацию, но в этом еще нет уверенности).Это создает 2 записи таблицы файлов и одну запись таблицы v-узла.Открытие файла с последующим разветвлением не должно работать, но, похоже, до тех пор, пока вы не превысите буфер ядра, как раньше (я делал это в предыдущей программе).

Так что я думаю, если вы хотите независимую от платформы многопроцессорную регистрацию, вы используете сокеты и создаете новый SocketHandler после каждой развилки, чтобы быть безопасными, как предложил Vinay ниже (это должно работать везде).Для меня, так как у меня есть строгий контроль над тем, на какой ОС запускается мое программное обеспечение, я думаю, что я собираюсь использовать один глобальный log объект с FileHandler (открывается в режиме добавления по умолчанию и буферизуется в большинстве ОС),Документация для open гласит: «Отрицательная буферизация означает использование системного значения по умолчанию, которое обычно является линейной буферизацией для tty-устройств и полностью буферизовано для других файлов. Если не указано, используется системное значение по умолчанию».или я мог бы просто создать свой собственный поток регистрации, чтобы быть уверенным в буферизации строк.И чтобы быть ясным, я в порядке:

# Process A
a_file.write("A\n")
a_file.write("A\n")
# Process B
a_file.write("B\n")

производит ...

A\n
B\n
A\n

, пока это не производит ...

AB\n
\n
A\n

Vinay (или кто-либо еще), насколько я не прав?Дай мне знать.Спасибо за дополнительную ясность / уверенность, которую вы можете предоставить.

1 Ответ

2 голосов
/ 06 декабря 2011

Нужно ли создавать новый объект журнала после каждого os.fork ()? Что происходит с существующим объектом глобального журнала?

AFAIK объект глобального журнала остается указывать на один и тот же регистратор в родительских и дочерних процессах. Так что вам не нужно создавать новый. Тем не менее, я думаю, что вы должны создать и добавить SocketHandler после fork() в monitor(), чтобы сервер сокетов имел четыре разных соединения, по одному на каждый дочерний процесс. Если вы этого не сделаете, то дочерние процессы, разветвленные в monitor (), унаследуют SocketHandler и его дескриптор сокета от своего родителя, но я точно не знаю, что он будет плохо себя вести. Поведение, скорее всего, зависит от ОС, и вам может повезти с OSX.

Работая так, как я, я даже обхожу проблему, которую пытаюсь избежать (несколько открытых файлов / сокетов)? Будет ли это терпеть неудачу и почему это потерпит неудачу (я хотел бы быть в состоянии сказать, будут ли в будущем подобные реализации терпеть неудачу)?

Я бы не ожидал сбоя, если вы создадите сокет-соединение с сервером сокетов после последнего fork(), как я предлагал выше, но я не уверен, что поведение четко определено в любом другом случае. Вы ссылаетесь на несколько открытых файлов, но я не вижу ссылки на открытие файлов в вашем фрагменте псевдокода, а просто на открытие сокетов.

Кроме того, каким образом происходит сбой «нормального» (один лог = выражение) метода входа в один файл из нескольких процессов? Это вызывает IOError / OSError? Или он просто не полностью записывает данные в файл?

Я думаю, что поведение не является четко определенным, но можно ожидать, что режимы сбоев будут отображаться как чередующиеся сообщения журнала от различных процессов в файле, например,

Process A writes first part of its message
Process B writes its message
Process A writes second part of its message

Обновление: Если вы используете FileHandler так, как вы описали в своем комментарии, все будет не так хорошо из-за сценария, который я описал выше: процессы A и B оба начнутся указывая на конец файла (из-за режима добавления), но после этого все может выйти из-за синхронизации, потому что (например, на многопроцессорном, но даже потенциально на однопроцессорном) один процесс может (выгрузить другой и) записать в общий файл обработать до того, как это завершит другой процесс.

...