web-dev-qa-db-fra.com

Configuration élégante de Python connexion Django

Je n'ai pas encore trouvé de moyen de configurer Python journalisation avec Django dont je suis satisfait. Mes exigences sont assez simples:

  • Différents gestionnaires de journaux pour différents événements - c'est-à-dire que je veux pouvoir me connecter à différents fichiers
  • Accès facile aux enregistreurs dans mes modules. Le module devrait pouvoir trouver son enregistreur sans effort.
  • Devrait être facilement applicable aux modules de ligne de commande. Certaines parties du système sont des processus de ligne de commande ou démon autonomes. La journalisation doit être facilement utilisable avec ces modules.

Ma configuration actuelle consiste à utiliser un logging.conf fichier et configuration de la journalisation dans chaque module à partir duquel je me connecte. Ça ne me semble pas bien.

Avez-vous une configuration de journalisation que vous aimez? Veuillez détailler: comment configurer la configuration (utilisez-vous logging.conf ou configurez-le en code), où/quand lancez-vous les enregistreurs, et comment y accéder dans vos modules, etc.

93
Parand

La meilleure façon que j'ai trouvée jusqu'à présent est d'initialiser la configuration de la journalisation dans settings.py - nulle part ailleurs. Vous pouvez soit utiliser un fichier de configuration, soit le faire par programme étape par étape - cela dépend simplement de vos besoins. L'essentiel est que j'ajoute généralement les gestionnaires que je veux à l'enregistreur racine, en utilisant les niveaux et parfois la journalisation.Filtres pour obtenir les événements que je veux dans les fichiers appropriés, console, syslogs, etc. Vous pouvez bien sûr ajouter des gestionnaires à n'importe quel autre enregistreur aussi, mais il n'y a généralement pas un besoin de cela dans mon expérience.

Dans chaque module, je définis un logger en utilisant

logger = logging.getLogger(__name__)

et l'utiliser pour la journalisation des événements dans le module (et, si je veux différencier davantage) utiliser un enregistreur qui est un enfant de l'enregistreur créé ci-dessus.

Si mon application va être potentiellement utilisée dans un site qui ne configure pas la connexion dans settings.py, je définis un NullHandler quelque part comme suit:

#someutils.py

class NullHandler(logging.Handler):
    def emit(self, record):
        pass

null_handler = NullHandler()

et assurez-vous qu'une instance de celui-ci est ajoutée à tous les enregistreurs créés dans les modules de mes applications qui utilisent la journalisation. (Remarque: NullHandler est déjà dans le package de journalisation pour Python 3.1, et sera dans Python 2.7.) Donc:

logger = logging.getLogger(__name__)
logger.addHandler(someutils.null_handler)

Ceci est fait pour s'assurer que vos modules fonctionnent correctement sur un site qui ne configure pas la connexion dans settings.py, et que vous n'obtenez aucun message ennuyeux "Aucun gestionnaire n'a pu être trouvé pour l'enregistreur XYZ" (qui sont des avertissements concernant potentiellement journalisation mal configurée).

Le faire de cette façon répond à vos exigences déclarées:

  • Vous pouvez configurer différents gestionnaires de journaux pour différents événements, comme vous le faites actuellement.
  • Accès facile aux enregistreurs dans vos modules - utilisez getLogger(__name__).
  • Facilement applicable aux modules de ligne de commande - ils importent également settings.py.

Mise à jour: Notez qu'à partir de la version 1.3, Django incorpore désormais support pour la journalisation .

53
Vinay Sajip

Je sais que c'est déjà une réponse résolue, mais selon Django> = 1.3, il y a un nouveau paramètre de journalisation.

Passer de l'ancien au nouveau n'est pas automatique, alors j'ai pensé que je vais l'écrire ici.

Et bien sûr checkout le Django doc pour encore plus.

Ceci est la configuration de base, créée par défaut avec Django-admin createproject v1.3 - le kilométrage peut changer avec les dernières versions Django:

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'mail_admins': {
            'level': 'ERROR',
            'class': 'Django.utils.log.AdminEmailHandler',
        }
    },
    'loggers': {
        'Django.request': {
            'handlers': ['mail_admins'],
            'level': 'ERROR',
            'propagate': True,
        }
    }
}

Cette structure est basée sur la norme Python logging dictConfig , qui dicte les blocs suivants:

  • formatters - la valeur correspondante sera un dict dans lequel chaque clé est un identifiant de formateur et chaque valeur est un dict décrivant comment configurer l'instance de formateur correspondante.
  • filters - la valeur correspondante sera un dict dans lequel chaque clé est un identifiant de filtre et chaque valeur est un dict décrivant comment configurer l'instance de filtre correspondante.
  • handlers - la valeur correspondante sera un dict dans lequel chaque clé est un identifiant de gestionnaire et chaque valeur est un dict décrivant comment configurer l'instance de gestionnaire correspondante. Chaque gestionnaire possède les clés suivantes:

    • class (obligatoire). Il s'agit du nom complet de la classe de gestionnaire.
    • level (facultatif). Le niveau du gestionnaire.
    • formatter (facultatif). L'ID du formateur pour ce gestionnaire.
    • filters (facultatif). Une liste des identifiants des filtres pour ce gestionnaire.

