Logging

Los programadores de Python a menudo usan print() en su código como una herramienta de depuración rápida y conveniente. Usar el marco de registro es solo un poco más de esfuerzo que eso, pero es mucho más elegante y flexible. Además de ser útil para la depuración, el registro también puede brindarle más información, y mejor estructurada, sobre el estado y la salud de su aplicación.

Información general

Django usa y extiende el módulo logging incorporado de Python para realizar el registro del sistema. Este módulo se analiza en detalle en la propia documentación de Python; esta sección proporciona una descripción general rápida.

Rol de los recursos

Una configuración de registro de Python consta de cuatro partes:

Loggers

Un logger es el punto de entrada al sistema de registro. Cada logger es un depósito con nombre en el que se pueden escribir mensajes para su procesamiento.

Un logger está configurado para tener un nivel de registro. Este nivel de registro describe la gravedad de los mensajes que manejará el logger. Python define los siguientes niveles de registro:

  • DEBUG: Información del sistema de bajo nivel con fines de depuración
  • INFO: Información general del sistema
  • WARNING: Información que describe un problema menor que ha ocurrido.
  • ERROR: Información que describe un problema mayor que ha ocurrido.
  • CRÍTICO: Información que describe un problema crítico que ha ocurrido.

Cada mensaje que se escribe en el logger es un Registro del logger. Cada registro también tiene un nivel de registro que indica la gravedad de ese mensaje específico. Un registro de logger también puede contener metadatos útiles que describen el evento que se está registrando. Esto puede incluir detalles como un seguimiento de la pila o un código de error.

Cuando se envía un mensaje al logger, el nivel de registro del mensaje se compara con el nivel de registro del logger. Si el nivel de registro del mensaje alcanza o supera el nivel de registro del propio logger, el mensaje se someterá a un procesamiento adicional. Si no es así, el mensaje será ignorado.

Una vez que un logger ha determinado que un mensaje debe procesarse, se pasa a un Handler.

Handlers

El handler es el motor que determina qué sucede con cada mensaje en un logger. Describe un comportamiento de registro particular, como escribir un mensaje en la pantalla, en un archivo o en un socket de red.

Al igual que los loggers, los handlers también tienen un nivel de registro. Si el nivel de registro de una entrada de registro no alcanza o excede el nivel del controlador, el controlador ignorará el mensaje.

Un logger puede tener varios handlers y cada handlers puede tener un nivel de registro diferente. De esta forma, es posible proporcionar diferentes formas de notificación dependiendo de la importancia de un mensaje. Por ejemplo, podría instalar un controlador que reenvíe los mensajes ERROR y CRITICAL a un servicio de paginación, mientras que un segundo controlador registra todos los mensajes (incluidos los mensajes ERROR y CRITICAL) a un archivo para su posterior análisis.

Filtros

Se utiliza un filtro para proporcionar un control adicional sobre qué entradas de registro se pasan del registrador al controlador.

De manera predeterminada, se manejará cualquier mensaje de registro que cumpla con los requisitos de nivel de registro. Sin embargo, al instalar un filtro, puede colocar criterios adicionales en el proceso de registro. Por ejemplo, podrías instalar un filtro que solo permita que se emitan mensajes ERROR de una fuente en particular.

Los filtros también se pueden usar para modificar el registro de registro antes de que se emita. Por ejemplo, podría escribir un filtro que rebaje los registros de registro ERROR a registros WARNING si se cumple un conjunto particular de criterios.

Los filtros se pueden instalar en loggers o en handlers; se pueden usar múltiples filtros en una cadena para realizar múltiples acciones de filtrado.

Formateadores

En última instancia, un registro debe representarse como texto. Los Formatters describen el formato exacto de ese texto. Un formateador generalmente consta de una cadena de formato de Python que contiene atributos LogRecord;

Implicaciones de seguridad

El sistema de registro maneja información potencialmente confidencial. Por ejemplo, el registro puede contener información sobre una solicitud web o un seguimiento de pila, mientras que algunos de los datos que recopila en sus propios loggers también pueden tener implicaciones de seguridad. Debes asegurarte de saber:

  • qué información se recopila
  • donde se almacenará posteriormente
  • cómo se transferirá
  • quien pudiera tener acceso a ella.

Para ayudar a controlar la recopilación de información confidencial, puede designar explícitamente cierta información confidencial para que se filtre de los informes de errores. Obtenga más información sobre cómo filtrar informes de errores.

AdminEmailHandler

La AdminEmailHandler integrada merece una mención en el contexto de la seguridad. Si su opción include_html está habilitada, el mensaje de correo electrónico que envía contendrá un rastreo completo, con nombres y valores de variables locales en cada nivel de la pila, además de los valores de su configuración de Django (en otras palabras, el mismo nivel de detalle que se expone en una página web cuando DEBUG es True).

