Елегантна настройка Python-реєстрації в Django


101

Мені ще потрібно знайти спосіб налаштування журналу Python із Django, яким я задоволений. Мої вимоги досить прості:

  • Різні обробники журналів для різних подій - тобто я хочу мати можливість увійти в різні файли
  • Легкий доступ до реєстраторів в моїх модулях. Модуль повинен мати можливість знайти свій реєстратор з невеликими зусиллями.
  • Слід легко застосувати до модулів командного рядка. Частини системи є автономним командним рядком або процесами демон. За допомогою цих модулів журнал повинен бути легко застосований.

Моя поточна настройка - використовувати logging.confфайл та журнал налаштувань у кожному модулі, з якого я входжу. Це не вірно.

Чи є у вас налаштування журналу, яке вам подобається? Будь ласка, детально розкажіть: як ви налаштовуєте конфігурацію (чи використовуєте ви її logging.confчи встановлюєте її в коді), де / коли ви ініціюєте реєстратори та як ви отримуєте доступ до них у своїх модулях тощо.


1
Можливо, вам здасться така корисна трансляція - ericholscher.com/blog/2008/aug/29/… . Також кращу підтримку для входу в Django запропонував Саймон Віллісон (див. Simonwillison.net/2009/Sep/28/ponies ).
Домінік Роджер

@Dominic Rodger - Ви вже можете робити гнучку реєстрацію програм у Django, пропозиція Саймона головним чином щодо полегшення входу в систему Django. У Python працює робота над тим, щоб додати конфігурацію на базі словника до журналу Python, від якої Django може отримати користь.
Vinay Sajip

Відповіді:


57

Найкращий спосіб, який я знайшов поки що, - це ініціалізувати налаштування журналу в settings.py - ніде більше. Можна або скористатися конфігураційним файлом, або зробити це поетапно програмно - це просто залежить від ваших вимог. Головне, що я зазвичай додаю обробники, які я хочу, до кореневого журналу, використовуючи рівні, а іноді й журнал. Фільтри для отримання подій, які я хочу, до відповідних файлів, консолей, syslogs тощо. Ви, звичайно, можете додавати обробники до будь-яких інших реєстраторів теж, але в моєму досвіді в цьому зазвичай немає потреби.

У кожному модулі я визначаю реєстратор за допомогою

logger = logging.getLogger(__name__)

і використовувати це для реєстрації подій у модулі (і, якщо я хочу далі розмежуватись), використовуйте реєстратор, який є дочірнім джерелом реєстратора, створеним вище.

Якщо моє додаток потенційно буде використовуватися на сайті, який не налаштовує вхід у систему settings.py, я десь визначаю NullHandler так:

#someutils.py

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

null_handler = NullHandler()

і переконайтеся, що його екземпляр додається до всіх реєстраторів, створених у модулях моїх додатків, які використовують журнал. (Примітка: NullHandler вже є в пакеті журналу для Python 3.1, і він буде в Python 2.7.) Отже:

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

Це робиться для того, щоб ваші модулі чудово грали на сайті, який не налаштовує вхід у систему settings.py, і щоб у вас не було дратувальних повідомлень "Не вдалося знайти жодних обробників для реєстратора XYZ" (які потенційно попереджають неправильно налаштований журнал).

Це робиться таким чином, що відповідає вашим заявленим вимогам:

  • Ви можете налаштувати різні обробники журналів для різних подій, як це робите в даний час.
  • Легкий доступ до реєстраторів у ваших модулях - використання getLogger(__name__).
  • Легко застосовно до модулів командного рядка - вони також імпортують settings.py.

Оновлення: Зауважте, що, починаючи з версії 1.3, Django тепер має підтримку для ведення журналу .


Чи не вимагає цього, щоб кожен модуль мав обробник, визначений у конфігурації (ви не можете використовувати обробник foo для обробки foo.bar)? Дивіться розмову, яку ми мали роки тому, на сторінці groups.google.com/group/comp.lang.python/browse_thread/thread/…
andrew cooke

1
@andrew cooke: Ви можете використовувати обробник, fooщоб обробляти події, до яких увійшли в систему foo.bar. Re. цей потік - і fileConfig, і dictConfig тепер мають варіанти запобігання відключення старих реєстраторів. Дивіться цей випуск: bugs.python.org/issue3136 , який з’явився через пару місяців після випуску bugs.python.org/issue2697 - у будь-якому разі, його розібрали з червня 2008 року.
Vinay Sajip

хіба не буде краще робити logger = someutils.getLogger(__name__)там, де someutils.getLoggerповертає реєстратор logging.getLoggerз уже доданим null_handler?
7yl4r

1
@ 7yl4r Вам не потрібен кожен реєстратор, який має NullHandlerдодані - зазвичай це лише реєстратор верхнього рівня для ієрархії ваших пакетів. Тож це буде зайвим, ІМО.
Vinay Sajip

122

Я знаю, що це вже вирішена відповідь, але відповідно до django> = 1.3 є новий параметр журналу.

Перехід від старого до нового не є автоматичним, тому я подумав, що запишу його тут.

І звичайно оформити док-джанго на ще декілька.

