it-swarm.com.de

django.request logger nicht an root weitergegeben?

Mit Django 1.5.1:

DEBUG = False

LOGGING = {
    'version': 1,
    'disable_existing_loggers': True,
    'formatters': {
        'verbose': {
            'format': '%(levelname)s %(asctime)s %(module)s %(message)s'
        },
    },
    'handlers': {
        'console': {
            'level': 'DEBUG',
            'class': 'logging.StreamHandler',
            'formatter': 'verbose',
        },
    },
    'loggers': {
        # root logger
        '': {
            'handlers': ['console'],
        },
        #'Django.request': {
        #    'handlers': ['console'],
        #    'level': 'DEBUG',
        #    'propagate': False,
        #},
    }
}

Wenn ich die kommentierten Zeilen auskommentiere und eine Ansicht mit 1/0 aufrufe, wird der Traceback auf der Konsole ausgegeben:

ERROR 2013-11-29 13:33:23,102 base Internal Server Error: /comment/*******/
Traceback (most recent call last):
  ...
  File "*****/comments/views.py", line 10, in post
    1/0
ZeroDivisionError: integer division or modulo by zero
WARNING 2013-11-29 13:33:23,103 csrf Forbidden (CSRF cookie not set.): /comment/******/
[29/Nov/2013 13:33:23] "POST /comment/******/ HTTP/1.0" 500 27

Wenn die Zeilen jedoch kommentiert bleiben, wird kein Traceback auf der Konsole ausgegeben.

[29/Nov/2013 13:33:23] "POST /comment/******/ HTTP/1.0" 500 27

Ich dachte, wenn Django.request logger nicht konfiguriert ist, würde es an den Root-Logger weitergegeben, der alles an die Konsole druckt.

Ich habe keine Information gefunden, dass Django.request etwas Besonderes ist.

Warum funktioniert es nicht?

Hier Ich las:

Vor Django 1.5 hat die Einstellung LOGGING immer die Standardkonfiguration für die Django-Protokollierung überschrieben. Von Django 1.5 nach vorne ist es möglich, die Projektprotokollkonfiguration fusionierte mit Django Standardwerte zu bekommen, daher können Sie entscheiden, ob Sie hinzufügen möchten, oder die bestehende Konfiguration zu ersetzen.

Wenn der Schlüssel disable_existing_loggers in der LOGGING dictConfig auf True gesetzt ist (dies ist die Standardeinstellung), wird die Standardkonfiguration vollständig überschrieben. Alternativ können Sie einige oder alle Logger neu definieren, indem Sie disable_existing_loggers auf False setzen.

In Django/utils/log.py:

# Default logging for Django. This sends an email to the site admins on every
# HTTP 500 error. Depending on DEBUG, all other log records are either sent to
# the console (DEBUG=True) or discarded by mean of the NullHandler (DEBUG=False).
DEFAULT_LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'filters': {
        'require_debug_false': {
            '()': 'Django.utils.log.RequireDebugFalse',
        },
        'require_debug_true': {
            '()': 'Django.utils.log.RequireDebugTrue',
        },
    },
    'handlers': {
        'console':{
            'level': 'INFO',
            'filters': ['require_debug_true'],
            'class': 'logging.StreamHandler',
        },
        'null': {
            'class': 'Django.utils.log.NullHandler',
        },
        'mail_admins': {
            'level': 'ERROR',
            'filters': ['require_debug_false'],
            'class': 'Django.utils.log.AdminEmailHandler'
        }
    },
    'loggers': {
        'Django': {
            'handlers': ['console'],
        },
        'Django.request': {
            'handlers': ['mail_admins'],
            'level': 'ERROR',
            'propagate': False,
        },
        'py.warnings': {
            'handlers': ['console'],
        },
    }
}

Daher hat Django.request standardmäßig propagate = False. Aber in meinem Fall habe ich 'disable_existing_loggers': True.

31
warvariuc

Die Lösung besteht darin, zu verhindern, dass Django die Protokollierung konfiguriert und selbst damit umgeht. Zum Glück ist das einfach. In settings.py:

LOGGING_CONFIG = None
LOGGING = {...}  # whatever you want, as you already have

import logging.config
logging.config.dictConfig(LOGGING)

UPDATE ~ März 2015 : Django hat geklärt ihre Dokumentation :

