Django логирование ошибок

  • Getting Help

  • el

  • es

  • fr

  • id

  • it

  • ja

  • ko

  • pl

  • pt-br

  • zh-hans

  • Language: en
  • 4.0

  • 4.2

  • dev

  • Documentation version:
    4.1

Logging¶

Django’s logging module extends Python’s builtin logging.

Logging is configured as part of the general Django django.setup()
function, so it’s always available unless explicitly disabled.

Django’s default logging configuration¶

By default, Django uses Python’s logging.config.dictConfig format.

Default logging conditions¶

The full set of default logging conditions are:

When DEBUG is True:

  • The django logger sends messages in the django hierarchy (except
    django.server) at the INFO level or higher to the console.

When DEBUG is False:

  • The django logger sends messages in the django hierarchy (except
    django.server) with ERROR or CRITICAL level to
    AdminEmailHandler.

Independently of the value of DEBUG:

  • The django.server logger sends messages at the INFO level
    or higher to the console.

All loggers except django.server propagate logging to their
parents, up to the root django logger. The console and mail_admins
handlers are attached to the root logger to provide the behavior described
above.

Python’s own defaults send records of level WARNING and higher
to the console.

Default logging definition¶

Django’s default logging configuration inherits Python’s defaults. It’s
available as django.utils.log.DEFAULT_LOGGING and defined in
django/utils/log.py:

{
    'version': 1,
    'disable_existing_loggers': False,
    'filters': {
        'require_debug_false': {
            '()': 'django.utils.log.RequireDebugFalse',
        },
        'require_debug_true': {
            '()': 'django.utils.log.RequireDebugTrue',
        },
    },
    'formatters': {
        'django.server': {
            '()': 'django.utils.log.ServerFormatter',
            'format': '[{server_time}] {message}',
            'style': '{',
        }
    },
    'handlers': {
        'console': {
            'level': 'INFO',
            'filters': ['require_debug_true'],
            'class': 'logging.StreamHandler',
        },
        'django.server': {
            'level': 'INFO',
            'class': 'logging.StreamHandler',
            'formatter': 'django.server',
        },
        'mail_admins': {
            'level': 'ERROR',
            'filters': ['require_debug_false'],
            'class': 'django.utils.log.AdminEmailHandler'
        }
    },
    'loggers': {
        'django': {
            'handlers': ['console', 'mail_admins'],
            'level': 'INFO',
        },
        'django.server': {
            'handlers': ['django.server'],
            'level': 'INFO',
            'propagate': False,
        },
    }
}

See Configuring logging on how to complement or replace this default
logging configuration.

Django logging extensions¶

Django provides a number of utilities to handle the particular requirements of
logging in a web server environment.

Loggers¶

Django provides several built-in loggers.

django

The parent logger for messages in the django named logger hierarchy. Django does not post messages using this name.
Instead, it uses one of the loggers below.

django.request

Log messages related to the handling of requests. 5XX responses are
raised as ERROR messages; 4XX responses are raised as WARNING
messages. Requests that are logged to the django.security logger aren’t
logged to django.request.

Messages to this logger have the following extra context:

  • status_code: The HTTP response code associated with the request.
  • request: The request object that generated the logging message.

django.server

Log messages related to the handling of requests received by the server invoked
by the runserver command. HTTP 5XX responses are logged as ERROR
messages, 4XX responses are logged as WARNING messages, and everything else
is logged as INFO.

Messages to this logger have the following extra context:

  • status_code: The HTTP response code associated with the request.
  • request: The request object that generated the logging message.

django.template

Log messages related to the rendering of templates.

  • Missing context variables are logged as DEBUG messages.

django.db.backends

Messages relating to the interaction of code with the database. For example,
every application-level SQL statement executed by a request is logged at the
DEBUG level to this logger.

Messages to this logger have the following extra context:

  • duration: The time taken to execute the SQL statement.
  • sql: The SQL statement that was executed.
  • params: The parameters that were used in the SQL call.
  • alias: The alias of the database used in the SQL call.

For performance reasons, SQL logging is only enabled when
settings.DEBUG is set to True, regardless of the logging
level or handlers that are installed.

This logging does not include framework-level initialization (e.g.
SET TIMEZONE) or transaction management queries (e.g. BEGIN,
COMMIT, and ROLLBACK). Turn on query logging in your database if you
wish to view all database queries.

Changed in Django 4.0:

The database alias was added to log messages.

django.security.*

The security loggers will receive messages on any occurrence of
SuspiciousOperation and other security-related
errors. There is a sub-logger for each subtype of security error, including all
SuspiciousOperations. The level of the log event depends on where the
exception is handled. Most occurrences are logged as a warning, while
any SuspiciousOperation that reaches the WSGI handler will be logged as an
error. For example, when an HTTP Host header is included in a request from
a client that does not match ALLOWED_HOSTS, Django will return a 400
response, and an error message will be logged to the
django.security.DisallowedHost logger.

These log events will reach the django logger by default, which mails error
events to admins when DEBUG=False. Requests resulting in a 400 response due
to a SuspiciousOperation will not be logged to the django.request
logger, but only to the django.security logger.

To silence a particular type of SuspiciousOperation, you can override that
specific logger following this example:

'handlers': {
    'null': {
        'class': 'logging.NullHandler',
    },
},
'loggers': {
    'django.security.DisallowedHost': {
        'handlers': ['null'],
        'propagate': False,
    },
},

Other django.security loggers not based on SuspiciousOperation are:

  • django.security.csrf: For CSRF failures.

django.db.backends.schema

Logs the SQL queries that are executed during schema changes to the database by
the migrations framework. Note that it won’t log the
queries executed by RunPython.
Messages to this logger have params and sql in their extra context (but
unlike django.db.backends, not duration). The values have the same meaning
as explained in django.db.backends.

Handlers¶

Django provides one log handler in addition to those provided by the
Python logging module
.

