Pattern di debugging condizionale migliore?

6

Data la necessità di accedere solo in modalità di debug, il modo più semplice sarebbe utilizzare le condizioni:

def test(x, debug=False):
    if debug:
        print(x)

    # ...Some more code

    if debug:
        print("Something else")

    # ...More conditional logging

    return x * 2

Questo porta al disordine della logica della funzione reale. Il meglio che potevo pensare per migliorare questo era nasconderlo dietro una funzione per renderlo meno ingombrante:

def _log_if_debug(info, debug):
    if debug:
        print(info)

def test(x, debug=False):
    _log_if_debug(x, debug)

    # ...Some more code

    _log_if_debug("Something else", debug)

    # ...More conditional logging
    return x * 2

C'è un modello migliore per farlo?

    
posta Veneet Reddy 25.05.2018 - 16:29
fonte

3 risposte

8

Utilizza la libreria di registrazione standard

In Python esiste un solo pattern appropriato: usa modulo di registrazione standard secondo HowTo ufficiale . Oppure, per Python 3, questo modulo di registrazione standard e questo HowTo . Non ci sono differenze importanti tra queste versioni¹.

Se hai motivi per non gradire la libreria standard, puoi in alternativa utilizzare una delle sostituzioni esistenti . Hanno una configurazione diversa e alcune estensioni, ma l'utilizzo di base è simile o identico. Li raggiungerò più tardi. Dovrebbe essere relativamente facile passare a loro in qualsiasi momento, quindi logging è almeno un buon punto di partenza.

Utilizzo nel modulo

Il tuo codice è simile a questo:

import logging

logger = logging.getLogger(__name__)  # you can use other name

logger.info('Loading my cool module with cool logging')

    def coolFunction(x):
        logger.debug('Most of the time this message is really irrelevant')
        ...
        if something_suspicious:
            logger.warning('Something suspicious happened: nothing broken yet, but you probably want to know')
        ...
        if everything_is_wrong:
            logger.error('Everything is broken and this program just went nuts')

O in alternativa, puoi usare più semplice:

logging.info(...)
logging.debug(...)

invece di creare un logger, ma sembra abbastanza semplice aggiungere questa singola linea ovunque.

Se non sei sicuro di quali livelli di registrazione utilizzare per diversi messaggi, puoi fare riferimento a questa domanda SO con alcune risposte decenti.

Configurazione

Poi da qualche parte nel tuo cofig dici:

logging.basicConfig(filename='example.log', level=logging.INFO)

o

logging.basicConfig(filename='example.log', level=logging.DEBUG)

per impostare l'output e filtrare gli errori in modo appropriato. Se vuoi usare stderr, dì

logging.basicConfig(stream=sys.stderr, level=logging.DEBUG)

, invece.

Gestori alternativi, accesso a servizi esterni

C'è anche un numero di librerie che puoi, ma al posto del logger di base, come raven per Sentinella . Non è necessario modificare nulla nel codice per accedere a tale servizio, basta modificare la configurazione.

Librerie alternative

Ci sono alcune librerie che potresti voler usare per sostituire completamente logging . Seguono un modello simile, configurazione di registrazione centrale, quindi accedono a un logger in ogni file e registrano utilizzando i livelli di registro. Il diario di bordo sembra essere un rimpiazzo per la registrazione, con alcune caratteristiche interessanti aggiunte. Alcuni altri sono menzionati in questa domanda SO .

Rimozione della registrazione dalla build ottimizzata

Per i casi estremi in cui la registrazione di debug potrebbe influire significativamente sulle prestazioni o sui dati sensibili alle perdite nell'ambiente di produzione, integrato La costante di __debug__ può essere utilizzata:

if __debug__:
    debug_info = some_complex_computation()
    logger.debug()

In questo caso, l'intera istruzione verrà cancellata durante la compilazione del codice con l'opzione -o . Tuttavia, questa soluzione è brutta e un po 'fragile, dovrebbe essere usata con cura e solo dove realmente necessario. Per la maggior parte delle applicazioni significa "da nessuna parte, mai".

Altre lingue

Per altri linguaggi, può sembrare identico (ad es. in Java) o leggermente diverso, e può variare leggermente (non tutte le lingue hanno un modo standard). Ma in generale, troverete schemi molto simili, cose del tipo

LOG(DEBUG, "Your message")

o

LOG_ERROR("Error message")

o

LOG(INFO) << "Some people like C++ streams' syntax"

¹ AFAIK l'unica differenza che vale la pena di sapere è che Python 3 ha un logger "last resort" che accede a stderror, quindi se ometti completamente la configurazione, ottieni ancora qualcosa . In Python 2 viene visualizzato solo un avviso relativo alla registrazione errata.

    
risposta data 26.05.2018 - 11:05
fonte
3

Certo, chiama il tuo metodo solo log() .

Sembra sciocco, ma è leggibile il doppio di avere un nome di metodo breve cosparso nel codice di uno lungo.

Il prossimo livello di leggibilità sarebbe quello di separare completamente le preoccupazioni trasversali dalla logica aziendale e di intrecciarle con una sorta di strumento orientato all'aspetto. Ma questo è uno sforzo enormemente più grande (ovviamente, per risultati molto migliori).

    
risposta data 25.05.2018 - 16:33
fonte
3

Hai sentito parlare di chiusure?

log = buildLog(debug)

...

log("Something ya wanna log")

Ora puoi controllare la registrazione globalmente o puoi controllarla localmente passando solo la funzione di registro abilitata alle poche cose che ne hanno bisogno in questo momento. Questo piccolo schema si combina bene con gli argomenti predefiniti.

    
risposta data 25.05.2018 - 18:59
fonte

Leggi altre domande sui tag