123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339 |
- .. _logging-how-to:
- ================================
- How to configure and use logging
- ================================
- .. seealso::
- * :ref:`Django logging reference <logging-ref>`
- * :ref:`Django logging overview <logging-explanation>`
- Django provides a working :ref:`default logging configuration
- <default-logging-configuration>` that is readily extended.
- Make a basic logging call
- =========================
- To send a log message from within your code, you place a logging call into it.
- .. admonition:: Don't be tempted to use logging calls in ``settings.py``.
- The way that Django logging is configured as part of the ``setup()``
- function means that logging calls placed in ``settings.py`` may not work as
- expected, because *logging will not be set up at that point*. To explore
- logging, use a view function as suggested in the example below.
- First, import the Python logging library, and then obtain a logger instance
- with :py:func:`logging.getLogger`. Provide the ``getLogger()`` method with a
- name to identify it and the records it emits. A good option is to use
- ``__name__`` (see :ref:`naming-loggers` below for more on this) which will
- provide the name of the current Python module as a dotted path::
- import logging
- logger = logging.getLogger(__name__)
- It's a good convention to perform this declaration at module level.
- And then in a function, for example in a view, send a record to the logger::
- def some_view(request):
- ...
- if some_risky_state:
- logger.warning("Platform is running at risk")
- When this code is executed, a :py:class:`~logging.LogRecord` containing that
- message will be sent to the logger. If you're using Django's default logging
- configuration, the message will appear in the console.
- The ``WARNING`` level used in the example above is one of several
- :ref:`logging severity levels <topic-logging-parts-loggers>`: ``DEBUG``,
- ``INFO``, ``WARNING``, ``ERROR``, ``CRITICAL``. So, another example might be::
- logger.critical("Payment system is not responding")
- .. important::
- Records with a level lower than ``WARNING`` will not appear in the console
- by default. Changing this behavior requires additional configuration.
- Customize logging configuration
- ===============================
- Although Django's logging configuration works out of the box, you can control
- exactly how your logs are sent to various destinations - to log files, external
- services, email and so on - with some additional configuration.
- You can configure:
- * logger mappings, to determine which records are sent to which handlers
- * handlers, to determine what they do with the records they receive
- * filters, to provide additional control over the transfer of records, and
- even modify records in-place
- * formatters, to convert :class:`~logging.LogRecord` objects to a string or
- other form for consumption by human beings or another system
- There are various ways of configuring logging. In Django, the
- :setting:`LOGGING` setting is most commonly used. The setting uses the
- :ref:`dictConfig format <logging-config-dictschema>`, and extends the
- :ref:`default logging configuration <default-logging-definition>`.
- See :ref:`configuring-logging` for an explanation of how your custom settings
- are merged with Django's defaults.
- See the :mod:`Python logging documentation <python:logging.config>` for
- details of other ways of configuring logging. For the sake of simplicity, this
- documentation will only consider configuration via the ``LOGGING`` setting.
- .. _basic-logger-configuration:
- Basic logging configuration
- ---------------------------
- When configuring logging, it makes sense to
- Create a ``LOGGING`` dictionary
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- In your ``settings.py``::
- LOGGING = {
- "version": 1, # the dictConfig format version
- "disable_existing_loggers": False, # retain the default loggers
- }
- It nearly always makes sense to retain and extend the default logging
- configuration by setting ``disable_existing_loggers`` to ``False``.
- Configure a handler
- ~~~~~~~~~~~~~~~~~~~
- This example configures a single handler named ``file``, that uses Python's
- :class:`~logging.FileHandler` to save logs of level ``DEBUG`` and higher to the
- file ``general.log`` (at the project root):
- .. code-block:: python
- :emphasize-lines: 3-8
- LOGGING = {
- # ...
- "handlers": {
- "file": {
- "class": "logging.FileHandler",
- "filename": "general.log",
- },
- },
- }
- Different handler classes take different configuration options. For more
- information on available handler classes, see the
- :class:`~django.utils.log.AdminEmailHandler` provided by Django and the various
- :py:mod:`handler classes <logging.handlers>` provided by Python.
- Logging levels can also be set on the handlers (by default, they accept log
- messages of all levels). Using the example above, adding:
- .. code-block:: python
- :emphasize-lines: 4
- {
- "class": "logging.FileHandler",
- "filename": "general.log",
- "level": "DEBUG",
- }
- would define a handler configuration that only accepts records of level
- ``DEBUG`` and higher.
- Configure a logger mapping
- ~~~~~~~~~~~~~~~~~~~~~~~~~~
- To send records to this handler, configure a logger mapping to use it for
- example:
- .. code-block:: python
- :emphasize-lines: 3-8
- LOGGING = {
- # ...
- "loggers": {
- "": {
- "level": "DEBUG",
- "handlers": ["file"],
- },
- },
- }
- The mapping's name determines which log records it will process. This
- configuration (``''``) is *unnamed*. That means that it will process records
- from *all* loggers (see :ref:`naming-loggers` below on how to use the mapping
- name to determine the loggers for which it will process records).
- It will forward messages of levels ``DEBUG`` and higher to the handler named
- ``file``.
- Note that a logger can forward messages to multiple handlers, so the relation
- between loggers and handlers is many-to-many.
- If you execute::
- logger.debug("Attempting to connect to API")
- in your code, you will find that message in the file ``general.log`` in the
- root of the project.
- Configure a formatter
- ~~~~~~~~~~~~~~~~~~~~~
- By default, the final log output contains the message part of each :class:`log
- record <logging.LogRecord>`. Use a formatter if you want to include additional
- data. First name and define your formatters - this example defines
- formatters named ``verbose`` and ``simple``:
- .. code-block:: python
- :emphasize-lines: 3-12
- LOGGING = {
- # ...
- "formatters": {
- "verbose": {
- "format": "{name} {levelname} {asctime} {module} {process:d} {thread:d} {message}",
- "style": "{",
- },
- "simple": {
- "format": "{levelname} {message}",
- "style": "{",
- },
- },
- }
- The ``style`` keyword allows you to specify ``{`` for :meth:`str.format` or
- ``$`` for :class:`string.Template` formatting; the default is ``$``.
- See :ref:`logrecord-attributes` for the :class:`~logging.LogRecord` attributes
- you can include.
- To apply a formatter to a handler, add a ``formatter`` entry to the handler's
- dictionary referring to the formatter by name, for example:
- .. code-block:: python
- :emphasize-lines: 5
- "handlers": {
- "file": {
- "class": "logging.FileHandler",
- "filename": "general.log",
- "formatter": "verbose",
- },
- }
- .. _naming-loggers:
- Use logger namespacing
- ~~~~~~~~~~~~~~~~~~~~~~
- The unnamed logging configuration ``''`` captures logs from any Python
- application. A named logging configuration will capture logs only from loggers
- with matching names.
- The namespace of a logger instance is defined using
- :py:func:`~logging.getLogger`. For example in ``views.py`` of ``my_app``::
- logger = logging.getLogger(__name__)
- will create a logger in the ``my_app.views`` namespace. ``__name__`` allows you
- to organize log messages according to their provenance within your project's
- applications automatically. It also ensures that you will not experience name
- collisions.
- A logger mapping named ``my_app.views`` will capture records from this logger:
- .. code-block:: python
- :emphasize-lines: 4
- LOGGING = {
- # ...
- "loggers": {
- "my_app.views": {...},
- },
- }
- A logger mapping named ``my_app`` will be more permissive, capturing records
- from loggers anywhere within the ``my_app`` namespace (including
- ``my_app.views``, ``my_app.utils``, and so on):
- .. code-block:: python
- :emphasize-lines: 4
- LOGGING = {
- # ...
- "loggers": {
- "my_app": {...},
- },
- }
- You can also define logger namespacing explicitly::
- logger = logging.getLogger("project.payment")
- and set up logger mappings accordingly.
- .. _naming-loggers-hierarchy:
- Using logger hierarchies and propagation
- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- Logger naming is *hierarchical*. ``my_app`` is the parent of ``my_app.views``,
- which is the parent of ``my_app.views.private``. Unless specified otherwise,
- logger mappings will propagate the records they process to their parents - a
- record from a logger in the ``my_app.views.private`` namespace will be handled
- by a mapping for both ``my_app`` and ``my_app.views``.
- To manage this behavior, set the propagation key on the mappings you define::
- LOGGING = {
- # ...
- "loggers": {
- "my_app": {
- # ...
- },
- "my_app.views": {
- # ...
- },
- "my_app.views.private": {
- # ...
- "propagate": False,
- },
- },
- }
- ``propagate`` defaults to ``True``. In this example, the logs from
- ``my_app.views.private`` will not be handled by the parent, but logs from
- ``my_app.views`` will.
- Configure responsive logging
- ----------------------------
- Logging is most useful when it contains as much information as possible, but
- not information that you don't need - and how much you need depends upon what
- you're doing. When you're debugging, you need a level of information that would
- be excessive and unhelpful if you had to deal with it in production.
- You can configure logging to provide you with the level of detail you need,
- when you need it. Rather than manually change configuration to achieve this, a
- better way is to apply configuration automatically according to the
- environment.
- For example, you could set an environment variable ``DJANGO_LOG_LEVEL``
- appropriately in your development and staging environments, and make use of it
- in a logger mapping thus::
- "level": os.getenv("DJANGO_LOG_LEVEL", "WARNING")
- \- so that unless the environment specifies a lower log level, this
- configuration will only forward records of severity ``WARNING`` and above to
- its handler.
- Other options in the configuration (such as the ``level`` or ``formatter``
- option of handlers) can be similarly managed.
|