class AdminEmailHandler(include_html=False, email_backend=None, reporter_class=None

This handler sends an email to the site ADMINS for each log
message it receives.

If the log record contains a request attribute, the full details
of the request will be included in the email. The email subject will
include the phrase “internal IP” if the client’s IP address is in the
INTERNAL_IPS setting; if not, it will include “EXTERNAL IP”.

If the log record contains stack trace information, that stack
trace will be included in the email.

The include_html argument of AdminEmailHandler is used to
control whether the traceback email includes an HTML attachment
containing the full content of the debug web page that would have been
produced if DEBUG were True. To set this value in your
configuration, include it in the handler definition for
django.utils.log.AdminEmailHandler, like this:

'handlers': {
    'mail_admins': {
        'level': 'ERROR',
        'class': 'django.utils.log.AdminEmailHandler',
        'include_html': True,
    },
},

Be aware of the security implications of logging when using the AdminEmailHandler.

By setting the email_backend argument of AdminEmailHandler, the
email backend that is being used by the
handler can be overridden, like this:

'handlers': {
    'mail_admins': {
        'level': 'ERROR',
        'class': 'django.utils.log.AdminEmailHandler',
        'email_backend': 'django.core.mail.backends.filebased.EmailBackend',
    },
},

By default, an instance of the email backend specified in
EMAIL_BACKEND will be used.

The reporter_class argument of AdminEmailHandler allows providing
an django.views.debug.ExceptionReporter subclass to customize the
traceback text sent in the email body. You provide a string import path to
the class you wish to use, like this:

'handlers': {
    'mail_admins': {
        'level': 'ERROR',
        'class': 'django.utils.log.AdminEmailHandler',
        'include_html': True,
        'reporter_class': 'somepackage.error_reporter.CustomErrorReporter',
    },
},
send_mail(subject, message, *args, **kwargs

Sends emails to admin users. To customize this behavior, you can
subclass the AdminEmailHandler class and
override this method.

Filters¶

Django provides some log filters in addition to those provided by the Python
logging module.

class CallbackFilter(callback

This filter accepts a callback function (which should accept a single
argument, the record to be logged), and calls it for each record that
passes through the filter. Handling of that record will not proceed if the
callback returns False.

For instance, to filter out UnreadablePostError
(raised when a user cancels an upload) from the admin emails, you would
create a filter function:

from django.http import UnreadablePostError

def skip_unreadable_post(record):
    if record.exc_info:
        exc_type, exc_value = record.exc_info[:2]
        if isinstance(exc_value, UnreadablePostError):
            return False
    return True

and then add it to your logging config:

'filters': {
    'skip_unreadable_posts': {
        '()': 'django.utils.log.CallbackFilter',
        'callback': skip_unreadable_post,
    },
},
'handlers': {
    'mail_admins': {
        'level': 'ERROR',
        'filters': ['skip_unreadable_posts'],
        'class': 'django.utils.log.AdminEmailHandler',
    },
},
class RequireDebugFalse

This filter will only pass on records when settings.DEBUG is False.

This filter is used as follows in the default LOGGING
configuration to ensure that the AdminEmailHandler only sends
error emails to admins when DEBUG is False:

'filters': {
    'require_debug_false': {
        '()': 'django.utils.log.RequireDebugFalse',
    },
},
'handlers': {
    'mail_admins': {
        'level': 'ERROR',
        'filters': ['require_debug_false'],
        'class': 'django.utils.log.AdminEmailHandler',
    },
},
class RequireDebugTrue

This filter is similar to RequireDebugFalse, except that records are
passed only when DEBUG is True.

Back to Top

Logging¶

Python programmers will often use print() in their code as a quick and
convenient debugging tool. Using the logging framework is only a little more
effort than that, but it’s much more elegant and flexible. As well as being
useful for debugging, logging can also provide you with more — and better
structured — information about the state and health of your application.

Overview¶

Django uses and extends Python’s builtin logging module to perform
system logging. This module is discussed in detail in Python’s own
documentation; this section provides a quick overview.

The cast of players¶

A Python logging configuration consists of four parts:

  • Loggers

  • Handlers

  • Filters

  • Formatters

Loggers¶

A logger is the entry point into the logging system. Each logger is a named
bucket to which messages can be written for processing.

A logger is configured to have a log level. This log level describes
the severity of the messages that the logger will handle. Python
defines the following log levels:

  • DEBUG: Low level system information for debugging purposes

  • INFO: General system information

  • WARNING: Information describing a minor problem that has
    occurred.

  • ERROR: Information describing a major problem that has
    occurred.

  • CRITICAL: Information describing a critical problem that has
    occurred.

Each message that is written to the logger is a Log Record. Each log
record also has a log level indicating the severity of that specific
message. A log record can also contain useful metadata that describes
the event that is being logged. This can include details such as a
stack trace or an error code.

When a message is given to the logger, the log level of the message is
compared to the log level of the logger. If the log level of the
message meets or exceeds the log level of the logger itself, the
message will undergo further processing. If it doesn’t, the message
will be ignored.

Once a logger has determined that a message needs to be processed,
it is passed to a Handler.

Handlers¶

The handler is the engine that determines what happens to each message
in a logger. It describes a particular logging behavior, such as
writing a message to the screen, to a file, or to a network socket.

Like loggers, handlers also have a log level. If the log level of a
log record doesn’t meet or exceed the level of the handler, the
handler will ignore the message.

A logger can have multiple handlers, and each handler can have a
different log level. In this way, it is possible to provide different
forms of notification depending on the importance of a message. For
example, you could install one handler that forwards ERROR and
CRITICAL messages to a paging service, while a second handler
logs all messages (including ERROR and CRITICAL messages) to a
file for later analysis.

Filters¶

A filter is used to provide additional control over which log records
are passed from logger to handler.

By default, any log message that meets log level requirements will be
handled. However, by installing a filter, you can place additional
criteria on the logging process. For example, you could install a
filter that only allows ERROR messages from a particular source to
be emitted.

Filters can also be used to modify the logging record prior to being
emitted. For example, you could write a filter that downgrades
ERROR log records to WARNING records if a particular set of
criteria are met.

Filters can be installed on loggers or on handlers; multiple filters
can be used in a chain to perform multiple filtering actions.

Formatters¶

Ultimately, a log record needs to be rendered as text. Formatters
describe the exact format of that text. A formatter usually consists
of a Python formatting string containing
LogRecord attributes; however,
you can also write custom formatters to implement specific formatting behavior.

Security implications¶

The logging system handles potentially sensitive information. For example, the
log record may contain information about a web request or a stack trace, while
some of the data you collect in your own loggers may also have security
implications. You need to be sure you know:

  • what information is collected

  • where it will subsequently be stored

  • how it will be transferred

  • who might have access to it.

To help control the collection of sensitive information, you can explicitly
designate certain sensitive information to be filtered out of error reports –
read more about how to filter error reports.

AdminEmailHandler

The built-in AdminEmailHandler deserves a mention in
the context of security. If its include_html option is enabled, the email
message it sends will contain a full traceback, with names and values of local
variables at each level of the stack, plus the values of your Django settings
(in other words, the same level of detail that is exposed in a web page when
DEBUG is True).

It’s generally not considered a good idea to send such potentially sensitive
information over email. Consider instead using one of the many third-party
services to which detailed logs can be sent to get the best of multiple worlds
– the rich information of full tracebacks, clear management of who is notified
and has access to the information, and so on.

Configuring logging¶

Python’s logging library provides several techniques to configure
logging, ranging from a programmatic interface to configuration files.
By default, Django uses the dictConfig format.

In order to configure logging, you use LOGGING to define a
dictionary of logging settings. These settings describe the loggers,
handlers, filters and formatters that you want in your logging setup,
and the log levels and other properties that you want those components
to have.

By default, the LOGGING setting is merged with Django’s
default logging configuration
using the
following scheme.

If the disable_existing_loggers key in the LOGGING dictConfig is
set to True (which is the dictConfig default if the key is missing)
then all loggers from the default configuration will be disabled. Disabled
loggers are not the same as removed; the logger will still exist, but will
silently discard anything logged to it, not even propagating entries to a
parent logger. Thus you should be very careful using
'disable_existing_loggers': True; it’s probably not what you want. Instead,
you can set disable_existing_loggers to False and redefine some or all
of the default loggers; or you can set LOGGING_CONFIG to None
and handle logging config yourself.

Logging is configured as part of the general Django setup() function.
Therefore, you can be certain that loggers are always ready for use in your
project code.

Examples¶

The full documentation for dictConfig format
is the best source of information about logging configuration dictionaries.
However, to give you a taste of what is possible, here are several examples.

To begin, here’s a small configuration that will allow you to output all log
messages to the console:

settings.py

import os

LOGGING = {
    "version": 1,
    "disable_existing_loggers": False,
    "handlers": {
        "console": {
            "class": "logging.StreamHandler",
        },
    },
    "root": {
        "handlers": ["console"],
        "level": "WARNING",
    },
}

This configures the parent root logger to send messages with the
WARNING level and higher to the console handler. By adjusting the level to
INFO or DEBUG you can display more messages. This may be useful during
development.

Next we can add more fine-grained logging. Here’s an example of how to make the
logging system print more messages from just the django named
logger:

settings.py

import os

LOGGING = {
    "version": 1,
    "disable_existing_loggers": False,
    "handlers": {
        "console": {
            "class": "logging.StreamHandler",
        },
    },
    "root": {
        "handlers": ["console"],
        "level": "WARNING",
    },
    "loggers": {
        "django": {
            "handlers": ["console"],
            "level": os.getenv("DJANGO_LOG_LEVEL", "INFO"),
            "propagate": False,
        },
    },
}

By default, this config sends messages from the django logger of level
INFO or higher to the console. This is the same level as Django’s default
logging config, except that the default config only displays log records when
DEBUG=True. Django does not log many such INFO level messages. With
this config, however, you can also set the environment variable
DJANGO_LOG_LEVEL=DEBUG to see all of Django’s debug logging which is very
verbose as it includes all database queries.

You don’t have to log to the console. Here’s a configuration which writes all
logging from the django named logger to a local file:

settings.py

LOGGING = {
    "version": 1,
    "disable_existing_loggers": False,
    "handlers": {
        "file": {
            "level": "DEBUG",
            "class": "logging.FileHandler",
            "filename": "/path/to/django/debug.log",
        },
    },
    "loggers": {
        "django": {
            "handlers": ["file"],
            "level": "DEBUG",
            "propagate": True,
        },
    },
}

If you use this example, be sure to change the 'filename' path to a
location that’s writable by the user that’s running the Django application.

Finally, here’s an example of a fairly complex logging setup:

settings.py

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": {
        "special": {
            "()": "project.logging.SpecialFilter",
            "foo": "bar",
        },
        "require_debug_true": {
            "()": "django.utils.log.RequireDebugTrue",
        },
    },
    "handlers": {
        "console": {
            "level": "INFO",
            "filters": ["require_debug_true"],
            "class": "logging.StreamHandler",
            "formatter": "simple",
        },
        "mail_admins": {
            "level": "ERROR",
            "class": "django.utils.log.AdminEmailHandler",
            "filters": ["special"],
        },
    },
    "loggers": {
        "django": {
            "handlers": ["console"],
            "propagate": True,
        },
        "django.request": {
            "handlers": ["mail_admins"],
            "level": "ERROR",
            "propagate": False,
        },
        "myproject.custom": {
            "handlers": ["console", "mail_admins"],
            "level": "INFO",
            "filters": ["special"],
        },
    },
}

This logging configuration does the following things:

  • Identifies the configuration as being in ‘dictConfig version 1’
    format. At present, this is the only dictConfig format version.

  • Defines two formatters:

    • simple, that outputs the log level name (e.g., DEBUG) and the log
      message.

      The format string is a normal Python formatting string
      describing the details that are to be output on each logging
      line. The full list of detail that can be output can be
      found in Formatter Objects.

    • verbose, that outputs the log level name, the log
      message, plus the time, process, thread and module that
      generate the log message.

  • Defines two filters:

    • project.logging.SpecialFilter, using the alias special. If this
      filter required additional arguments, they can be provided as additional
      keys in the filter configuration dictionary. In this case, the argument
      foo will be given a value of bar when instantiating
      SpecialFilter.

    • django.utils.log.RequireDebugTrue, which passes on records when
      DEBUG is True.

  • Defines two handlers:

    • console, a StreamHandler, which prints any INFO
      (or higher) message to sys.stderr. This handler uses the simple
      output format.

    • mail_admins, an AdminEmailHandler, which
      emails any ERROR (or higher) message to the site ADMINS.
      This handler uses the special filter.

  • Configures three loggers:

    • django, which passes all messages to the console handler.

    • django.request, which passes all ERROR messages to
      the mail_admins handler. In addition, this logger is
      marked to not propagate messages. This means that log
      messages written to django.request will not be handled
      by the django logger.

    • myproject.custom, which passes all messages at INFO
      or higher that also pass the special filter to two
      handlers – the console, and mail_admins. This
      means that all INFO level messages (or higher) will be
      printed to the console; ERROR and CRITICAL
      messages will also be output via email.

Custom logging configuration¶

If you don’t want to use Python’s dictConfig format to configure your
logger, you can specify your own configuration scheme.

The LOGGING_CONFIG setting defines the callable that will
be used to configure Django’s loggers. By default, it points at
Python’s logging.config.dictConfig() function. However, if you want to
use a different configuration process, you can use any other callable
that takes a single argument. The contents of LOGGING will
be provided as the value of that argument when logging is configured.

Disabling logging configuration¶

If you don’t want to configure logging at all (or you want to manually
configure logging using your own approach), you can set
LOGGING_CONFIG to None. This will disable the
configuration process for Django’s default logging.

Setting LOGGING_CONFIG to None only means that the automatic
configuration process is disabled, not logging itself. If you disable the
configuration process, Django will still make logging calls, falling back to
whatever default logging behavior is defined.

Here’s an example that disables Django’s logging configuration and then
manually configures logging:

settings.py

LOGGING_CONFIG = None

import logging.config

logging.config.dictConfig(...)

Note that the default configuration process only calls
LOGGING_CONFIG once settings are fully-loaded. In contrast, manually
configuring the logging in your settings file will load your logging config
immediately. As such, your logging config must appear after any settings on
which it depends.

Django – один из самых популярных веб фреймворков Python, используемый многими организациями. Поскольку он использует встроенное средство ведения логов Python logging, использование логов в Django совсем несложная задача.

В этой статье мы создадим небольшое тестовое приложение которое поможет мне рассказать о том как настраивать логгирование в Django. Мы сделаем следующее:

  • создадим виртуальную среду Python,
  • создадим небольшой проект Django,
  • напишем базовый пример инициирования события,
  • настроим логгирование
  • исследуем стандартные логгеры
  • рассмотрим настройку почтовых сообщений
  • рассмотрим чуть более сложный пример
  • и в заключение рассмотрим возможность раскрашивание консоли

Создание виртуальной среды

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

Поскольку в этой статье мы будем использовать Python 3, перед созданием виртуальной среды Python проверьте, что вы используете версию Python не ниже 3.6.1:

$ python3 --version
Python 3.7.0

Затем создайте каталог, чтобы сохранить наш проект:

$ mkdir test && cd $_

Отлично. Прежде чем перейти к следующему разделу, давайте создадим и активируем нашу новую виртуальную среду:

test$ mkvirtualenv loggers --python=python3 
Using base prefix '/usr/local/Cellar/python/3.7.0/Frameworks/Python.framework/Versions/3.7' 
New python executable in /Users/casey/projects/django-logging/env/bin/python3.7 
Also creating executable in /Users/casey/projects/django-logging/env/bin/python 
Installing setuptools, pip, wheel... done.
loggers$ source env/bin/activate
(env) test$

Теперь все хорошо, перейдем к установке Django.

В виртуальной среде установим Django:

(env) test$ pip install Django
Collecting Django
  Using cached https://files.pythonhosted.org/packages/d1/e5/2676be45ea49cfd09a663f289376b3888accd57ff06c953297bfdee1fb08/Django-2.1.3-py3-none-any.whl
Collecting pytz (from Django)
  Using cached https://files.pythonhosted.org/packages/f8/0e/2365ddc010afb3d79147f1dd544e5ee24bf4ece58ab99b16fbb465ce6dc0/pytz-2018.7-py2.py3-none-any.whl
Installing collected packages: pytz, Django
Successfully installed Django-2.1.3 pytz-2018.7

Проверим как установилась Django выводом версии:

(env) test$ python -m django --version
3.0.8

Теперь перейдем к созданию простого приложения loggers.

Создание простого приложение

Для этой статьи наш проект не будет чем-то необычным – простой обработчик одной веб-страницы, которая будет испытательным стендом для логгирования сообщений.

Команда django-admin создаст для нас начальный скелет приложения:

(env) test$ django-admin startproject loggers

в вашем каталоге test вы увидите новую папку приложения:

(env) test$ ls
loggers

В качестве последнего шага, чтобы убедиться, что все работает, перейдем в каталог test и запустим встроенный веб-сервер Django:

(env) test$ cd loggers
(env) test$ python manage.py runserver
Performing system checks...

System check identified no issues (0 silenced).

You have 17 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions.
Run 'python manage.py migrate' to apply them.

July 30, 2020 - 10:00:12
Django version 3.0.8, using settings 'loggers.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.

Видите это страшное сообщение о 17 непримененных миграциях? Просто игнорируй это. Откройте браузер и перейдите на http://localhost:8000, и вы должны увидеть веб-страницу Django по умолчанию

Добавление нового модуля Django

Теперь, когда у нас есть главный каркас приложения, давайте создадим модуль с именем hello:

(env) test$ python manage.py startapp hello

Эта команда создаст новую папку с именем hello вместе с несколькими дополнительными файлами Python. Замените содержимое hello/views.py следующим:

from django.http import HttpResponse

def index(request):
    return HttpResponse("Hello logging world.")

Это создает наше первоначальное представление (view), но нам все еще нужно сообщить об этом Django. Создайте новый файл в hello/urls.py и добавьте в него следующее:

from django.urls import path

from . import views

urlpatterns = [
    path('', views.index, name='index'),
]

Это связывает наше представление с базовым URL. Теперь все, что нам нужно сделать, это подключить его к файлу loggers/urls.py, чтобы Django знал, как все маршрутизировать. Откройте loggers/urls.py и замените его содержимое следующим:

from django.contrib import admin
from django.urls import include, path

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('hello.urls'))
]

