Подтвердить что ты не робот

Регистрация в Python в Django

Я разрабатываю приложение Django, и я пытаюсь использовать модуль протоколирования Python для регистрации ошибок/трассировки. В идеале я хотел бы иметь разные регистраторы, настроенные для разных областей сайта. Пока у меня все это работает, но одно дело, что я почесываю голову.

У меня есть корневой журнал, отправляющийся в sys.stderr, и я настроил другой регистратор для записи в файл. Это находится в моем файле settings.py:

sviewlog = logging.getLogger('MyApp.views.scans')
view_log_handler = logging.FileHandler('C:\\MyApp\\logs\\scan_log.log')
view_log_handler.setLevel(logging.INFO)
view_log_handler.setFormatter(logging.Formatter('%(asctime)s %(name)-12s %(levelname)-8s %(message)s'))
sviewlog.addHandler(view_log_handler)

Кажется довольно простым. Здесь проблема, хотя: то, что я пишу в sviewlog, дважды записывается в файл журнала. Корневой регистратор печатает только один раз. Это как addHandler() вызывается дважды. И когда я отправляю свой код через отладчик, это именно то, что я вижу. Код в settings.py выполняется дважды, поэтому два файла FileHandlers создаются и добавляются в один экземпляр журнала. Но почему? И как мне обойти это?

Может ли кто-нибудь сказать мне, что здесь происходит? Я попытался переместить код экземпляра loglog/обработчика sviewlog в файл, где он использовался (поскольку это на самом деле кажется подходящим для меня местом), но у меня такая же проблема. Большинство примеров, которые я видел в Интернете, используют только корневой журнал, и я предпочел бы иметь несколько регистраторов.

4b9b3361

Ответ 1

Позвольте мне ответить на мой собственный вопрос. Основная проблема здесь заключается в том, что settings.py импортируется дважды или, возможно, даже больше (см. здесь). (Я до сих пор не понимаю, почему это так. Может быть, какой-то эксперт Django может объяснить это мне.) Это похоже на некоторые другие модули. На данный момент я не думаю, что было бы разумно делать предположения о том, сколько раз будет загружен параметр settings.py. В этом отношении такие допущения в целом небезопасны. У меня был этот код в других местах, кроме settings.py, и результаты были похожи.

Вам нужно закодировать это. То есть, вы должны проверить свой регистратор для существующих обработчиков, прежде чем добавлять к нему дополнительные обработчики. Это немного уродливо, потому что вполне разумно иметь несколько обработчиков - даже одного типа - связанных с одним регистратором. Есть несколько решений для решения этой проблемы. Один из них - проверить свойство обработчиков вашего объекта журнала. Если вам нужен только один обработчик и длинa > 0, не добавляйте его. Лично я не люблю это решение, потому что оно становится беспорядочным с большим количеством обработчиков.

Я предпочитаю что-то вроде этого (спасибо Томасу Геттлеру):

# file logconfig.py
if not hasattr(logging, "set_up_done"):
    logging.set_up_done=False

def set_up(myhome):
    if logging.set_up_done:
        return
    # set up your logging here
    # ...
    logging.set_up_done=True

Я должен сказать, что я хочу, чтобы Django импортировал параметры settings.py несколько раз, были лучше документированы. И я бы предположил, что моя конфигурация каким-то образом вызывает этот множественный импорт, но мне трудно понять, что вызывает проблему и почему. Возможно, я просто не мог найти это в своих документах, но я бы подумал, что вам нужно предупредить своих пользователей.

Ответ 2

Начиная с версии 1.3, Django использует стандартное ведение журнала python, настроенное с настройкой LOGGING (здесь описано: 1.3, dev).

Ссылка на запись в Django: 1.3, dev.

Ответ 3

Трудно прокомментировать ваш конкретный случай. Если параметр settings.py выполняется дважды, то это нормально, что вы получаете две строки для каждого отправленного журнала.

У нас была одна и та же проблема, поэтому мы создали в наших проектах один модуль, предназначенный для ведения журнала. У этого модуля есть шаблон "singleton" модуля, так что мы выполняем только один интересный код один раз.

Он выглядит следующим образом:

def init_logging():
    stdoutHandler = logging.StreamHandler( sys.stdout )
    stdoutHandler.setLevel( DEBUG )
    stdoutHandler.setFormatter( logging.Formatter( LOG_FORMAT_WITH_TIME ) )
    logging.getLogger( LOG_AREA1 ).addHandler( stdoutHandler )

logInitDone=False #global variable controlling the singleton.
if not logInitDone:
    logInitDone = True
    init_logging()

Импортирование log.py в первый раз будет правильно настроить ведение журнала.

Ответ 4

Возрождение старого потока, но у меня возникали повторяющиеся сообщения при использовании Django 1.3 Python с формат dictConfig.

disable_existing_loggers избавляется от проблемы дублирования обработчика/ведения журнала с несколькими параметрами settings.py, но вы все равно можете получать повторяющиеся сообщения журнала, если не укажете propagate boolean соответствующим образом на конкретном logger. А именно, убедитесь, что вы установили propagate=False для дочерних регистраторов. Например.

