Come configurare ed utilizzare i log

Django offre una configurazione predefinita di logging funzionante che può essere prontamente estesa.

Esegui una chiamata di base al sistema di log

Per inviare un messaggio di log dal tuo codice, inserisci una chiamata di logging.

Non utilizzare le chiamate al sistema di log all’interno di settings.py.

Il modo in cui il logging di Django è configurato come parte della funzione setup() significa che le chiamate di logging poste in settings.py potrebbero non funzionare come ci si aspetta, perchè il logging non sarà pronto a quel punto. Per esplorare il logging, usa una funzione di view come suggerito nell’esempio qui sotto.

Prima, importa la libreria di logging di Python e poi ottieni una istanza di logger con logging.getLogger(). Fornisci il metodo getLogger() con un nome che lo identifica ed i record che emette. Una buona opzione è usare __name__ (vedi Usa lo spazio dei nomi del logger qui sotto per saperne di più) che offrirà il nome del modulo Python corrente come percorso puntato:

import logging

logger = logging.getLogger(__name__)

E” una buona convenzione eseguire questa dichiarazione a livello di modulo.

E quindi in una funzione, per esempio in una vista, invia un record al sistema di logging:

def some_view(request):
    ...
    if some_risky_state:
        logger.warning("Platform is running at risk")

Quando questo codice viene eseguito, verrà inviato al logger un LogRecord contenente quel messaggio. Se stai usando la configurazione di logging predefinita di Django, il messaggio apparirà sulla console.

Il livello WARNING usato nell’esempio qui sopra è uno dei molti livelli di severità del logging: DEBUG, INFO, WARNING, ERROR, CRITICAL. Così, un altro esempio potrebbe essere:

logger.critical("Payment system is not responding")

Importante

I record con un livello più basso di WARNING non appariranno in console di default. Cambiare questo comportamento richiede configurazioni aggiuntive.

Personalizza la configurazione del sistema di log

Anche se la configurazione di logging di Django funziona da sè, puoi controllare esattamente come i tuoi log sono inviati a varie destinazioni - ai file di log, servizi esterni, email e così via - con qualche configurazione aggiuntiva.

Puoi configurare:

  • la mappatura del logger, per determinare quale record deve essere inviato a quale gestore
  • i gestori, per determinare cosa devono fare con i log che ricevono
  • i filtri, per fornire un controllo addizionale sul trasferimento dei record e eventualmente per effettuare delle modifiche al volo
  • formattatori, per convertire oggetti LogRecord a stringa o altre forme per il consumo da parte degli umani o di un altro sistema.

Ci sono diversi modi di configurare il logging. In Django, l’impostazione LOGGING viene usata più comunemente. L’impostazione usa il formato dictConfig ed estende la configurazione di logging di default.

Vedi Configuring logging per una spiegazione di come le tue impostazioni personalizzate vengono fuse con le impostazioni predefinite di Django.

Vedi la documentazione del logging di Python per avere dettagli su altri modi di configurare il logging. Per amor di semplicità, questa documentazione considererà solamente la configurazione tramite l’impostazione LOGGING.

Configurazione di base del sistema di log

Quando si configurano i log, ha senso fare

Creare un dizionario di LOGGING

Nel tuo settings.py:

LOGGING = {
    "version": 1,  # the dictConfig format version
    "disable_existing_loggers": False,  # retain the default loggers
}

Ha quasi sempre senso trattenere ed estendere la configurazione di logging predefinita impostando disable_existing_loggers a False.

Configura un gestore

Questo esempio configura un singolo handler chiamato file, che usa il FileHandler di Python per salvare i log con livello DEBUG e superiore sul file general.log (nella radice del progetto):

LOGGING = {
    # ...
    "handlers": {
        "file": {
            "class": "logging.FileHandler",
            "filename": "general.log",
        },
    },
}

Differenti classi handler accettano differenti opzioni di configurazione. Per avere più informazioni sulle classi handler disponibili, vedi AdminEmailHandler, fornita da Django e le varie various classi handler fornite da Python.

I livelli di log possono essere anche configurati sul gestore (di default, accettano log di tutti i livelli). Usando l’esempio precedente, aggiungendo:

{
    "class": "logging.FileHandler",
    "filename": "general.log",
    "level": "DEBUG",
}

definirebbe una configurazione di gestione che accetta solo record di livello DEBUG e superiore.

Configura la mappatura del sistema di log

Per inviare record a questo handler, configurare una mappatura del logger per usarlo ad esempio:

LOGGING = {
    # ...
    "loggers": {
        "": {
            "level": "DEBUG",
            "handlers": ["file"],
        },
    },
}