Por lo general, no se considera una buena idea enviar información potencialmente confidencial por correo electrónico. En su lugar, considere utilizar uno de los muchos servicios de terceros a los que se pueden enviar registros detallados para obtener lo mejor de múltiples mundos: la rica información de rastreos completos, una gestión clara de quién recibe notificaciones y tiene acceso a la información, etc. .

Configuración de registro

La biblioteca de registro de Python proporciona varias técnicas para configurar el registro, que van desde una interfaz programática hasta archivos de configuración. Por defecto, Django usa el formato dictConfig.

Para configurar el registro, utilice LOGGING para definir un diccionario de configuración de registro. Esta configuración describe los loggers, handlers, filtros y formateadores que desea en su configuración de registro, y los niveles de registro y otras propiedades que desea que tengan esos componentes.

De forma predeterminada, la configuración de LOGGING se fusiona con La configuración de registro predeterminada de Django utilizando el siguiente esquema.

Si la llave disable_existing_loggers en LOGGING dictConfig se establece como True (que es el valor predeterminado de dictConfig si falta la llave), entonces todos los loggers de la configuración predeterminada serán desactivados. Los loggers deshabilitados no son lo mismo que los eliminados; el logger seguirá existiendo, pero descartará silenciosamente todo lo registrado en él, ni siquiera propagará las entradas a un logger principal. Por lo tanto, debe tener mucho cuidado al usar 'disable_existing_loggers': True; probablemente no sea lo que quieres. En su lugar, puede establecer disable_existing_loggers como False y redefinir algunos o todos los loggers predeterminados; o puede establecer LOGGING_CONFIG como None y manejar la configuración de registro usted mismo.

El registro se configura como parte de la función setup() general de Django. Por lo tanto, puede estar seguro de que los loggers siempre estarán listos para usar en el código de su proyecto.

Ejemplos

La documentación completa para dictConfig format es la mejor fuente de información sobre el registro de diccionarios de configuración. Sin embargo, para darle una idea de lo que es posible, aquí hay varios ejemplos.

Para comenzar, aquí hay una pequeña configuración que le permitirá enviar todos los mensajes de registro a la consola:

settings.py
import os

LOGGING = {
    "version": 1,
    "disable_existing_loggers": False,
    "handlers": {
        "console": {
            "class": "logging.StreamHandler",
        },
    },
    "root": {
        "handlers": ["console"],
        "level": "WARNING",
    },
}

Esto configura el logger principal root para enviar mensajes con el nivel WARNING y superior al controlador de la consola. Ajustando el nivel a INFO o DEBUG puedes mostrar más mensajes. Esto puede ser útil durante el desarrollo.

A continuación, podemos agregar un registro más detallado. Aquí hay un ejemplo de cómo hacer que el sistema de registro imprima más mensajes solo del registrador llamado django:

settings.py
import os

LOGGING = {
    "version": 1,
    "disable_existing_loggers": False,
    "handlers": {
        "console": {
            "class": "logging.StreamHandler",
        },
    },
    "root": {
        "handlers": ["console"],
        "level": "WARNING",
    },
    "loggers": {
        "django": {
            "handlers": ["console"],
            "level": os.getenv("DJANGO_LOG_LEVEL", "INFO"),
            "propagate": False,
        },
    },
}

Por defecto, esta configuración envía mensajes desde el logger django de nivel INFO o superior a la consola. Este es el mismo nivel que la configuración de registro predeterminada de Django, excepto que la configuración predeterminada solo muestra los registros cuando DEBUG=True. Django no registra muchos de estos mensajes de nivel INFO. Sin embargo, con esta configuración, también puede configurar la variable de entorno DJANGO_LOG_LEVEL=DEBUG para ver todo el registro de depuración de Django, que es muy detallado ya que incluye todas las consultas de la base de datos.

No tienes que iniciar sesión en la consola. Aquí hay una configuración que escribe todos los registros del registrador llamado django en un archivo local:

settings.py
LOGGING = {
    "version": 1,
    "disable_existing_loggers": False,
    "handlers": {
        "file": {
            "level": "DEBUG",
            "class": "logging.FileHandler",
            "filename": "/path/to/django/debug.log",
        },
    },
    "loggers": {
        "django": {
            "handlers": ["file"],
            "level": "DEBUG",
            "propagate": True,
        },
    },
}

Si usa este ejemplo, asegúrese de cambiar la ruta 'filename a una ubicación en la que pueda escribir el usuario que está ejecutando la aplicación Django.

Finalmente, aquí hay un ejemplo de una configuración de registro bastante compleja:

settings.py
LOGGING = {
    "version": 1,
    "disable_existing_loggers": False,
    "formatters": {
        "verbose": {
            "format": "{levelname} {asctime} {module} {process:d} {thread:d} {message}",
            "style": "{",
        },
        "simple": {
            "format": "{levelname} {message}",
            "style": "{",
        },
    },
    "filters": {
        "special": {
            "()": "project.logging.SpecialFilter",
            "foo": "bar",
        },
        "require_debug_true": {
            "()": "django.utils.log.RequireDebugTrue",
        },
    },
    "handlers": {
        "console": {
            "level": "INFO",
            "filters": ["require_debug_true"],
            "class": "logging.StreamHandler",
            "formatter": "simple",
        },
        "mail_admins": {
            "level": "ERROR",
            "class": "django.utils.log.AdminEmailHandler",
            "filters": ["special"],
        },
    },
    "loggers": {
        "django": {
            "handlers": ["console"],
            "propagate": True,
        },
        "django.request": {
            "handlers": ["mail_admins"],
            "level": "ERROR",
            "propagate": False,
        },
        "myproject.custom": {
            "handlers": ["console", "mail_admins"],
            "level": "INFO",
            "filters": ["special"],
        },
    },
}

Esta configuración de registro hace lo siguiente:

  • Identifica la configuración como si estuviera en el formato “dictConfig versión 1”. Actualmente, esta es la única versión en formato dictConfig.

  • Define dos formateadores:

    • simple, que genera el nombre del nivel de registro (por ejemplo, DEBUG) y el mensaje de registro.

      La cadena format es una cadena de formato normal de Python que describe los detalles que se generarán en cada línea de registro. La lista completa de detalles que se pueden generar se puede encontrar en Formatter Objects.

    • verbose, que genera el nombre del nivel de registro, el mensaje de registro, además de la hora, el proceso, el subproceso y el módulo que generan el mensaje de registro.

  • Define dos filtros:

    • project.logging.SpecialFilter, usando el alias special. Si este filtro requiere argumentos adicionales, se pueden proporcionar como claves adicionales en el diccionario de configuración del filtro. En este caso, al argumento foo se le dará un valor de bar al instanciar SpecialFilter.
    • django.utils.log.RequireDebugTrue, que transmite registros cuando DEBUG es True.
  • Define dos handlers:

    • console, una StreamHandler, que imprime cualquier mensaje INFO (o superior) en sys.stderr. Este handler usa el formato de salida simple.
    • mail_admins, una AdminEmailHandler, que envía por correo electrónico cualquier mensaje ERROR (o superior) al sitio ADMINS. Este handler usa el filtro especial.
  • Configura tres loggers:

    • django, que pasa todos los mensajes al handler console.
    • django.request, que pasa todos los mensajes ERROR al handler mail_admins. Además, este logger está marcado para no propagar mensajes. Esto significa que los mensajes de registro escritos en django.request no serán manejados por el logger django.
    • myproject.custom, que pasa todos los mensajes en INFO o superior que también pasan el filtro especial a dos handlers: console y mail_admins. Esto significa que todos los mensajes de nivel INFO (o superior) se imprimirán en la consola; Los mensajes ERROR y CRITICAL también se enviarán por correo electrónico.

Configuración de registro personalizada

Si no desea utilizar el formato dictConfig de Python para configurar su logger, puede especificar su propio esquema de configuración.

La configuración LOGGING_CONFIG define el invocable que se usará para configurar los loggers de Django. Por defecto, apunta a la función logging.config.dictConfig() de Python. Sin embargo, si desea usar un proceso de configuración diferente, puede usar cualquier otro invocable que tome un solo argumento. El contenido de LOGGING se proporcionará como el valor de ese argumento cuando se configure el registro.

Deshabilitar la configuración de registro

Si no desea configurar el registro en absoluto (o desea configurar manualmente el registro utilizando su propio enfoque), puede establecer LOGGING_CONFIG como None. Esto deshabilitará el proceso de configuración para el registro predeterminado de Django.

Establecer LOGGING_CONFIG como None solo significa que el proceso de configuración automática está deshabilitado, no el registro en sí. Si deshabilita el proceso de configuración, Django aún realizará llamadas de registro, recurriendo al comportamiento de registro predeterminado que esté definido.

Aquí hay un ejemplo que deshabilita la configuración de registro de Django y luego configura manualmente el registro:

settings.py
LOGGING_CONFIG = None

import logging.config

logging.config.dictConfig(...)

Tenga en cuenta que el proceso de configuración predeterminado solo llama a LOGGING_CONFIG una vez que la configuración está completamente cargada. Por el contrario, configurar manualmente el inicio de sesión en su archivo de configuración cargará su configuración de inicio de sesión inmediatamente. Como tal, su configuración de registro debe aparecer después de cualquier configuración de la que dependa.

Back to Top