Затем запустите веб-сервер, как мы делали раньше:

(env) test$ python manage.py runserver

Вместо стандартного экрана приветствия Django, мы увидим наш вывод строки “Hello logging world.”

Теперь, когда у нас есть простое приложение, давайте посмотрим, как включить логгирование в Django.

Первые шаги с логгированием

Самое простое для включения логирования, которое мы можем сделать, это просто импортировать модуль Python logging и начать регистрацию. Начните с обновления файла app/views.py следующим кодом:

import logging

from django.http import HttpResponse

logger = logging.getLogger(__name__)

def index(request):

    logger.error("Test!!")

    return HttpResponse("Hello logging world.")

Здесь мы импортируем модуль logging и затем используем метод getLogger, чтобы получить экземпляр logging, который мы можем использовать. В методе index мы отправляем наше сообщение в журнал, используя метод logger.error. Всего существует 5 методов записи логов соответствующих уровню сообщений: logger.debug, logger.error, logger.warning, logger.info, logger.critical. И два базовых метода: logger.log и logger.exception

Перезапустите веб-сервер и обновите страницу, чтобы увидеть наше сообщение в окне консоли:

(env) test$ python manage.py runserver
Performing system checks...

System check identified no issues (0 silenced).

You have 15 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions.
Run 'python manage.py migrate' to apply them.


