Python守护进程日志仅在__init__生效,如何修复并复用Logger?
Got it, let's break down why your logs only work in the parent Daemon class's __init__ method but not in the child class's run method, and how to fix this while reusing your logger.
The Root Cause
Most daemon implementations perform two forks and then close all open file descriptors to detach from the parent process and terminal. If you set up your logging handler in the parent class's __init__ which runs before daemonization, the file descriptor for your application.log gets closed during the daemon's cleanup process. That's why your run method which executes after daemonization can't write to the log anymore—its handler is pointing to a closed file descriptor.
Step-by-Step Solution
We'll adjust when we set up the logger to ensure it happens after the daemon has finished cleaning up file descriptors, and we'll make sure the logger is reusable across the parent and child classes.
1. Modify the Parent Daemon Class
Move the logging setup out of __init__ into a dedicated method, and call this method after the daemon is fully initialized post-fork and file descriptor cleanup:
import os import sys import logging import resource import time class Daemon: def __init__(self, pidfile, logfile='application.log'): self.pidfile = pidfile self.logfile = logfile # Don't set up logging here—we'll do it later self.logger = None def setup_logging(self): """Initialize logger once after daemonization""" self.logger = logging.getLogger('daemon_logger') self.logger.setLevel(logging.INFO) # Prevent duplicate handlers if setup is called multiple times if not self.logger.handlers: formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s') # Create file handler AFTER daemonization so its FD isn't closed file_handler = logging.FileHandler(self.logfile) file_handler.setFormatter(formatter) self.logger.addHandler(file_handler) def start(self): # First fork: detach from parent process try: pid = os.fork() if pid > 0: sys.exit(0) except OSError as e: sys.stderr.write(f"First fork failed: {e}\n") sys.exit(1) # Set new session and umask os.setsid() os.umask(0) # Second fork: ensure daemon can't reattach to terminal try: pid = os.fork() if pid > 0: sys.exit(0) except OSError as e: sys.stderr.write(f"Second fork failed: {e}\n") sys.exit(1) # Close all open file descriptors for fd in range(resource.getrlimit(resource.RLIMIT_NOFILE)[0]): try: os.close(fd) except OSError: pass # Redirect stdin/stdout/stderr to /dev/null sys.stdin = open('/dev/null', 'r') sys.stdout = open('/dev/null', 'w') sys.stderr = open('/dev/null', 'w') # NOW set up logging after FD cleanup self.setup_logging() # Call the child class's run method self.run() # Add standard daemon methods like stop/restart as needed def stop(self): # Your stop logic here pass def restart(self): self.stop() self.start()
2. Update the Child MyDaemon Class
Now you can directly use the pre-configured logger in your run method without redefining anything:
class MyDaemon(Daemon): def run(self): # Use the logger from the parent class self.logger.info("MyDaemon run method started successfully!") # Your daemon logic here while True: self.logger.debug("Daemon is running...") # Example work task time.sleep(5)
3. Verify Real-Time Logging
Once your daemon is running, you can use tailf or tail -f to watch the logs in real time:
tailf application.log
Key Notes
- No Duplicate Handlers: The check
if not self.logger.handlers:ensures we don't add multiple file handlers ifsetup_loggingis called more than once e.g., during restarts. - File Permissions: Make sure the user running the daemon has write permissions for
application.loge.g., if it's in/var/log, you might need to adjust ownership or permissions. - Avoid Print Statements: Since we redirect stdout/stderr to
/dev/null, anyprintcalls in your daemon won't show up—always useself.loggerinstead.
内容的提问来源于stack exchange,提问作者Yener