'loggers': {
    'django': {
        'handlers':['null'],
        'propagate': True,
        'level':'INFO',
    },
    'django.request': {
        'handlers': ['console'],
        'level': 'ERROR',
        'propagate': False,
    },
    'project': {
        'handlers': ['console', 'project-log-file'],
        'level': 'DEBUG',
        'propagate': True,
    },
    'project.customapp': {
        'handlers': ['console', 'customapp-log-file'],
        'level': 'DEBUG',
        'propagate': False,
    },
}

Здесь project.customapp устанавливает propagate=False так, чтобы он не был пойман журналом project. Django logging docs отлично, как всегда.

Ответ 5

`` Чтобы ответить на вопрос, почему "Django импортирует settings.py несколько раз": это не так. ''

Собственно, он дважды импортируется (пропустите предыдущий фрагмент кода, чтобы получить право на него, но хорошо прочитайте, если у вас есть время):

http://blog.dscpl.com.au/2010/03/improved-wsgi-script-for-use-with.html

PS - Извините, что возродил старый поток.

Ответ 6

Вы можете обойти свою проблему, проверив количество обработчиков, когда вы делаете свой init.

def init_logging():
    stdoutHandler = logging.StreamHandler( sys.stdout )
    stdoutHandler.setLevel( DEBUG )
    stdoutHandler.setFormatter( logging.Formatter( LOG_FORMAT_WITH_TIME ) )
    logger = logging.getLogger( LOG_AREA1 )
    if len(logger.handlers) < 1:
        logger.addHandler( stdoutHandler )

Я не думаю, что это отличный способ справиться с этим. Лично для входа в django с модулем регистрации python я создаю logger в views.py для каждого интересующего меня приложения, а затем захватываю регистратор в каждой функции просмотра.

from django.http import HttpResponse
from magic import makeLogger
from magic import getLogger

makeLogger('myLogName', '/path/to/myLogName.log')
def testLogger(request):
    logger = getLogger('myLogName')
    logger.debug('this worked')
    return HttpResponse('TEXT, HTML or WHATEVER')

Это довольно хорошая статья об отладке django и охватывает некоторые записи: http://simonwillison.net/2008/May/22/debugging/

Ответ 7

Чтобы ответить на вопрос, почему "Django импортирует settings.py несколько раз": это не так.

Вероятно, вы используете многопроцессорный/многопоточный веб-сервер, который создает несколько суб-интерпретаторов python, где каждый из них однажды импортирует код из вашего приложения django.

Проверьте, что на тестовом сервере django вы увидите, что настройки не импортируются многократно.

Некоторое время назад я разработал отличный синглтон (версия идиомы python borg, чтобы быть более точным) с моим первым приложением django/apache, прежде чем я быстро понял, что да, у меня было более одного экземпляра моего синглтона..

Ответ 8

Вы также можете использовать промежуточное ПО промежуточного уровня, чтобы получить аналогичный эффект, без частных переменных. Обратите внимание, что это только настраивает ведение журнала для веб-запросов - вам нужно будет найти другое решение, если вы хотите, чтобы в вашей командной строке выполнялись протоколирования.

from django.conf import settings
from django.core.exceptions import MiddlewareNotUsed
import logging
import logging.handlers
import logging.config

__all__ = ('LoggingConfigMiddleware',)


class LoggingConfigMiddleware:
    def __init__(self):
        '''Initialise the logging setup from settings, called on first request.'''
        if hasattr(settings, 'LOGGING'):
            logging.config.dictConfig(settings.LOGGING)
        elif getattr(settings, 'DEBUG', False):
            print 'No logging configured.'
        raise MiddlewareNotUsed('Logging setup only.')

Ответ 9

Зачем использовать python logger вместо django-logging? Попробуйте, он может решить вашу проблему.

http://code.google.com/p/django-logging/wiki/Overview

В настоящий момент это разрешит просмотр корневого регистратора, но вы можете уверенно записать несколько логгеров.

Ответ 10

Взломанный способ, но вы можете попытаться поместить код регистрации внутри admin.py. Предполагается, что он будет импортирован только один раз.

В качестве альтернативы; можете ли вы сначала проверить, существует ли журнал MyApp.views.scans? Если он существует (возможно, возникает ошибка), вы можете просто пропустить создание (и, следовательно, не добавлять обработчик снова). Более чистый способ, но я не пробовал это, хотя.

Также должно быть более подходящее место для размещения этого кода (__init__.py?). settings.py предназначен для настроек.

Ответ 11

Чтобы добавить сообщение A Lee, в документации по протоколу python говорится об этом:

Logger.propagate

Если это оценивается как false, сообщения регистрации не передаются этим регистратором или его дочерними регистраторами в обработчики регистраторов более высокого уровня (предков). Конструктор устанавливает этот атрибут в 1.

Это означает, что если propagate == False, то дочерний регистратор НЕ отправит сообщение регистрации в свой родительский журнал