July 30, 2020 - 10:00:12
Django version 3.0.8, using settings 'loggers.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
Test!!
[30/Jul/2020 02:11:35] "GET / HTTP/1.1" 200 20
Not Found: /favicon.ico
[30/Jul/2020 02:11:35] "GET /favicon.ico HTTP/1.1" 404 2024

Настройка логгирования в Django

Мы можем изменить настройки логирование непосредственно в представление (view), сразу после импоритирования. По умолчанию Django использует формат dictConfig  для настройки handlers, loggers, filters и formatters. Эти настройки задаются с помощью словаря LOGGING, который объединяется с конфигурацией ведения журнала Django по умолчанию. Выглядеть это будет следующим образом:

import logging

logging.config.dictConfig({
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'console': {
            'format': '%(name)-12s %(levelname)-8s %(message)s'
        },
        'file': {
            'format': '%(asctime)s %(name)-12s %(levelname)-8s %(message)s'
        }
    },
    'handlers': {
        'console': {
            'class': 'logging.StreamHandler',
            'formatter': 'console'
        },
        'file': {
            'level': 'DEBUG',
            'class': 'logging.FileHandler',
            'formatter': 'file',
            'filename': 'debug.log'
        }
    },
    'loggers': {
        '': {
            'level': 'DEBUG',
            'handlers': ['console', 'file']
        }
    }
})

Или что бы настройки логгирования применились для всего проекта лучше всего это сделать в файле настроек loggers/settings.py. Просто добавив в конец файла следующие строки:

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'console': {
            'format': '%(name)-12s %(levelname)-8s %(message)s'
        },
        'file': {
            'format': '%(asctime)s %(name)-12s %(levelname)-8s %(message)s'
        }
    },
    'handlers': {
        'console': {
            'class': 'logging.StreamHandler',
            'formatter': 'console'
        },
        'file': {
            'level': 'DEBUG',
            'class': 'logging.FileHandler',
            'formatter': 'file',
            'filename': 'debug.log'
        }
    },
    'loggers': {
        '': {
            'level': 'DEBUG',
            'handlers': ['console', 'file']
        }
    }
}

Рассмотрим это настройки более подробно.

version

Схема dictConfig не требует версии, но ее установка обеспечивает обратную совместимость. В настоящее время единственным допустимым значением здесь является 1.

disable_existing_loggers

Этот параметр отключает существующие логгеры. По умолчанию Django использует некоторые из своих собственных логеров. Эти логгеры связаны с Django ORM и другими внутренними частями Django. Значением по умолчанию является False, но не помешает указывать это явно. При установке этого значения в True нужно указать дополнительные логгеры . В противном случае сообщения могут быть потеряны.

formatters (форматы)

Этот раздел определяет, как будут выглядеть строки в логгах. Мы не будем рассматривать все возможные варианты форматирования. В этой статье описывается все, что поддерживается. Давайте посмотрим на формат журнала консоли:

%(name)-12s %(levelname)-8s %(message)s

Вот что здесь используется:

  • %(name) – это имя пакета, которое выдает сообщение журнала
  • %(levelname) – степень важности сообщения (ERROR, WARNING, INFO, и т.д.)
  • %(message) – само сообщение

-12s и -8s управляют интервалом между различными спецификациями формата. Сообщения в этом формате будет выглядеть следующим образом:

hello.views  ERROR    Test!!

Вы можете заметить, что formatter в file содержит дополнительный спецификатор %(asctime). Это добавляет временную метку к каждому сообщению, отправляемому через обработчик file. В качестве примера, сообщение из нашего определенного debug.log будет выглядеть так:

2020-11-29 14:36:58,739 hello.views  ERROR    Test!!

handlers (обработчики)

Этот раздел определяет, как обрабатывать сообщения журнала. Мы можем отправлять сообщения на консоль, файлы или даже в сокеты! Все обработчики по умолчанию описаны в документации по Python здесь. Эта конфигурация определяет отдельные обработчики: один для сообщений консоли и другой для сообщений, отправляемых в файл. Кроме того, для обработчика file мы указываем класс обработчика, имя файла и log level. В этом обработчике также указано, какой formatter нужно использовать.

loggers (логгеры)

В этом разделе мы связываем все это вместе и определяем наши логгеры, включая способы обработки сообщений корневого логгера – root logger (представленного пустой строкой). Мы также устанавливаем уровень ведения журнала logging level – DEBUG и определяем два обработчика, один для консоли и один для файла.

В нашем примере мы не использовали один параметр который необходимо упомянуть здесь это filters

filters (фильтры)

Фильтры предоставляют дополнительный контроль над тем, какие сообщения будут переданы из логгера в обработчик. По умолчанию все сообщения, прошедшие проверку уровня логгирования, будут переданы в обработчик. Добавив фильтры вы можете определить дополнительные правила проверки при обработке сообщений. Например, вы можете добавить фильтр, который позволяет обрабатывать ERROR сообщения отправленные определенным источником.

Фильтры также могу изменить сообщение. Например, вы можете создать фильтр, который изменяет уровень логгирования определенных сообщения с ERROR на WARNING.

