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

Каждый рецепт, который я нашел для создания процесса 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), и ведение журнала продолжало бы работать.

Ответ 1

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

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

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

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

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

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

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

Ответ 2

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

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." )

Ответ 3

У нас просто была аналогичная проблема, и из-за каких-то вещей, находящихся вне моего контроля, материал демона был отделен от материала, создающего регистратор. Тем не менее, 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