Вход в Python в Django - PullRequest
       24

Вход в Python в Django

41 голосов
/ 05 декабря 2008

Я занимаюсь разработкой приложения 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 создаются и добавляются в один и тот же экземпляр регистратора. Но почему? И как мне обойти это?

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

Ответы [ 11 ]

30 голосов
/ 06 декабря 2008

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

Вы должны кодировать это. То есть вы должны проверить свой регистратор на наличие существующих обработчиков, прежде чем добавлять к нему дополнительные обработчики. Это немного уродливо, потому что совершенно разумно иметь несколько обработчиков - даже одного типа - подключенных к одному регистратору. Есть несколько вариантов решения этой проблемы. Одним из них является проверка свойства handlers вашего объекта logger. Если вам нужен только один обработчик и ваша длина> 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 несколько раз, что было лучше задокументировано. И я бы предположил, что моя конфигурация как-то вызывает этот множественный импорт, но у меня возникают проблемы с выяснением того, что вызывает проблему и почему. Может быть, я просто не смог найти это в их документах, но я думаю, что именно об этом нужно предупреждать пользователей.

24 голосов
/ 21 октября 2010

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

Ссылка на Django: 1.3 , dev .

14 голосов
/ 05 декабря 2008

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

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

Это выглядит так:

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 логирование будет настроено правильно.

11 голосов
/ 05 мая 2011

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

disable_existing_loggers избавляет от проблемы с дублирующим обработчиком / ведением журнала при нескольких загрузках settings.py, но вы все равно можете получить дублированные сообщения журнала, если вы не укажете логическое значение propagate соответствующим образом для конкретного 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 превосходны, как всегда.

6 голосов
/ 17 апреля 2010

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

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

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

PS– Извините, что оживил старую ветку.

4 голосов
/ 06 декабря 2008

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

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 logging я создаю регистратор в 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/

3 голосов
/ 08 февраля 2011

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

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.')
3 голосов
/ 08 июля 2009

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

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

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

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

1 голос
/ 05 декабря 2008

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

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

На данный момент он позволяет только просматривать корневой регистратор, но вы можете писать в несколько регистраторов.

0 голосов
/ 05 октября 2011

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

Logger.propagate

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...