Il nome del mapping determina quali record di log elaborerà. Questa configurazione ('') è senza nome. Questo significa che elaborerà record da tutti i logger (vedi Usa lo spazio dei nomi del logger qua sotto per sapere come usare il nome di mapping per determinare i logger per i quali processerà i record).

Questo inoltrerà i messaggi di livello DEBUG e superiore all’handler chiamato file.

Nota che un logger può inoltrare messaggi a più gestori, così la relazione tra logger e gestori è molti-a-molti.

Se esegui:

logger.debug("Attempting to connect to API")

nel tuo codice, troverai quel messaggio nel file general.log nella radice del progetto.

Configurare un formatter

Di default, l’output finale del log contiene la parte di messaggio di ogni record di log. Usa un formattatore se vuoi includere dati aggiuntivi. Prima di tutto nomina e definisci i tuoi formattatori - questo esempio definisce formattatori di nome verbose e``simple``:

LOGGING = {
    # ...
    "formatters": {
        "verbose": {
            "format": "{name} {levelname} {asctime} {module} {process:d} {thread:d} {message}",
            "style": "{",
        },
        "simple": {
            "format": "{levelname} {message}",
            "style": "{",
        },
    },
}

La keyword style ti permette di specificare { per str.format() o $ per la formattazione string.Template; quello predefinito è ``$`.

Vedi LogRecord attributes per gli attributi di LogRecord che puoi includere.

Per applicare un formattatore ad un gestore, aggiungi una voce formatter al dizionario del gestore, che faccia riferimento al formattatore per nome, per esempio:

"handlers": {
    "file": {
        "class": "logging.FileHandler",
        "filename": "general.log",
        "formatter": "verbose",
    },
}

Usa lo spazio dei nomi del logger

La configurazione di logging senza nome '' cattura log da ogni applicazione Python. Una configurazione di logging con nome catturerà log solo dai logger con nomi che combaciano.

Il namespace di una istanza di un logger è definita usando getLogger(). Per esempio in views.py di my_app:

logger = logging.getLogger(__name__)

creerà un logger nel namespace my_app.views namespace. __name__ ti permette di organizzare i messaggi di log in accordo con la loro provenienza nelle applicazioni del tuo progetto in modo automatico. Assicura anche che non ci siano collisioni sui nomi.

Un mapping di logger chiamato my_app.views catturerà record da questo logger:

LOGGING = {
    # ...
    "loggers": {
        "my_app.views": {...},
    },
}

Un mapping di logger chiamato my_app sarà più permissivo, catturando record dai logger che si trovino ovunque nel namespace my_app (incluso my_app.views, my_app.utils e così via):

LOGGING = {
    # ...
    "loggers": {
        "my_app": {...},
    },
}

Puoi anche definire esplicitamente i namespacing del logger:

logger = logging.getLogger("project.payment")

e impostare le mappature del logger di conseguenza.

Utilizzo di gerarchie di logger e propagazione

La nomenclatura dei logger è gerarchica. my_app è genitore di my_app.views, che è genitore di my_app.views.private. Finchè non specificato altrimenti, i mapping dei logger propagheranno i record che elaborano ai loro genitori - un record da un logger nel namespace my_app.views.private sarà gestito da entrambi i mapping di my_app e my_app.views.

Per gestire questo comportamento, imposta la chiave di propagazione sulle mappature che definisci:

LOGGING = {
    # ...
    "loggers": {
        "my_app": {
            # ...
        },
        "my_app.views": {
            # ...
        },
        "my_app.views.private": {
            # ...
            "propagate": False,
        },
    },
}

propagate è di dafault a True. In questo esempio, i log da my_app.views.private non saranno gestiti dal genitore, ma i log da my_app.views lo saranno.

Configura logging reattivi

Il logging è soprattutto utile quando contiene più informazioni possibili ma non le informazioni di cui non hai bisogno - e di quanto hai bisogno dipende da quel che stai facendo. Quando stai facendo debug, hai bisogno di un livello di informazioni che sarebbe eccessivo ed inutile se fossi in produzione.

Puoi configurare il loggin per fornirti il livello di dettaglio di cui necessiti, quando ne hai bisogno. Piuttosto che cambiare manualmente la configurazione per fare questo, un modo migliore è applicare la configurazione automaticamente a seconda dell’environment.

Per esempio, potresti impostare una variabile d’ambiente appropriata DJANGO_LOG_LEVEL nei tuoi ambienti di sviluppo e staging ed usarla nel mapping del logger, quindi:

"level": os.getenv("DJANGO_LOG_LEVEL", "WARNING")

- così che, fintanto che l’ambiente non specifica un livello di log più basso, questa configurazione girerà solo i record di severità WARNING e superiore al proprio gestore.

Altre opzioni nella configurazione (come le opzioni del gestore level o formatter) possono essere gestite in modo simile.

Back to Top