Wenn der Schlüssel disable_existing_loggers in LOGGING dictConfig auf Auf True gesetzt ist, werden alle Logger der Standardkonfiguration Deaktiviert. Deaktivierte Logger sind nicht dasselbe wie Entfernt. Der Logger ist zwar noch vorhanden, verwirft jedoch alles, was an ihm protokolliert wird, und gibt nicht einmal Einträge an einen übergeordneten Logger weiter. Daher sollten Sie bei der Verwendung von 'Disable_existing_loggers' sehr vorsichtig sein: True; Es ist wahrscheinlich nicht das, was Sie wollen. Stattdessen können Sie disable_existing_loggers auf False setzen und einige oder alle Standard-Logger neu definieren. oder Sie können LOGGING_CONFIG auf None setzen und die Protokollierungskonfiguration selbst übernehmen.

Für Nachwelt und Detail: Die Erklärung? Die meisten Verwirrung, die ich denke, beruht auf Djangos schlechter Erklärung von disable_existing_loggers, die besagt, dass bei True "die Standardkonfiguration vollständig überschrieben wird". In Ihrer eigenen Antwort haben Sie festgestellt, dass dies nicht richtig ist. Was passiert ist, dass die vorhandenen Logger, die Django bereits konfiguriert, disabled nicht ersetzt werden.

Die Python-Protokollierung Dokumentation erklärt es besser (Hervorhebung hinzugefügt):

disable_existing_loggers - Wenn als False angegeben, werden Protokollierer, die bei diesem Aufruf vorhanden sind, in Ruhe gelassen. Der Standardwert ist True, da Das alte Verhalten rückwärtskompatibel macht. Dieses Verhalten Bewirkt das Vorhandensein deaktivieren vorhandener Logger, es sei denn, sie oder ihre Vorfahren werden explizit in der Protokollierungskonfiguration angegeben.

Basierend auf Django-Dokumenten denken wir, "die Standardeinstellungen mit meiner eigenen LOGGING-Konfiguration überschreiben und alles, was ich nicht spezifizieren werde, wird bubble up". Ich bin auch über diese Erwartung gestolpert. Das Verhalten, das wir erwarten, ist in der Richtung von replace_existing_loggers (was keine echte Sache ist). Stattdessen sind die Django-Logger halt die Klappe nicht gesprudelt.

Wir müssen das Setup dieser Django-Logger von vornherein verhindern und hier sind die Django docs hilfreicher:

Wenn Sie die Protokollierung überhaupt nicht konfigurieren möchten (oder Sie möchten die Protokollierung manuell Mit Ihrem eigenen Ansatz konfigurieren), können Sie LOGGING_CONFIG Auf Keine setzen. Dadurch wird der Konfigurationsvorgang deaktiviert.

Hinweis: Wenn Sie LOGGING_CONFIG auf None setzen, bedeutet dies nur, dass der Konfigurationsprozess Deaktiviert ist und sich nicht selbst protokolliert. Wenn Sie den Konfigurationsprozess Deaktivieren, führt Django weiterhin Protokollierungsaufrufe aus, wobei Auf das festgelegte Standardprotokollverhalten zurückgesetzt wird.

Django verwendet weiterhin seine Logger. Da sie jedoch von der Konfiguration nicht behandelt (und dann deaktiviert) werden, werden diese Logger erwartungsgemäß aufblasen. Ein einfacher Test mit den obigen Einstellungen:

manage.py Shell
>>> import logging
>>> logging.warning('root logger')
WARNING 2014-03-11 13:35:08,832 root root logger
>>> l = logging.getLogger('Django.request')
>>> l.warning('request logger')
WARNING 2014-03-11 13:38:22,000 Django.request request logger
>>> l.propagate, l.disabled
(1, 0)
70
JCotton

Ok, das Verhalten ist "korrekt", aber nicht erwartet. Django/conf/__init__.py:65:

def _configure_logging(self):
    ...
    if self.LOGGING_CONFIG:
        from Django.utils.log import DEFAULT_LOGGING
        # First find the logging configuration function ...
        logging_config_path, logging_config_func_name = self.LOGGING_CONFIG.rsplit('.', 1)
        logging_config_module = importlib.import_module(logging_config_path)
        logging_config_func = getattr(logging_config_module, logging_config_func_name)

        logging_config_func(DEFAULT_LOGGING)

        if self.LOGGING:
            # Backwards-compatibility shim for #16288 fix
            compat_patch_logging_config(self.LOGGING)

            # ... then invoke it with the logging settings
            logging_config_func(self.LOGGING)

Was passiert, ist, dass die Standardprotokollierungskonfiguration angewendet wird und Django.request-Logger erstellt wird. Dann wird meine benutzerdefinierte LOGGING-Konfiguration mit disable_existing_loggers = True angewendet, aber Python löscht den bereits vorhandenen Logger Django.request nicht, sondern deaktiviert ihn nur.

