2012-02-09 12 views
13

Sto costruendo un'applicazione utilizzando gevent. La mia app sta diventando piuttosto grande ora che ci sono un sacco di posti di lavoro generati e distrutti. Ora ho notato che quando uno di questi processi si interrompe, la mia intera applicazione continua a funzionare (se l'eccezione proviene da una greenlet non principale), il che va bene. Ma il problema è che devo guardare la mia console per vedere l'errore. Quindi alcune parti della mia applicazione possono "morire" e io non ne sono consapevole all'istante e l'app continua a funzionare.Monitoraggio delle eccezioni gevent nei lavori

Il jittering della mia app con try catch non sembra essere una soluzione pulita. Forse una funzione di spawn personalizzata che segnala alcuni errori?

Qual è il modo corretto per monitorare i lavori gevent/greenlet? prendere le eccezioni?

Nel mio caso ascolto eventi di poche fonti diverse e dovrei occuparmi di ogni diverso. Ci sono 5 lavori come estremamente importanti. Greenlet webserver, greenlet websocket, verdetto del database , greenlet degli allarmi e greenlet zmq. Se qualcuno di quelli "muore" la mia applicazione dovrebbe morire completamente. Altri lavori che muoiono non sono così importanti. Ad esempio, è possibile che websocket greenlet muoia a causa di qualche eccezione sollevata e il resto delle applicazioni continua a funzionare correttamente come se nulla fosse successo. E 'completamente inutile e pericoloso ora e dovrebbe andare in crash.

+0

Ho reso i greenlet di Mission critical molto piccoli (8 linee di codice) e al loro turno generano greenlet per cui è "ok" andare in crash. – Stephan

risposta

12

penso che il modo più pulito sarebbe quello di intercettare l'eccezione si considera fatale e fare sys.exit() (avrete bisogno gevent 1.0 fin da prima che SystemExit non abbia abbandonato il processo).

Un altro modo è usare link_exception, che verrebbe chiamato se la greenlet fosse morta con un'eccezione.

spawn(important_greenlet).link_exception(lambda *args: sys.exit("important_greenlet died")) 

Nota: per il funzionamento è necessario anche gevent 1.0.

Se sul 0.13.6, fare qualcosa di simile per uccidere il processo:

gevent.get_hub().parent.throw(SystemExit()) 
+0

il tuo nuovo ufficio futuro btw è bello;) – Stephan

+0

On 0.13.6 Non ho 'gevent.get_hub()'. Ho 'gevent.getcurrent()', ma l'attributo genitore era 'None'. – scottm

+0

@scottm prova quindi gevent.hub.get_hub(). –

3

Si desidera greenlet.link_exception() tutti i tuoi greenlets a una alla funzione bidello.

La funzione bidello verrà passata a qualsiasi greenlet che muore, da cui può ispezionare il suo greenlet.exception per vedere cosa è successo, e se necessario fare qualcosa al riguardo.

+0

Molto più ragionevole che uccidere l'intero processo. –

0

Il problema principale con greenlet.link_exception() è che non fornisce alcuna informazione sul traceback che può essere davvero importante per il log.

Per la registrazione con traceback, io uso un decoratore di spwan lavori che chiamata di lavoro indiretti in una semplice funzione di registrazione:

from functools import wraps  

import gevent 

def async(wrapped): 

    def log_exc(func): 

     @wraps(wrapped) 
     def wrapper(*args, **kwargs): 
      try: 
       func(*args, **kwargs) 
      except Exception: 
       log.exception('%s', func) 
     return wrapper 

    @wraps(wrapped) 
    def wrapper(*args, **kwargs): 
     greenlet = gevent.spawn(log_exc(wrapped), *args, **kwargs) 

    return wrapper 

Naturalmente, è possibile aggiungere la chiamata link_exception per gestire i lavori (che non ho necessario)

2

Come @Denis e @lvo ha detto, link_exception è OK, ma penso che ci sarebbe un modo migliore per farlo, senza cambiare il codice corrente per generare il verdetto.

Generalmente, ogni volta che un'eccezione viene lanciata in un greenlet, il metodo _report_error (in gevent.greenlet.Greenlet) verrà chiamato per quel verdetto. Farà alcune cose come chiamare tutte le funzioni di collegamento e, infine, chiamare self.parent.handle_error con exc_info dallo stack corrente.Il self.parent qui è l'oggetto globale Hub, questo significa che tutte le eccezioni avvenute in ogni greenlet saranno sempre centralizzate su un metodo per la gestione. Per impostazione predefinita, Hub.handle_error distingue il tipo di eccezione, ignora alcuni tipi e stampa gli altri (che è ciò che abbiamo sempre visto nella console).

Con il patch del metodo Hub.handle_error, possiamo facilmente registrare i nostri gestori di errori e non perdere più un errore. Ho scritto una funzione di supporto per realizzarlo:

from gevent.hub import Hub 


IGNORE_ERROR = Hub.SYSTEM_ERROR + Hub.NOT_ERROR 


def register_error_handler(error_handler): 

    Hub._origin_handle_error = Hub.handle_error 

    def custom_handle_error(self, context, type, value, tb): 
     if not issubclass(type, IGNORE_ERROR): 
      # print 'Got error from greenlet:', context, type, value, tb 
      error_handler(context, (type, value, tb)) 

     self._origin_handle_error(context, type, value, tb) 

    Hub.handle_error = custom_handle_error 

Per usarlo, basta chiamare prima il ciclo degli eventi viene inizializzato:

def gevent_error_handler(context, exc_info): 
    """Here goes your custom error handling logics""" 
    e = exc_info[1] 
    if isinstance(e, SomeError): 
     # do some notify things 
     pass 
    sentry_client.captureException(exc_info=exc_info) 

register_error_handler(gevent_error_handler) 

Questa soluzione è stata testata sotto gevent 1.0.2 e 1.1 b3, lo usiamo per inviare informazioni sugli errori di greenlet a sentry (un sistema di tracciamento delle eccezioni), funziona abbastanza bene finora.