我发现在Python中创建守护进程的每个配方都涉及两次(对于Unix),然后关闭所有打开的文件描述符。 (有关示例,请参阅http://www.jejik.com/articles/2007/02/a_simple_unix_linux_daemon_in_python/。)
这很简单,但我似乎有问题。在我正在设置的生产机器上,我的守护程序正在中止 - 因为所有打开的文件描述符都已关闭,所以它是静默的。我目前正在调试这个问题,并且想知道捕获和记录这些错误的正确方法是什么。
设置日志记录的正确方法是什么,以便在守护进程后继续工作?我是否只是在守护之后第二次打电话给logging.basicConfig()
?捕获stdout
和stderr
的正确方法是什么?我对所有文件关闭的细节很模糊。理想情况下,我的主要代码只能调用daemon_start(pid_file)
并且日志记录将继续有效。
答案 0 :(得分:18)
我使用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
}并创建相同的新处理程序;由于@ dave-mankoff指出的问题,你不能只是重新调用basicConfig
。
答案 1 :(得分:8)
如果将日志记录处理程序对象与根记录程序对象分开设置,则可以简化此代码,然后将处理程序对象作为独立步骤添加,而不是一次完成所有操作。以下内容适合您。
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." )
答案 2 :(得分:5)
我们遇到了类似的问题,并且由于一些我无法控制的事情,守护进程的东西与创建记录器的东西是分开的。但是,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