Ведение журнала и / или stdout/stderr в Python Daemon

Каждый рецепт, который я нашел для создания процесса демона в Python, включает в себя двойное разветвление (для Unix) и затем закрытие всех дескрипторов открытых файлов. (См. http://www.jejik.com/articles/2007/02/a_simple_unix_linux_daemon_in_python/ для примера).

Это все достаточно просто, но у меня, похоже, есть проблема. На производственном компьютере, который я настраиваю, мой демон прерывается - молча, так как все дескрипторы открытых файлов были закрыты. В настоящее время мне сложно отладить проблему, и мне интересно, как правильно отследить и записать эти ошибки.

Как правильно настроить ведение журнала, чтобы оно продолжало работать после демонизации? Я просто позвоню logging.basicConfig() второй раз после демонизации? Как правильно запечатлеть stdout а также stderr? Я не совсем уверен, почему все файлы закрыты. В идеале мой основной код мог бы просто позвонить daemon_start(pid_file) и регистрация продолжит работать.

3 ответа

Я использую python-daemon библиотека для моего поведения демонизации.

Интерфейс описан здесь:

Реализация здесь:

Это позволяет указать files_preserve аргумент, чтобы указать любые файловые дескрипторы, которые не должны быть закрыты при демонизации.

Если вам нужно войти через тот же Handler экземпляры до и после демонизации вы можете:

  1. Сначала настройте свои обработчики журналов, используя basicConfig или же dictConfig или что угодно.
  2. Журнал вещи
  3. Определите, какие файловые дескрипторы ваши Handlerзависит от. К сожалению, это зависит от Handler подкласс. Если ваш первый установлен Handler это StreamHandlerэто ценность logging.root.handlers[0].stream.fileno(); если ваш второй установлен Handler это SyslogHandler, вы хотите значение logging.root.handlers[1].socket.fileno(); и т.д. Это грязно:-(
  4. Демонизировать ваш процесс, создав DaemonContext с files_preserve равно списку файловых дескрипторов, которые вы определили на шаге 3.
  5. Продолжить запись; Ваши файлы журналов не должны были быть закрыты во время двойного форка.

Альтернативой может быть, как предположил @Exelian, использование Handler случаи до и после демонизации. Сразу после демонизации уничтожить существующие обработчики delизвлекая их из logger.root.handlers?) и создавать идентичные новые; ты не можешь просто перезвонить basicConfig из-за проблемы, на которую указал @dave-mankoff.

Вы можете упростить код для этого, если вы настраиваете свои объекты-обработчики журналов отдельно от корневого объекта-регистратора, а затем добавляете объекты-обработчики как самостоятельный шаг, а не делаете все это одновременно. Следующее должно работать для вас.

import daemon
import logging

logger = logging.getLogger()
logger.setLevel(logging.DEBUG)
fh = logging.FileHandler("./foo.log")
logger.addHandler(fh)

context = daemon.DaemonContext(
   files_preserve = [
      fh.stream,
   ],
)

logger.debug( "Before daemonizing." )
context.open()
logger.debug( "After daemonizing." )

У нас просто была похожая проблема, и из-за некоторых вещей, не зависящих от меня, материал демона был отделен от материала, создающего регистратор. Однако, logger имеет атрибуты.handlers и.parent, которые делают это возможным с чем-то вроде:

    self.files_preserve = self.getLogFileHandles(self.data.logger)

def getLogFileHandles(self,logger):
    """ Get a list of filehandle numbers from logger
        to be handed to DaemonContext.files_preserve
    """
    handles = []
    for handler in logger.handlers:
        handles.append(handler.stream.fileno())
    if logger.parent:
        handles += self.getLogFileHandles(logger.parent)
    return handles
Другие вопросы по тегам