Je fais habituellement au moins ceci:

  • ajouter un fichier .log
  • configurer mes applications pour écrire dans ce journal

Ce qui se traduit par:

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'verbose': {
            'format': '%(levelname)s %(asctime)s %(module)s %(process)d %(thread)d %(message)s'
        },
        'simple': {
            'format': '%(levelname)s %(message)s'
        },
    },
    'filters': {
        'require_debug_false': {
            '()': 'Django.utils.log.RequireDebugFalse'
        }
    },
    'handlers': {
        'null': {
            'level':'DEBUG',
            'class':'Django.utils.log.NullHandler',
        },
        'console':{
            'level': 'DEBUG',
            'class': 'logging.StreamHandler',
            'formatter': 'simple'
        },
        # I always add this handler to facilitate separating loggings
        'log_file':{
            'level': 'DEBUG',
            'class': 'logging.handlers.RotatingFileHandler',
            'filename': os.path.join(VAR_ROOT, 'logs/Django.log'),
            'maxBytes': '16777216', # 16megabytes
            'formatter': 'verbose'
        },
        'mail_admins': {
            'level': 'ERROR',
            'filters': ['require_debug_false'],
            'class': 'Django.utils.log.AdminEmailHandler',
            'include_html': True,
        }
    },
    'loggers': {
        'Django.request': {
            'handlers': ['mail_admins'],
            'level': 'ERROR',
            'propagate': True,
        },
        'apps': { # I keep all my of apps under 'apps' folder, but you can also add them one by one, and this depends on how your virtualenv/paths are set
            'handlers': ['log_file'],
            'level': 'INFO',
            'propagate': True,
        },
    },
    # you can also shortcut 'loggers' and just configure logging for EVERYTHING at once
    'root': {
        'handlers': ['console', 'mail_admins'],
        'level': 'INFO'
    },
}

modifier

Voir les exceptions de requête sont désormais toujours enregistrées et Ticket # 16288 :

J'ai mis à jour l'exemple de configuration ci-dessus pour inclure explicitement le filtre correct pour mail_admins afin que, par défaut, les e-mails ne soient pas envoyés lorsque le débogage a la valeur True.

Vous devez ajouter un filtre:

'filters': {
    'require_debug_false': {
        '()': 'Django.utils.log.RequireDebugFalse'
    }
},

et appliquez-le au gestionnaire mail_admins:

    'mail_admins': {
        'level': 'ERROR',
        'filters': ['require_debug_false'],
        'class': 'Django.utils.log.AdminEmailHandler',
        'include_html': True,
    }

Sinon, le Django.core.handers.base.handle_uncaught_exception ne transmet pas d'erreurs à l'enregistreur 'Django.request' si settings.DEBUG est True.

Si vous ne le faites pas dans Django 1.5 vous obtiendrez un

DeprecationWarning: Vous n'avez aucun filtre défini sur le gestionnaire de journalisation 'mail_admins': ajout d'un filtre de débogage implicite-faux uniquement

mais les choses fonctionneront toujours correctement LES DEUX dans Django 1.4 et Django 1.5.

** terminer l'édition **

Cette conf est fortement inspirée de l'exemple de conf du document Django, mais en ajoutant la partie du fichier journal.

Je fais souvent aussi ce qui suit:

LOG_LEVEL = 'DEBUG' if DEBUG else 'INFO'

...
    'level': LOG_LEVEL
...

Ensuite, dans mon code python j'ajoute toujours un NullHandler au cas où aucune conf de journalisation n'est définie. Cela évite les avertissements pour aucun gestionnaire spécifié. Particulièrement utile pour les bibliothèques qui ne sont pas nécessairement appelées uniquement dans Django ( ref )

import logging
# Get an instance of a logger
logger = logging.getLogger(__name__)
class NullHandler(logging.Handler): #exists in python 3.1
    def emit(self, record):
        pass
nullhandler = logger.addHandler(NullHandler())

# here you can also add some local logger should you want: to stdout with streamhandler, or to a local file...

[...]

logger.warning('etc.etc.')

J'espère que cela t'aides!

121
Stefano

Nous initialisons la journalisation au niveau supérieur urls.py en utilisant un logging.ini fichier.

L'emplacement du logging.ini est fourni dans settings.py, mais c'est tout.

Chaque module fait ensuite

logger = logging.getLogger(__name__)

Pour distinguer les instances de test, de développement et de production, nous avons différents fichiers logging.ini. Pour la plupart, nous avons un "journal de console" qui va à stderr avec des erreurs uniquement. Nous avons un "journal d'application" qui utilise un fichier journal régulier qui va dans un répertoire de journaux.

8
S.Lott

J'utilise actuellement un système de journalisation, que j'ai créé moi-même. Il utilise le format CSV pour la journalisation.

Django-csvlog

Ce projet n'a toujours pas de documentation complète, mais j'y travaille.

6
Oduvan