Ведение журнала и/или 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