2012-11-01 8 views
11

Ogni ricetta che ho trovato per la creazione di un processo demone in Python prevede due volte la forking (per Unix) e quindi la chiusura di tutti i descrittori di file aperti. (Vedere http://www.jejik.com/articles/2007/02/a_simple_unix_linux_daemon_in_python/ per un esempio).Mantenimento della registrazione e/o stdout/stderr in Python Daemon

Questo è tutto abbastanza semplice ma mi sembra di avere un problema. Sul sistema di produzione che sto configurando, il mio demone sta abortendo - silenziosamente dal momento che tutti i descrittori di file aperti sono stati chiusi. Sto avendo un momento difficile debuggare il problema al momento e mi chiedo quale sia il modo corretto per catturare e registrare questi errori.

Qual è il modo corretto di configurare la registrazione in modo che continui a funzionare dopo il daemonizzazione? Devo chiamare lo logging.basicConfig() una seconda volta dopo il demonizzazione? Qual è il modo giusto per acquisire stdout e stderr? Sono sfocato sui dettagli del perché tutti i file sono chiusi. Idealmente, il mio codice principale potrebbe semplicemente chiamare daemon_start(pid_file) e la registrazione continuerà a funzionare.

+1

Chiamare la configurazione di registrazione DOPO la demonizzazione è davvero la strada da percorrere. – Exelian

+1

Ho notato questo commento nei documenti di registrazione: "Questa funzione non fa nulla se il registratore di root ha già configurato gestori per esso." Se voglio registrare prima e dopo la demonizzazione, come influisce sulla situazione? –

+1

Se sono corretto, è possibile aggiungere gestori/filtri dopo l'inizializzazione del registratore. Ciò significa che è possibile aggiungere un FileHandler prima di avviare il contesto daemon e aggiungerne un altro dopo averlo avviato. Non sono del tutto sicuro che questo funzioni comunque. – Exelian

risposta

17

Uso la libreria python-daemon per il mio comportamento di demonizzazione.

Interfaccia descritto qui:

attuazione qui:

permette specificando unArgomento, per indicare qualsiasi descrittore di file che dovrebbe non essere chiuso durante il daemonizzazione.

Se è necessario la registrazione tramite gli stessi Handler casi prima e dopo daemonizing, è possibile:

  1. Prima impostare i gestori di registrazione utilizzando basicConfig o dictConfig o qualsiasi altra cosa.
  2. Elementi di registro
  3. Determinare quali descrittori di file dipendono dai valori Handler. Sfortunatamente questo dipende dalla sottoclasse Handler. Se il tuo primo installato Handler è un StreamHandler, è il valore di logging.root.handlers[0].stream.fileno(); se il tuo secondo installato Handler è un SyslogHandler, desideri il valore di logging.root.handlers[1].socket.fileno(); ecc Questo è :-(disordinato
  4. demonizzare il processo per la creazione di un DaemonContext con files_preserve pari ad un elenco dei descrittori di file è determinato nel passaggio 3.
  5. Continua la registrazione; i file di log non avrebbero dovuto essere chiuse durante il doppio forchetta.

un'alternativa potrebbe essere, come suggerito @Exelian, utilizzare effettivamente diverso Handler casi prima e dopo la daemonziation. Subito dopo daemonizing, distruggere i gestori esistenti (da del loro ing da logger.root.handlers?) e creane di nuovi identici, non puoi semplicemente richiamare lo basicConfig a causa di il problema che @ dave-mankoff ha sottolineato.

+0

Questa risposta è salva vita, grazie! – Drachenfels

7

È possibile semplificare il codice per questo se si impostano gli oggetti del gestore di registrazione separatamente dall'oggetto del logger principale e quindi si aggiungono gli oggetti del gestore come passaggio indipendente anziché eseguire tutto in una volta. Il seguente dovrebbe funzionare per te.

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.") 
+0

Ricordarsi di chiamare ['context.close()'] (https://pagure.io/python-daemon/blob/master/f/daemon/daemon.py#_400) o usare ['with'] (https: //docs.python.org/3/reference/compound_stmts.html#the-with-statement) dichiarazione. –

4

Abbiamo appena avuto un problema simile, e a causa di alcune cose al di là del mio controllo, la roba daemon era separato dalla roba creando il logger. Tuttavia, il logger ha attributi .handler e .parent che lo rendono possibile con qualcosa come:

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 
+0

Questa soluzione ha funzionato con il daemonizzazione di un comando di gestione personalizzato python. – phicou