Logger已配置时,Daemon线程日志记录出现文件描述符错误
问题分析与解决方案
这个错误的核心原因是daemon包的DaemonContext默认会关闭所有非标准输入输出的文件描述符,而主程序通过logging.basicConfig打开的日志文件句柄会被这个操作关闭。当主程序后续尝试写入日志时,就会触发OSError: [Errno 9] Bad file descriptor错误——因为对应的文件已经被daemon的初始化流程关掉了。
下面是两种可行的解决方案:
方案1:让DaemonContext保留主程序的日志文件描述符
你可以在创建DaemonContext时,通过files_preserve参数指定需要保留的文件描述符,这样主程序的日志文件句柄就不会被关闭。
步骤如下:
- 先获取主程序日志文件的文件描述符
- 将它和守护进程的日志文件描述符一起加入
files_preserve列表
示例代码:
import logging import daemon from daemon import pidfile # 主程序配置日志 logging.basicConfig(level='DEBUG', filename="/some/directory/cstash.log") # 获取主程序日志文件的文件描述符 main_log_fd = None for handler in logging.root.handlers: if isinstance(handler, logging.FileHandler): main_log_fd = handler.stream.fileno() break # 初始化守护进程上下文 with daemon.DaemonContext( pidfile=pidfile.PIDLockFile(self.pid_file), stderr=self.log_file, stdout=self.log_file, # 保留主程序和守护进程的日志文件描述符 files_preserve=[main_log_fd, self.log_file.fileno()] if main_log_fd else [self.log_file.fileno()] ): self.watch_files()
方案2:在守护进程内部重新配置日志
既然daemon会关闭原来的文件句柄,那我们可以在守护进程启动后,重新初始化日志系统,让守护进程使用独立的日志配置,和主程序完全隔离。
示例代码(在watch_files方法内):
import logging def watch_files(self): # 清空主程序遗留的日志handler,避免无效句柄 root_logger = logging.getLogger() root_logger.handlers = [] # 配置守护进程自己的日志 daemon_handler = logging.FileHandler("/path/to/daemon-specific.log") formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s') daemon_handler.setFormatter(formatter) root_logger.addHandler(daemon_handler) root_logger.setLevel(logging.DEBUG) # 后续的日志操作就会写入守护进程的独立文件 logging.debug("Daemon started successfully") # ... 你的守护进程逻辑
两种方案对比:
- 方案1适合需要主程序和守护进程共享部分日志上下文的场景,但需要确保文件描述符正确获取
- 方案2更彻底,两者日志完全独立,避免了文件描述符冲突的问题,推荐用于日志分离的场景
内容的提问来源于stack exchange,提问作者Afraz
相关产品推荐
相关产品推荐