Фильтр могут быть добавлены к логгеру или обработчику, можно использовать несколько фильтров.

Мы рассмотрим использование фильтров в разделе настройки почтовых сообщений

Давайте сейчас обновим конфигурацию и посмотрим что получилось.

Тестирование новой конфигурации

Обновите страницу в браузере, теперь сообщение журнала консоли содержит дополнительную информацию из строки формата:

$ python manage.py runserver
...

July 30, 2020 - 10:00:12
Django version 3.0.8, using settings 'loggers.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
hello.views  ERROR    Test!!

Файл журнала будет содержать то же сообщение и формат, но с добавлением отметки времени:

(env) test$ cat debug.log 
2020-07-30 14:36:58,739 hello.views  ERROR    Test!!

Этого достаточно, чтобы включить логгирование в любом проекте Django. Сейчас вы можете самостоятельно поиграться с настройками обработчиков и логгеров в созданном тестовом приложение.

Далее, рассмотрим более подробно стандартные логгеры (logging) для ведения логов, которые предоставляет Django.

Стандартные логгеры

В среде веб-сервера у нас часто есть рутинная информация, которую мы должны регистрировать. Django предоставляет несколько стандартных логгеров (loggers) для ведения логов.

django

Это универсальный логгер принимающий все сообщения. Сообщения не записываются непосредственно в этот логгер поэтому нужно использовать один из следующих логгеров.

django.request

Этот логгер обрабатывает все сообщения вызванные HTTP-запросами и вызывает исключения для определенных кодов состояния. Все коды ошибок HTTP 5xx будут вызывать сообщения об ERROR. Аналогичным образом, коды HTTP 4xx будут отображаться в виде WARNING.

django.server

Когда сервер запускается с помощью команды runserver (что мы и делали), он будет регистрировать сообщения, связанные с обработкой этих запросов, точно так же, как логгер django.request, за исключением того, что все другие сообщения регистрируются на уровне INFO.

django.template

Независимо от того, как часто я работал с шаблонами Django, я никогда не собирал их без ошибок с первого раза. Логгер django.template обрабатывает ошибки, связанные с отображением шаблонов.

django.db.backends

Этот логгер обрабатывает любые сообщения связанные с взаимодействием кода с базой данных. Все операторы SQL уровня приложения будут отображаться на уровне DEBUG. Поскольку это может быть довольно шумно, необходимо включить ведение журнала отладки, изменив settings.DEBUG на True.

django.security.*

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

Примечание. Если сообщение будет обрабатываться django.security то оно не будет отправлено через логгер django.request. Так что не игнорируйте сообщения от логгера django.security. *!

django.security.csrf

Этот логгер не наследуется от SuspiciousOperation. Он обрабатывает любые исключения, которые происходят из-за атак Cross-Site Request Forgery (CSRF).

И так в качестве следующего примера давайте запишем сообщения с помощью логгера django.request. Это делается путем добавления логгера в раздел регистрации конфигурации следующим образом:

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'console': {
            'format': '%(name)-12s %(levelname)-8s %(message)s'
        },
        'file': {
            'format': '%(asctime)s %(name)-12s %(levelname)-8s %(message)s'
        }
    },
    'handlers': {
        'console': {
            'class': 'logging.StreamHandler',
            'formatter': 'console'
        },
        'file': {
            'level': 'DEBUG',
            'class': 'logging.FileHandler',
            'formatter': 'file',
            'filename': 'debug.log'
        }
    },
    'loggers': {
        '': {
            'level': 'DEBUG',
            'handlers': ['console', 'file'],
            'propagate': True
        },
        'django.request': {
            'level': 'DEBUG',
            'handlers': ['console', 'file']
        }
    }
})

Это будет регистрировать сообщения-запросы, подобные следующим:

2020-07-29 13:58:55,044 django.request WARNING Not Found: /

Обратите внимание на использование параметра propagate. Этим параметром регулируется возможность передачи сообщения другим логгерам. Если оно установлено в False то дальше сообщение не пойдет.

Так же для каждого модуля в вашем приложение вы можете описать свой логгер (например для ведение отдельных файлов). Это делается следующим образом:

...
    'loggers': {
    ...
        'loggers.hello': {
            'level': 'DEBUG',
            'handlers': ['console',],
            'propagate': True
        },
    },

Настройка отправки сообщений об ошибках по email

После того как вы настроите регистрации сообщений об ошибках в логах, вы скорее всего не станете просматривать логи каждый день с целью поиска ошибок. Поэтому многие ошибки могут остаться не замеченными. Вот почему обязательно нужно настраивать отправку сообщений об ошибках по email. Для этого настроим специальный handler mail_admins следующим образом:

...
'handlers': {
        ...
       
        'mail_admins': {
            'level': 'DEBUG',
            'class': 'django.utils.log.AdminEmailHandler',
            'include_html': True,
        },
...

что бы он работал нужно добавить настройки отправки почты также в файл settings.py (в нашем примере мы будет использовать сервер yandex, но можно использовать любой другой, с соотвествующими настройками)

ADMINS = (
    ('admin', 'admin@example.com'),
)
...
EMAIL_SUBJECT_PREFIX = '[SuperService] '
EMAIL_HOST = 'smtp.yandex.ru'
EMAIL_HOST_USER = 'user@example.ru'
EMAIL_HOST_PASSWORD = 'XXXXXX'
EMAIL_PORT = 465
EMAIL_USE_TLS = False
EMAIL_USE_SSL = True

SERVER_EMAIL = 'user@example.ru'
DEFAULT_FROM_EMAIL = 'user@example.ru'
...

Имена используемых параметров сами говорят за себя их предназначение. Очень важно понимать что бы все заработало сервер на котором запускается Django должен уметь отправлять почту (на нем должна быть настроена отправка почты по SMTP). Настройка отправки почты на сервере выходит за рамки данной статьи. Что бы немного облегчить поиск проблем связанных с настройкой отправки почты на сервере можно имитировать отправку почты Django. То есть вместо реальной отправки писем записывать их в локальный файл. Для этого нужно указать соотвествующий почтовой бекенд email_backend и файл для записи в настройках.

...
EMAIL_FILE_PATH = 'email-messages'
...
LOGGING = {
...
   'mail_admins': {
            'level': 'DEBUG',
            'class': 'django.utils.log.AdminEmailHandler',
            'include_html': True,
            'email_backend': 'django.core.mail.backends.filebased.EmailBackend',
        },
...

Так же можно добавить фильтр django.utils.log.RequireDebugFalse, который сделает так что бы письма отправлялись только когда отключен режим DEBUG то есть установлен DEBUG=False

...
    'filters': {
        'require_debug_false': {
            '()': 'django.utils.log.RequireDebugFalse'
        }
    },
...
    'handlers': {
    ...
       'mail_admins': {
            'level': 'DEBUG',
            'filters': ['require_debug_false'],
            'class': 'django.utils.log.AdminEmailHandler',
            'include_html': True,
        },
    }
...

Если по каким то причинам почта не отправляется, можно поискать причины отправив тестовое письмо вручную, через следующую функцию:

from django.core.mail import send_mail
...
send_mail(
        subject = "Test Email",
        message = "This is a test email",
        from_email = None,   # This will have no effect is you have set DEFAULT_FROM_EMAIL in settings.py
        recipient_list = ['user@example.com'], 
        fail_silently = False   
    )

Еще один пример конфигурации логгирования

В заключение хочу привести еще одну конфигурацию указанную в официальной документации и описать ее.

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': {
        'special': {
            '()': 'project.logging.SpecialFilter',
            'foo': 'bar',
        }
    },
    'handlers': {
        'null': {
            'level': 'DEBUG',
            'class': 'logging.NullHandler',
        },
        'console': {
            'level': 'DEBUG',
            'class': 'logging.StreamHandler',
            'formatter': 'simple'
        },
        'mail_admins': {
            'level': 'ERROR',
            'class': 'django.utils.log.AdminEmailHandler',
            'filters': ['special']
        }
    },
    'loggers': {
        'django': {
            'handlers': ['null'],
            'propagate': True,
            'level': 'INFO',
        },
        'django.request': {
            'handlers': ['mail_admins'],
            'level': 'ERROR',
            'propagate': False,
        },
        'myproject.custom': {
            'handlers': ['console', 'mail_admins'],
            'level': 'INFO',
            'filters': ['special']
        }
    }
}

