Ведение журнала и / или 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
экземпляры до и после демонизации вы можете:
- Сначала настройте свои обработчики журналов, используя
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.
Вы можете упростить код для этого, если вы настраиваете свои объекты-обработчики журналов отдельно от корневого объекта-регистратора, а затем добавляете объекты-обработчики как самостоятельный шаг, а не делаете все это одновременно. Следующее должно работать для вас.
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