Also muss ich Django.request logger in meiner Konfiguration manuell neu konfigurieren. :(

1
warvariuc

Für Django-2.1 habe ich festgestellt, dass die Protokollierungskonfiguration präziser ist:

$ ./manage.py Shell

>>> import logging
>>> # Grub all Django loggers
>>> loggers = [
        name for name in logging.root.manager.loggerDict 
        if 'Django' in name
    ]
>>> for each in loggers:
        logger = logging.getLogger(each)
        print(
            'Logger Name: {0}\nLogger Handlers: {1}\n'
            'Logger Propagates: {2}\n\n'.format(
                each, 
                logger.handlers, 
                logger.propagate
            )
        )

Logger Name: Django.db
Logger Handlers: []
Logger Propagates: True


Logger Name: Django.request
Logger Handlers: []
Logger Propagates: True


Logger Name: Django.template
Logger Handlers: []
Logger Propagates: True


Logger Name: Django.db.backends
Logger Handlers: []
Logger Propagates: True


Logger Name: Django.db.backends.schema
Logger Handlers: []
Logger Propagates: True


Logger Name: Django.security.csrf
Logger Handlers: []
Logger Propagates: True


Logger Name: Django
Logger Handlers: [<logging.StreamHandler object at 0x7f706d5dd780>, <Django.utils.log.AdminEmailHandler object at 0x7f706d740cf8>]
Logger Propagates: True


Logger Name: Django.contrib.gis
Logger Handlers: []
Logger Propagates: True


Logger Name: Django.contrib
Logger Handlers: []
Logger Propagates: True


Logger Name: Django.security
Logger Handlers: []
Logger Propagates: True


Logger Name: Django.server
Logger Handlers: [<logging.StreamHandler object at 0x7f706d59eba8>]
Logger Propagates: False

Wie zitiert in den Dokumenten :

Alle Logger mit Ausnahme von Django.server geben die Protokollierung an ihre Eltern weiter, bis zum Root-Django-Logger. Die Handler Console und mail_admins sind an den Root-Logger angehängt, um das oben beschriebene Verhalten bereitzustellen.

Dies stimmt mit den Dokumenten von propagate überein, die Folgendes angeben: 

Hinweis

Wenn Sie einen Handler an einen Logger und einen oder mehrere seiner Vorfahren anhängen, Kann derselbe Datensatz mehrmals ausgegeben werden. Im Allgemeinen sollten Sie Keinen Handler an mehr als einen Logger anschließen müssen - wenn Sie nur An den entsprechenden Logger anhängen, der in der Logger-Hierarchie am höchsten ist, dann Es werden alle Ereignisse angezeigt, die von allen nachgelagerten Protokollierern protokolliert werden, vorausgesetzt, wird die Weitergabeeinstellung auf "True" gesetzt. Ein übliches Szenario ist , Um Handler nur an den Root-Logger anzuhängen und die Weitergabe Den Rest erledigen zu lassen.

Daher habe ich beschlossen, Django nicht daran zu hindern, die Protokollierung zu konfigurieren. Ich wollte das Senden von E-Mails an die Admins beenden, weil ich sentry verwende, und ich habe den Root-Logger so konfiguriert, dass er die console- und file-Handler verwendet, gemäß den Django-Dokumenten Beispielen :

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': {
        'require_debug_false': {
            '()': 'Django.utils.log.RequireDebugFalse',
        },
        'require_debug_true': {
            '()': 'Django.utils.log.RequireDebugTrue',
        },
    },
    'handlers': {
        'console': {
            'level': 'INFO',
            'filters': ['require_debug_true'],
            'class': 'logging.StreamHandler',
            'formatter': 'simple'
        },
        'file': {
            'level': 'INFO',
            'filters': ['require_debug_false'],
            'class': 'logging.FileHandler',
            'filename': os.path.join(LOGGING_DIR, 'Django.log'),
            'formatter': 'verbose'
        },
    },
    'loggers': {
        'Django': {
            'handlers': ['file', 'console'],
            'level': 'INFO',
            'propagate': True,
        },
    }
}

Was in ... resultiert:

Logger Name: Django
Logger Handlers: [<logging.FileHandler object at 0x7f5aa0fd1cc0>, <logging.StreamHandler object at 0x7f5aa0fd1ef0>]
Logger Propagates: True

Noch nicht in der Produktion getestet, aber es scheint, als würde es funktionieren.

0
raratiru