Це основний конф, створений за замовчуванням за допомогою django-admin createproject v1.3 - пробіг може змінитися в останніх версіях 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,
        }
    }
}

Ця структура заснована на стандартному протоколі протоколу Python dictConfig , який диктує такі блоки:

  • formatters - відповідне значення буде диктатом, у якому кожен ключ є ідентифікатором форматера, а кожне значення - це дікт, що описує, як налаштувати відповідний екземпляр Форматтера.
  • filters - відповідне значення буде диктатом, у якому кожен ключ є ідентифікатором фільтра, і кожне значення є діктом, що описує, як налаштувати відповідний екземпляр фільтра.
  • handlers- відповідне значення буде диктатом, у якому кожен ключ є ідентифікатором обробника, і кожне значення є діктом, що описує, як налаштувати відповідний екземпляр обробника. Кожен обробник має такі ключі:

    • class(обов’язково). Це повністю кваліфікована назва класу обробників.
    • level(на вибір). Рівень обробника.
    • formatter(на вибір). Ідентифікатор форматера для цього обробника.
    • filters(на вибір). Список ідентифікаторів фільтрів для цього обробника.

Я зазвичай роблю принаймні так:

  • додати файл .log
  • налаштувати мої програми для запису в цей журнал

Що перекладається на:

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'
    },
}

редагувати

Перегляньте винятки щодо запиту, які завжди реєструються, і Ticket № 16288 :

Я оновив вищевказаний зразок conf, щоб явно включити правильний фільтр для mail_admins, так що за замовчуванням електронні листи не надсилаються, коли налагодження відповідає True.

Вам слід додати фільтр:

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

і застосуйте його до обробника mail_admins:

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

В іншому випадку django.core.handers.base.handle_uncaught_exceptionне передає помилок у реєстратор 'django.request', якщо налаштування.DEBUG є True.

Якщо цього не зробити в Django 1.5, ви отримаєте

DeprecationWarning: у обробника журналу 'mail_admins' не визначено фільтрів: додавання неявного фільтра, що відповідає лише налагодженням

але все одно працюватимуть правильно БАТЬ у django 1.4 та django 1.5.

** завершити редагування **

Ця конфіденція сильно надихається зразком conf в документі django, але додаючи частину файлу журналу.

Я часто також роблю такі дії:

LOG_LEVEL = 'DEBUG' if DEBUG else 'INFO'

...
    'level': LOG_LEVEL
...

Тоді в свій код python я завжди додаю NullHandler на випадок, якщо конфліктування журналів не визначено. Це дозволить уникнути попереджень для жодного обробника. Особливо корисно для ліб, які не обов'язково називати лише в Джанго ( 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.')

Сподіваюся, це допомагає!


Стефано, велике спасибі за детальну відповідь, дуже корисно. Це може зробити корисним оновлення до 1.3.
Паранд

Паранд, напевно, варто (IMHO!) Перейти до джанго 1.3, хоча є кілька моментів, щоб подбати про плавний перехід - відкрийте нове питання ТА, якщо потрапите в біду ;-)
Стефано

до речі: я все ще використовую такі налаштування та журнал файлів, але я перейшов до вартового для виробництва!
Стефано

@clime добре, я спробував пояснити це у самій відповіді: у випадку, якщо конфіденційний журнал не визначений. Це дозволить уникнути попереджень для жодного обробника. Особливо корисно для ліб, які не обов'язково називаються лише у Джанго (ref)
Стефано

Я не бачу, як ви використовуєте це визначення: 'null': {'level': 'DEBUG', 'class': 'django.utils.log.NullHandler',}
clime

9

Ми ініціалізуємо ведення журналу на верхньому рівні urls.pyза допомогою logging.iniфайлу.

Місце розташування logging.iniпредставлене в settings.py, але це все.

Кожен модуль потім робить

logger = logging.getLogger(__name__)

Для розрізнення тестів, розробок та виробничих випадків ми маємо різні файли logging.ini. Здебільшого у нас є "консольний журнал", який переходить до stderr лише з помилками. У нас є "журнал додатків", який використовує звичайний файл журналу, що переходить, до каталогу журналів.


Я в кінцевому підсумку скористався цим, за винятком ініціалізації в settings.py замість urls.py
Паранд

Як ви використовуєте налаштування з settings.py у файлі logging.ini? Наприклад, мені потрібна настройка BASE_DIR, тому я можу сказати, де зберігати свої файли журналів.
slypete

@slypete: ми не використовуємо налаштування в logging.ini. Оскільки журнал даних значною мірою не залежить, ми не використовуємо жодного з параметрів Django. Так, є можливість повторити щось. Ні, це не має особливих практичних значень.
S.Lott

У такому випадку я б окремий файл logging.ini мав би в кожній установці свого додатка.
slypete

@slypete: у вас є settings.py для кожної установки. У вас також є logging.ini для кожної установки. Крім того, у вас, ймовірно, є файл конф-файлів Apache для кожної установки. Плюс файл інтерфейсу wsgi. Я не впевнений, у чому твій погляд.
С.Лотт

6

Зараз я використовую систему лісозаготівлі, яку створив сам. Він використовує формат CSV для ведення журналів.

django-csvlog

Цей проект ще не має повної документації, але я над ним працюю.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.