Эта конфигурация выполняет следующее:

Определяет два formatters (форматера):

  • simple, просто возвращает уровень логгирования сообщения (например, DEBUG) и содержимое сообщения. Строка format является обычной строкой форматирования Python, описывающая детали, которые будут присутствовать в каждой строке журнала. Полный список переменных для форматирования вы можете найти в документации.
  • verbose, выведет уровень логгирования, сообщение, время, название процесса, потока и модуля, который создал сообщение.

Определяет один filters (фильтр) – project.logging.SpecialFilter с названием special. Если конструктор фильтра требует наличия дополнительных аргументов, вы можете указать их в словаре настройки фильтра. В этом случае будет передан аргумент foo со значением bar при создании экземпляра SpecialFilter.

Определяет три handlers (обработчика):

  • null, NullHandler, который отправляет все сообщения уровня DEBUG (или выше) в /dev/null.
  • console, StreamHandler, который перенаправляет все сообщения уровня DEBUG (и выше) в stderr. Этот обработчик использует формат simple.
  • mail_admins, AdminEmailHandler, который отправляет e-mail с сообщением уровня ERROR (и выше) администраторам сайта. Этот обработчик использует фильтр special.

Настраивает три loggers (логгера):

  • django, который перенаправляет все сообщения уровня INFO и выше в обработчик null.
  • django.request, который передает все сообщения уровня ERROR в обработчик mail_admins. Также указывается, что логгер не должен передавать сообщения родительским логгерам. Это означает что сообщения переданные в django.request не будут обрабатываться логгером django.
  • myproject.custom, который передает все сообщения уровня INFO и выше прошедшие фильтр special в два обработчика – console и mail_admins. Это означает что все сообщения уровня INFO (или выше) будут отправлены в консоль, сообщения ERROR и CRITICAL будут отосланы через e-mail.

Раскрашивание сообщений в консоли

В завершение я бы хотел рассмотреть еще один момент, включение цветной консоли. И хотя эта тема напрямую не связана с логгированием. Удобство чтения сообщений в консоли значительно улучшает и упрощает процесс поиска багов. Для того бы включить раскрашивание сообщений в консоли нужно в начале установить пакет rich (минимальная версия python 3.6.1) командой

pip install rich

а затем настройте handlers консоли на использование rich.logging.RichHandler вместо logging.StreamHandler

...
 "formatters": {"rich": {"datefmt": "[%X]"}},
...
 'handlers': {
        'console': {
            'class': 'rich.logging.RichHandler',
            'formatter': 'console'
        },
 ....

Ваша жизнь после этого станет намного ярче 🙂

Заключение

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

При написание статьи использовались следующие источники:

  • Getting started quickly with django logging
  • https://docs.djangoproject.com/en/3.0/topics/logging/
  • https://docs.djangoproject.com/en/3.0/topics/email/
  • https://www.willmcgugan.com/blog/tech/post/richer-django-logging/

Была ли вам полезна эта статья?

Программисты Python часто используют print() в своем коде как быстрый и удобный инструмент отладки. Использование фреймворка протоколирования требует лишь немного больше усилий, но оно гораздо более элегантно и гибко. Помимо полезности для отладки, протоколирование также может предоставить вам больше — и лучше структурированной — информации о состоянии и работоспособности вашего приложения.

Быстрый обзор¶

Django использует и расширяет встроенный модуль Python logging для ведения системного журнала. Этот модуль подробно рассматривается в собственной документации Python; в данном разделе представлен краткий обзор.

Состав игроков¶

Конфигурация ведения журнала Python состоит из четырех частей:

  • Логеры
  • Обработчики
  • Фильтры
  • Форматировщики

Логеры¶

Журнал logger — это точка входа в систему протоколирования. Каждый логгер представляет собой именованное ведро, в которое можно записывать сообщения для обработки.

Логер настроен на уровень журнала. Этот уровень журнала описывает серьезность сообщений, обрабатываемых логером. Python определяет следующие уровни журнала:

  • DEBUG: системная информация низкого уровня для отладки
  • INFO: Общая информация о системе
  • WARNING: Информация, описывающая возникшую незначительную проблему.
  • ERROR: Информация, описывающая возникшую серьезную проблему.
  • CRITICAL: Информация, описывающая возникшую критическую проблему.

Каждое сообщение, которое записывается в регистратор, является записью журнала. Каждая запись журнала также имеет уровень журнала, указывающий на серьезность данного конкретного сообщения. Запись журнала может также содержать полезные метаданные, описывающие регистрируемое событие. Это могут быть такие детали, как трассировка стека или код ошибки.

Когда сообщение поступает в регистратор, уровень журнала сообщения сравнивается с уровнем журнала регистратора. Если уровень журнала сообщения соответствует уровню журнала самого регистратора или превышает его, сообщение будет обработано. Если нет, то сообщение будет проигнорировано.

Как только регистратор определил, что сообщение должно быть обработано, оно передается в Handler.

Обработчики¶

Обработчик* — это механизм, определяющий, что происходит с каждым сообщением в регистраторе. Он описывает определенное поведение регистратора, например, запись сообщения на экран, в файл или в сетевой сокет.

Как и регистраторы, обработчики также имеют уровень журнала. Если уровень записи журнала не соответствует уровню обработчика или превышает его, обработчик проигнорирует сообщение.

У регистратора может быть несколько обработчиков, и каждый обработчик может иметь свой уровень регистрации. Таким образом, можно обеспечить различные формы уведомления в зависимости от важности сообщения. Например, можно установить один обработчик, который пересылает сообщения ERROR и CRITICAL в службу подкачки, а второй обработчик записывает все сообщения (включая ERROR и CRITICAL) в файл для последующего анализа.

Фильтры¶

Для обеспечения дополнительного контроля над тем, какие записи журнала передаются от регистратора к обработчику, используется фильтр.

По умолчанию обрабатывается любое сообщение журнала, отвечающее требованиям уровня журнала. Однако, установив фильтр, вы можете задать дополнительные критерии для процесса протоколирования. Например, вы можете установить фильтр, который позволяет выдавать только сообщения ERROR из определенного источника.

Фильтры также можно использовать для изменения записи журнала перед ее выдачей. Например, вы можете написать фильтр, который понижает уровень ERROR записей журнала до WARNING записей, если выполняется определенный набор критериев.

Фильтры можно устанавливать на регистраторы или на обработчики; несколько фильтров можно использовать в цепочке для выполнения нескольких действий фильтрации.

Форматировщики¶

В конечном счете, запись журнала должна быть представлена в виде текста. Форматировщики описывают точный формат этого текста. Форматтер обычно состоит из строки форматирования Python, содержащей LogRecord attributes; однако вы также можете написать собственные форматтеры для реализации специфического поведения форматирования.

Последствия для безопасности¶

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

  • какая информация собирается
  • где он будет впоследствии храниться
  • как она будет передана
  • кто может иметь к нему доступ.

Чтобы помочь контролировать сбор конфиденциальной информации, вы можете явно указать определенную конфиденциальную информацию, которая будет отфильтрована из отчетов об ошибках — читайте подробнее о том, как это сделать filter error reports.

AdminEmailHandler

Встроенный AdminEmailHandler заслуживает упоминания в контексте безопасности. Если его опция include_html включена, то отправляемое им сообщение электронной почты будет содержать полный трассировочный откат, с именами и значениями локальных переменных на каждом уровне стека, плюс значения ваших настроек Django (другими словами, тот же уровень детализации, который раскрывается на веб-странице, когда DEBUG становится True).

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

Настройка протоколирования¶

Библиотека протоколирования Python предоставляет несколько способов настройки протоколирования, начиная от программного интерфейса и заканчивая конфигурационными файлами. По умолчанию Django использует dictConfig format.

Для настройки протоколирования вы используете LOGGING для определения словаря настроек протоколирования. Эти настройки описывают регистраторы, обработчики, фильтры и форматеры, которые вы хотите использовать в своей настройке протоколирования, а также уровни протоколирования и другие свойства, которыми должны обладать эти компоненты.

По умолчанию установка LOGGING объединяется с Django’s default logging configuration по следующей схеме.

Если ключ disable_existing_loggers в директ-конфиге LOGGING установлен в True (что является dictConfig по умолчанию, если ключ отсутствует), то все регистраторы из конфигурации по умолчанию будут отключены. Отключенные регистраторы — это не то же самое, что удаленные; регистратор будет по-прежнему существовать, но будет молча отбрасывать все, что записывается в него, даже не передавая записи в родительский регистратор. Таким образом, вы должны быть очень осторожны, используя 'disable_existing_loggers': True; вероятно, это не то, что вам нужно. Вместо этого вы можете установить disable_existing_loggers в False и переопределить некоторые или все логгеры по умолчанию; или вы можете установить LOGGING_CONFIG в None и handle logging config yourself.

Логирование настраивается как часть общей функции Django setup(). Поэтому вы можете быть уверены, что логгеры всегда готовы к использованию в коде вашего проекта.

Примеры¶

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

Для начала, вот небольшая конфигурация, которая позволит вам выводить все сообщения журнала на консоль:

settings.py

import os

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'console': {
            'class': 'logging.StreamHandler',
        },
    },
    'root': {
        'handlers': ['console'],
        'level': 'WARNING',
    },
}

Это настраивает родительский регистратор root на отправку сообщений с уровнем WARNING и выше в обработчик консоли. Изменив уровень до INFO или DEBUG, можно вывести больше сообщений. Это может быть полезно во время разработки.

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

settings.py

import os

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'console': {
            'class': 'logging.StreamHandler',
        },
    },
    'root': {
        'handlers': ['console'],
        'level': 'WARNING',
    },
    'loggers': {
        'django': {
            'handlers': ['console'],
            'level': os.getenv('DJANGO_LOG_LEVEL', 'INFO'),
            'propagate': False,
        },
    },
}

По умолчанию этот конфиг отправляет сообщения от логгера django уровня INFO или выше на консоль. Это тот же уровень, что и стандартная конфигурация логирования Django, за исключением того, что стандартная конфигурация отображает записи журнала только при уровне DEBUG=True. Django не записывает много сообщений такого уровня INFO. Однако с этой конфигурацией вы также можете установить переменную окружения DJANGO_LOG_LEVEL=DEBUG, чтобы увидеть все отладочные журналы Django, которые очень подробны, поскольку включают все запросы к базе данных.

Вам не обязательно вести журнал в консоль. Вот конфигурация, которая записывает все логи из django именованного логгера в локальный файл:

settings.py

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'file': {
            'level': 'DEBUG',
            'class': 'logging.FileHandler',
            'filename': '/path/to/django/debug.log',
        },
    },
    'loggers': {
        'django': {
            'handlers': ['file'],
            'level': 'DEBUG',
            'propagate': True,
        },
    },
}

Если вы используете этот пример, не забудьте изменить путь 'filename' на место, доступное для записи пользователю, который запускает приложение Django.

Наконец, вот пример довольно сложной настройки протоколирования:

settings.py

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': {
        'special': {
            '()': 'project.logging.SpecialFilter',
            'foo': 'bar',
        },
        'require_debug_true': {
            '()': 'django.utils.log.RequireDebugTrue',
        },
    },
    'handlers': {
        'console': {
            'level': 'INFO',
            'filters': ['require_debug_true'],
            'class': 'logging.StreamHandler',
            'formatter': 'simple'
        },
        'mail_admins': {
            'level': 'ERROR',
            'class': 'django.utils.log.AdminEmailHandler',
            'filters': ['special']
        }
    },
    'loggers': {
        'django': {
            'handlers': ['console'],
            'propagate': True,
        },
        'django.request': {
            'handlers': ['mail_admins'],
            'level': 'ERROR',
            'propagate': False,
        },
        'myproject.custom': {
            'handlers': ['console', 'mail_admins'],
            'level': 'INFO',
            'filters': ['special']
        }
    }
}

Эта конфигурация протоколирования выполняет следующие действия:

  • Идентифицирует конфигурацию как имеющую формат „dictConfig версии 1“. В настоящее время это единственная версия формата dictConfig.

  • Определяет два форматера:

    • simple, который выводит имя уровня журнала (например, DEBUG) и сообщение журнала.

      Строка format — это обычная строка форматирования Python, описывающая детали, которые должны быть выведены в каждой строке журнала. Полный список деталей, которые могут быть выведены, можно найти в Formatter Objects.

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

  • Определяет два фильтра:

    • project.logging.SpecialFilter, используя псевдоним special. Если для данного фильтра требуются дополнительные аргументы, они могут быть предоставлены как дополнительные ключи в словаре конфигурации фильтра. В этом случае аргументу foo будет присвоено значение bar при инстанцировании SpecialFilter.
    • django.utils.log.RequireDebugTrue, который передает записи, когда DEBUG становится True.
  • Определяет два обработчика:

    • console, обработчик StreamHandler, который печатает любое сообщение INFO (или выше) в sys.stderr. Этот обработчик использует формат вывода simple.
    • mail_admins, обработчик AdminEmailHandler, который отправляет любое сообщение ERROR (или выше) на сайт ADMINS. Этот обработчик использует фильтр special.
  • Настраивает три регистратора:

    • django, который передает все сообщения обработчику console.
    • django.request, который передает все сообщения ERROR обработчику mail_admins. Кроме того, этот логгер помечен как не распространяющий сообщения. Это означает, что сообщения журнала, записанные в django.request, не будут обработаны регистратором django.
    • myproject.custom, который передает все сообщения уровня INFO или выше, которые также проходят фильтр special, двум обработчикам — console и mail_admins. Это означает, что все сообщения уровня INFO (или выше) будут выведены на консоль; ERROR и CRITICAL сообщения также будут выведены по электронной почте.

Пользовательская конфигурация протоколирования¶

Если вы не хотите использовать формат Python dictConfig для настройки регистратора, вы можете задать свою собственную схему конфигурации.

Параметр LOGGING_CONFIG определяет вызываемую функцию, которая будет использоваться для настройки логгеров Django. По умолчанию он указывает на функцию Python logging.config.dictConfig(). Однако, если вы хотите использовать другой процесс конфигурации, вы можете использовать любую другую вызываемую функцию, принимающую один аргумент. Содержимое LOGGING будет предоставлено в качестве значения этого аргумента при настройке логирования.

Отключение конфигурации ведения журнала¶

Если вы не хотите настраивать ведение журнала вообще (или хотите вручную настроить ведение журнала, используя свой собственный подход), вы можете установить LOGGING_CONFIG на None. Это отключит процесс конфигурирования для Django’s default logging.

Установка LOGGING_CONFIG в None означает только отключение процесса автоматической настройки, но не самого протоколирования. Если вы отключите процесс конфигурирования, Django все равно будет выполнять вызовы логирования, возвращаясь к тому поведению логирования, которое определено по умолчанию.

Вот пример, который отключает конфигурацию логирования Django, а затем вручную настраивает логирование:

settings.py

LOGGING_CONFIG = None

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

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

21 января 2022 г. | Python

Правильное ведение логирования играет решающее значение для процесса отладки и устранения неполадок. Оно не только полезно для локального девелопменга, но и для продашен. При просмотре журналов в поиске проблемы, редко кто-то говорит: «Наше приложение слишком много логирует.» Но чаще можно услышать иное. Итак, принимая это во внимание, давайте начнем.

Введение в логирование на Python

В верхней части каждого файла должно быть что-то подобное:

import logging
logger = logging.getLogger(__name__)

Переменная name будет заменена точечным путём до Python модуля, например, скрипт myproject/myapp/views.py, то будет использоваться myproject.myapp.views. Теперь можно применять logger по всему файлу:

# Простая строка, зарегистрированная на уровне «warning»
logger.warning("Your log message is here")

# Строка с переменной на уровне «info»
logger.info("The value of var is %s", var)

# Ведение журнала трассировки для пойманного исключения
try:
    function_that_might_raise_index_error()
except IndexError:
    # эквивалентно logger.error(msg, exc_info=True)
    logger.exception("Something bad happened")

Примечание: логеры в Python будут обрабатывать вставки переменных в сообщение журнала, если передать их в качестве аргументов в функцию логирования. Если не требуется выводить журнал, замена переменной производиться не будет, что поможет избежать небольшого падения производительности для журнала, который никогда не будет использоваться.

Перепишите свой код с помощью операторов логирования. Краткие рекомендации для выбора уровней журналирования:

  • debug. Информация не нужна для повседневной работы, но полезна в разработке.
  • info: Информация, которая полезна во время обычной работы.
  • warning: Информация, которая может быть проблематичной, но не является срочной.
  • error: Информация, которая важна и, вероятно, требует срочного внимания.
  • critical: на практике используется редко, но если вам нужно больше, чем ошибка, то это то что нужно.

Куда логировать

Ваше приложение не должно беспокоиться о том, куда идут log сообщения. Вместо этого оно должно записывать все в консоль (stdout / stderr), и пусть сервер сам решает, что дальше делать с этой информацией. Обычно они помещаются в выделенный файл, захваченный журналом Systemd или Docker, с последующей отправкой на отдельный сервис, такой как ElasticSearch, или некоторое их сочетание. Хранение файлов журналов – это задача развертывания, а не приложения.

Единственное, что нужно вашему приложению – это формат журналов. Обычно это всего лишь строка с соответствующими данными, но если ваш сервер уже добавляет отметку времени в журнал, то вероятно, нужно будет исключить ее из своего собственного форматирования. Аналогично, если ваш агрегатор журналов может принимать JSON, возможно, более подходящим является форматирование, такое как python-json-logger.

Конфигурирование логирования. Настройка Sentry

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

Настроим Sentry, отправив любые сообщения службы в журнал, уровня warning или выше. В противном случае они были бы потеряны в море журнальных файлов, которые на практике редко проверяются. Для этого добавим «корневой» журнал, который будет использоваться для всех log сообщений, которые отправляются из любого модуля Python. Пример настройки Django:

import logging.config
LOGGING_CONFIG = None
logging.config.dictConfig({
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'console': {
            # точный формат не важен, это минимальная информация
            'format': '%(asctime)s %(name)-12s %(levelname)-8s %(message)s',
        },
    },
    'handlers': {
        'console': {
            'class': 'logging.StreamHandler',
            'formatter': 'console',
        },
        # Добавить обработчик для Sentry для `warning` и выше
        'sentry': {
            'level': 'WARNING',
            'class': 'raven.contrib.django.raven_compat.handlers.SentryHandler',
        },
    },
    'loggers': {
    # корневой логгер
        '': {
            'level': 'WARNING',
            'handlers': ['console', 'sentry'],
        },
    },
})

Логирование из приложения

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

logging.config.dictConfig({
    # ...
    'loggers': {
        '': {
            'level': 'WARNING',
            'handlers': ['console', 'sentry'],
        },
        'myproject': {
            'level': 'INFO',
            'handlers': ['console', 'sentry'],
            # требуется, чтобы избежать двойного ведения журнала с помощью корневого логгера
            'propagate': False,
        },
    },
})

Как быть, если нудно исследовать что-то в своем приложении глубже, чем debug уровень журналированя? Коммит нового кода и его развертывая ощущается излишним. Лучшим вариантом в этом случае воспользоваться переменной окружения среды. Модифицируем предыдущий код, приведя к такому виду:

import os
LOGLEVEL = os.environ.get('LOGLEVEL', 'info').upper()
logging.config.dictConfig({
    # ...
    'loggers': {
        '': {
            'level': 'WARNING',
            'handlers': ['console', 'sentry'],
        },
        'myproject': {
            'level': LOGLEVEL,
            'handlers': ['console', 'sentry'],
            # требуется, чтобы избежать двойного ведения журнала с помощью корневого регистратора
            'propagate': False,
        },
    },
})

Теперь ведение журнала по умолчанию будет info, но может быть легко временно изменено, установив переменную среды LOGLEVEL = debug. В качестве альтернативы, если хранилище журналов не является проблемой, учитывайте возможность ведения журнала на уровне debug. Их достаточно легко отфильтровать с помощью простого grep или с помощью инструмента визуализации журнала, например, Kibana.

Отфильтровывание шума

После того, как установлено и запущено логирование, может появляться информация о некоторых модулях, которые не являются важными, продуцирующими дополнительный шум в журнальные файлы. Для таких модулей можно добавить еще один регистратор в настройки. Первый вариант заключается в том, чтобы вывести их на консоль, но не распространять их на корневой журнал, который отправит их в Sentry:

logging.config.dictConfig({
    # ...
    'loggers': {
        '': {
            'level': 'WARNING',
            'handlers': ['console', 'sentry'],
        },
        'myproject': {
            'level': LOGLEVEL,
            'handlers': ['console', 'sentry'],
            # требуется, чтобы избежать двойного ведения журнала с помощью корневого регистратора
            'propagate': False,
        },
        # Не отправляйте журналы этого модуля в Sentry
        'noisy_module': {
            'level':'ERROR',
            'handlers': ['console'],
            'propagate': False,
        },
    },
})

Если обнаружится, что они слишком шумные, даже для консоли, можно полностью отказаться от них логирования:

logging.config.dictConfig({
    # ...
    'loggers': {
        # ...
        # Не логгировать этот модуль
        'noisy_module': {
            'level': 'NOTSET',
            'propagate': False,
        },
    },
})

Логирование локальный запросов

По умолчанию в Django будет выполняться журналирование запросов с runserver. Переопределяя конфигурацию Django происходит потеря этой возможности. Но её достаточно легко добавить:

from django.utils.log import DEFAULT_LOGGING
logging.config.dictConfig({
    # ...
    'formatters': {
        # ...
        'django.server': DEFAULT_LOGGING['formatters']['django.server'],
    },
    'handlers' {
        # ...
        'django.server': DEFAULT_LOGGING['handlers']['django.server'],
    },
    'loggers': {
        # ...
        'django.server': DEFAULT_LOGGING['loggers']['django.server'],
    },
})

Этот метод немного хрупкий, поскольку зависит от некоторых внутренних компонентов, которые могут меняться между версиями, но поломки легко исправить используя прямое копирование/вставку из исходников Django.

Понравилась статья? Поделить с друзьями:
  • Django rest обработка ошибок
  • Dism ошибка 769
  • Dism ошибка 740
  • Django noreversematch ошибка
  • Django no such table ошибка