flake8-logging


Nameflake8-logging JSON
Version 1.6.0 PyPI version JSON
download
home_pageNone
SummaryA Flake8 plugin that checks for issues using the standard library logging module.
upload_time2024-03-20 20:47:07
maintainerNone
docs_urlNone
authorNone
requires_python>=3.8
licenseMIT
keywords flake8
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            ==============
flake8-logging
==============

.. image:: https://img.shields.io/github/actions/workflow/status/adamchainz/flake8-logging/main.yml?branch=main&style=for-the-badge
   :target: https://github.com/adamchainz/flake8-logging/actions?workflow=CI

.. image:: https://img.shields.io/badge/Coverage-100%25-success?style=for-the-badge
   :target: https://github.com/adamchainz/flake8-logging/actions?workflow=CI

.. image:: https://img.shields.io/pypi/v/flake8-logging.svg?style=for-the-badge
   :target: https://pypi.org/project/flake8-logging/

.. image:: https://img.shields.io/badge/code%20style-black-000000.svg?style=for-the-badge
   :target: https://github.com/psf/black

.. image:: https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white&style=for-the-badge
   :target: https://github.com/pre-commit/pre-commit
   :alt: pre-commit

A `Flake8 <https://flake8.readthedocs.io/en/latest/>`_ plugin that checks for issues using the standard library logging module.

For a brief overview and background, see `the introductory blog post <https://adamj.eu/tech/2023/09/07/introducing-flake8-logging/>`__.

Requirements
============

Python 3.8 to 3.12 supported.

Installation
============

First, install with ``pip``:

.. code-block:: sh

     python -m pip install flake8-logging

Second, if you define Flake8’s ``select`` setting, add the ``L`` prefix to it.
Otherwise, the plugin should be active by default.

----

**Linting a Django project?**
Check out my book `Boost Your Django DX <https://adamchainz.gumroad.com/l/byddx>`__ which covers Flake8 and many other code quality tools.

----

Rules
=====

LOG001 use ``logging.getLogger()`` to instantiate loggers
---------------------------------------------------------

The `Logger Objects documentation section <https://docs.python.org/3/library/logging.html#logger-objects>`__ starts:

  Note that Loggers should NEVER be instantiated directly, but always through the module-level function ``logging.getLogger(name)``.

Directly instantiated loggers are not added into the logger tree.
This means that they bypass all configuration and their messages are only sent to the `last resort handler <https://docs.python.org/3/library/logging.html#logging.lastResort>`__.
This can mean their messages are incorrectly filtered, formatted, and sent only to ``stderr``.
Potentially, such messages will not be visible in your logging tooling and you won’t be alerted to issues.

Use |getLogger()|__ to correctly instantiate loggers.

.. |getLogger()| replace:: ``getLogger()``
__ https://docs.python.org/3/library/logging.html#logging.getLogger

This rule detects any module-level calls to ``Logger()``.

Failing example:

.. code-block:: python

    import logging

    logger = logging.Logger(__name__)

Corrected:

.. code-block:: python

    import logging

    logger = logging.getLogger(__name__)

LOG002 use ``__name__`` with ``getLogger()``
--------------------------------------------

The `logging documentation <https://docs.python.org/3/library/logging.html#logger-objects>`__ recommends this pattern:

.. code-block:: python

    logging.getLogger(__name__)

|__name__|__ is the fully qualified module name, such as ``camelot.spam``, which is the intended format for logger names.

.. |__name__| replace:: ``__name__``
__ https://docs.python.org/3/reference/import.html?#name__

This rule detects probably-mistaken usage of similar module-level dunder constants:

* |__cached__|__ - the pathname of the module’s compiled versio˜, such as ``camelot/__pycache__/spam.cpython-311.pyc``.

  .. |__cached__| replace:: ``__cached__``
  __ https://docs.python.org/3/reference/import.html?#cached__

* |__file__|__ - the pathname of the module, such as ``camelot/spam.py``.

  .. |__file__| replace:: ``__file__``
  __ https://docs.python.org/3/reference/import.html?#file__

Failing example:

.. code-block:: python

    import logging

    logger = logging.getLogger(__file__)

Corrected:

.. code-block:: python

    import logging

    logger = logging.getLogger(__name__)

LOG003 ``extra`` key ``'<key>'`` clashes with LogRecord attribute
-----------------------------------------------------------------

The |extra documentation|__ states:

.. |extra documentation| replace:: ``extra`` documentation
__ https://docs.python.org/3/library/logging.html#logging.Logger.debug

    The keys in the dictionary passed in ``extra`` should not clash with the keys used by the logging system.

Such clashes crash at runtime with an error like:

.. code-block:: text

    KeyError: "Attempt to overwrite 'msg' in LogRecord"

Unfortunately, this error is only raised if the message is not filtered out by level.
Tests may therefore not encounter the check, if they run with a limited logging configuration.

This rule detects such clashes by checking for keys matching the |LogRecord attributes|__.

.. |LogRecord attributes| replace:: ``LogRecord`` attributes
__ https://docs.python.org/3/library/logging.html#logrecord-attributes

Failing example:

.. code-block:: python

    import logging

    logger = logging.getLogger(__name__)

    response = acme_api()
    logger.info("ACME Response", extra={"msg": response.msg})

Corrected:

.. code-block:: python

    import logging

    logger = logging.getLogger(__name__)

    response = acme_api()
    logger.info("ACME Response", extra={"response_msg": response.msg})

LOG004 avoid ``exception()`` outside of exception handlers
----------------------------------------------------------

The |exception() documentation|__ states:

.. |exception() documentation| replace:: ``exception()`` documentation
__ https://docs.python.org/3/library/logging.html#logging.exception

    This function should only be called from an exception handler.

Calling ``exception()`` outside of an exception handler attaches ``None`` exception information, leading to confusing messages:

.. code-block:: pycon

    >>> logging.exception("example")
    ERROR:root:example
    NoneType: None

Use ``error()`` instead.
To log a caught exception, pass it in the ``exc_info`` argument.

This rule detects ``exception()`` calls outside of exception handlers.

Failing example:

.. code-block:: python

    import logging

    response = acme_api()
    if response is None:
        logging.exception("ACME failed")

Corrected:

.. code-block:: python

    import logging

    response = acme_api()
    if response is None:
        logging.error("ACME failed")

LOG005 use ``exception()`` within an exception handler
------------------------------------------------------

Within an exception handler, the |exception()|__ method is preferable over ``logger.error()``.
The ``exception()`` method captures the exception automatically, whilst ``error()`` needs it to be passed explicitly in the ``exc_info`` argument.
Both methods log with the level ``ERROR``.

.. |exception()| replace:: ``exception()``
__ https://docs.python.org/3/library/logging.html#logging.Logger.exception

This rule detects ``error()`` calls within exception handlers, excluding those with a falsy ``exc_info`` argument.

Failing example:

.. code-block:: python

    try:
        acme_api()
    except AcmeError as exc:
        logger.error("ACME API failed", exc_info=exc)

Corrected:

.. code-block:: python

    try:
        acme_api()
    except AcmeError:
        logger.exception("ACME API failed")

Or alternatively, if the exception information is truly uninformative:

.. code-block:: python

    try:
        acme_api()
    except DuplicateError:
        logger.error("ACME Duplicate Error", exc_info=False)

LOG006 redundant ``exc_info`` argument for ``exception()``
----------------------------------------------------------

The |exception()2|__ method captures the exception automatically, making a truthy ``exc_info`` argument redundant.

.. |exception()2| replace:: ``exception()``
__ https://docs.python.org/3/library/logging.html#logging.Logger.exception

This rule detects ``exception()`` calls within exception handlers with an ``exc_info`` argument that is truthy or the captured exception object.

Failing example:

.. code-block:: python

    try:
        acme_api()
    except AcmeError:
        logger.exception("ACME API failed", exc_info=True)

Corrected:

.. code-block:: python

    try:
        acme_api()
    except AcmeError:
        logger.exception("ACME API failed")

LOG007 use ``error()`` instead of ``exception()`` with ``exc_info=False``
-------------------------------------------------------------------------

The |exception()3|__ method captures the exception automatically.
Disabling this by setting ``exc_info=False`` is the same as using ``error()``, which is clearer and doesn’t need the ``exc_info`` argument.

.. |exception()3| replace:: ``exception()``
__ https://docs.python.org/3/library/logging.html#logging.Logger.exception

This rule detects ``exception()`` calls with an ``exc_info`` argument that is falsy.

Failing example:

.. code-block:: python

    logger.exception("Left phalange missing", exc_info=False)

Corrected:

.. code-block:: python

    logger.error("Left phalange missing")

LOG008 ``warn()`` is deprecated, use ``warning()`` instead
----------------------------------------------------------

The ``warn()`` method is a deprecated, undocumented alias for |warning()|__
``warning()`` should always be used instead.
The method was deprecated in Python 2.7, in commit `04d5bc00a2 <https://github.com/python/cpython/commit/04d5bc00a219860c69ea17eaa633d3ab9917409f>`__, and removed in Python 3.13, in commit `dcc028d924 <https://github.com/python/cpython/commit/dcc028d92428bd57358a5028ada2a53fc79fc365>`__.

.. |warning()| replace:: ``warning()``
__ https://docs.python.org/3/library/logging.html#logging.Logger.warning

This rule detects calls to ``warn()``.

Failing example:

.. code-block:: python

    logger.warn("Cheesy puns incoming")

Corrected:

.. code-block:: python

    logger.warning("Cheesy puns incoming")

LOG009 ``WARN`` is undocumented, use ``WARNING`` instead
--------------------------------------------------------

The ``WARN`` constant is an undocumented alias for |WARNING|__.
Whilst it’s not deprecated, it’s not mentioned at all in the documentation, so the documented ``WARNING`` should always be used instead.

.. |WARNING| replace:: ``WARNING``
__ https://docs.python.org/3/library/logging.html#logging-levels

This rule detects any import or access of ``WARN``.

Failing example:

.. code-block:: python

    import logging

    logging.WARN

Corrected:

.. code-block:: python

    import logging

    logging.WARNING

LOG010 ``exception()`` does not take an exception
-------------------------------------------------

Like other logger methods, the |exception()4|__ method takes a string as its first argument.
A common misunderstanding is to pass it an exception instead.
Doing so is redundant, as ``exception()`` will already capture the exception object.
It can also lead to unclear log messages, as the logger will call ``str()`` on the exception, which doesn’t always produce a sensible message.

.. |exception()4| replace:: ``exception()``
__ https://docs.python.org/3/library/logging.html#logging.Logger.exception

This rule detects ``exception()`` calls with a first argument that is the current exception handler’s capture variable.

Failing example:

.. code-block:: python

    try:
        shuffle_deck()
    except Exception as exc:
        logger.exception(exc)

Corrected:

.. code-block:: python

    try:
        shuffle_deck()
    except Exception:
        logger.exception("Failed to shuffle deck")

LOG011 avoid pre-formatting log messages
----------------------------------------

Logger methods support string formatting for `logging variable data <https://docs.python.org/3/howto/logging.html#logging-variable-data>`__, such as:

.. code-block:: python

    logger.info("Couldn’t chop %s", vegetable)

Log-aggregating tools, such as `Sentry <https://sentry.io/>`__ can group messages based on their unformatted message templates.
Using a pre-formatted message, such as from an f-string, prevents this from happening.
Tools have to rely on imperfect heuristics, which can lead to duplicate groups.

Additionally, the logging framework skips formatting messages that won’t be logged.
Using a pre-formatted string, such as from an f-string, has no such optimization.
This overhead can add up when you have a high volume of logs that are normally skipped.

This rule detects logger method calls with a ``msg`` argument that is one of:

* an f-string
* a call to ``str.format()``
* a string used with the modulus operator (``%``)
* a concatenation of strings with non-strings

Failing examples:

.. code-block:: python

    logging.error(f"Couldn’t chop {vegetable}")

.. code-block:: python

    logging.error("Couldn’t chop {}".format(vegetable))

.. code-block:: python

    logging.error("Couldn’t chop %s" % (vegetable,))

.. code-block:: python

    logging.error("Couldn’t chop " + vegetable)

Corrected:

.. code-block:: python

    logging.error("Couldn’t chop %s", vegetable)

LOG012 formatting error: ``<n>`` ``<style>`` placeholders but ``<m>`` arguments
-------------------------------------------------------------------------------

Logger methods support several string formatting options for messages.
If there’s a mismatch between the number of parameters in the message and those provided, the call will error:

.. code-block:: pycon

    >>> logging.info("Sent %s to %s", letter)
    --- Logging error ---
    Traceback (most recent call last):
      File "/.../logging/__init__.py", line 1110, in emit
        msg = self.format(record)
              ^^^^^^^^^^^^^^^^^^^
    ...

      File "/.../logging/__init__.py", line 377, in getMessage
        msg = msg % self.args
              ~~~~^~~~~~~~~~~
    TypeError: not enough arguments for format string
    Call stack:
      File "<stdin>", line 1, in <module>
    Message: ' %s to %s'
    Arguments: ('Red Letter',)

This will only happen when the logger is enabled since loggers don’t perform string formatting when disabled.
Thus a configuration change can reveal such errors.

Additionally, if no arguments are provided, parametrized messages are silently unformatted:

.. code-block:: pycon

    >>> logging.info("Sent %s to %s")
    INFO:root:Sent %s to %s

This rule detects mismatches between the number of message parameters and those provided.
At the moment, it only supports ``%``-style formatting with at least one parameter.

Failing examples:

.. code-block:: python

    logging.info("Blending %s")

.. code-block:: python

    logging.info("Blending %s", fruit.name, fruit.size)

Corrected:

.. code-block:: python

    logging.info("Blending %s of size %r", fruit.name, fruit.size)

LOG013 formatting error: ``<missing/unreferenced>`` keys: ``<keys>``
--------------------------------------------------------------------

When using named ``%``-style formatting, if the message references a missing key, the call will error:

.. code-block:: pycon

    >>> logging.error("Hi %(name)s", {"nam": "hacker"})
    --- Logging error ---
    Traceback (most recent call last):
      File "/.../logging/__init__.py", line 1160, in emit
        msg = self.format(record)
              ^^^^^^^^^^^^^^^^^^^
    ...

      File "/.../logging/__init__.py", line 392, in getMessage
        msg = msg % self.args
              ~~~~^~~~~~~~~~~
    KeyError: 'name'
    Call stack:
      File "<stdin>", line 1, in <module>
    Message: 'Hi %(name)s'
    Arguments: {'nam': 'hacker'}

This will only happen when the logger is enabled since loggers don’t perform string formatting when disabled.
Thus a configuration change can reveal such errors.

This rule detects mismatches between the message parameter names and those provided.
It only works if there’s a dict literal argument for the parameters.

Failing example:

.. code-block:: python

    logging.info("Blending %(fruit)s", {"froot": froot})

.. code-block:: python

    logging.info("Blending %(fruit)s", {"fruit": fruit, "colour": "yellow"})

Corrected:

.. code-block:: python

    logging.info("Blending %(fruit)s", {"fruit": fruit})

LOG014 avoid ``exc_info=True`` outside of exception handlers
------------------------------------------------------------

Using ``exc_info=True`` outside of an exception handler attaches ``None`` as the exception information, leading to confusing messages:

.. code-block:: pycon

    >>> logging.warning("Uh oh", exc_info=True)
    WARNING:root:Uh oh
    NoneType: None

This rule detects logging calls with ``exc_info=True`` outside of exception handlers.

Failing example:

.. code-block:: python

    import logging

    logging.warning("Uh oh", exc_info=True)

Corrected:

.. code-block:: python

    import logging

    logging.warning("Uh oh")

LOG015 avoid logging calls on the root logger
---------------------------------------------

Using the root logger means your messages have no source information, making them less useful for debugging.
It’s better to always create a logger object, normally with:

.. code-block:: python

    logger = logging.getLogger(__name__)

If you really do need the root logger, use ``logging.getLogger(None)``.

This rule detects any call to a logging method directly on the ``logging`` module object.

Failing examples:

.. code-block:: python

    import logging

    logging.info("hello world")

.. code-block:: python

    from logging import info

    info("hello world")

Corrected:

.. code-block:: python

    import logging

    logger = logging.getLogger(__name__)

    logger.info("hello world")

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "flake8-logging",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.8",
    "maintainer_email": null,
    "keywords": "flake8",
    "author": null,
    "author_email": "Adam Johnson <me@adamj.eu>",
    "download_url": "https://files.pythonhosted.org/packages/da/1c/a8b2ab535a6559c60ae925f829153c90145f6cd7380b2b18fb3d05b77c52/flake8-logging-1.6.0.tar.gz",
    "platform": null,
    "description": "==============\nflake8-logging\n==============\n\n.. image:: https://img.shields.io/github/actions/workflow/status/adamchainz/flake8-logging/main.yml?branch=main&style=for-the-badge\n   :target: https://github.com/adamchainz/flake8-logging/actions?workflow=CI\n\n.. image:: https://img.shields.io/badge/Coverage-100%25-success?style=for-the-badge\n   :target: https://github.com/adamchainz/flake8-logging/actions?workflow=CI\n\n.. image:: https://img.shields.io/pypi/v/flake8-logging.svg?style=for-the-badge\n   :target: https://pypi.org/project/flake8-logging/\n\n.. image:: https://img.shields.io/badge/code%20style-black-000000.svg?style=for-the-badge\n   :target: https://github.com/psf/black\n\n.. image:: https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white&style=for-the-badge\n   :target: https://github.com/pre-commit/pre-commit\n   :alt: pre-commit\n\nA `Flake8 <https://flake8.readthedocs.io/en/latest/>`_ plugin that checks for issues using the standard library logging module.\n\nFor a brief overview and background, see `the introductory blog post <https://adamj.eu/tech/2023/09/07/introducing-flake8-logging/>`__.\n\nRequirements\n============\n\nPython 3.8 to 3.12 supported.\n\nInstallation\n============\n\nFirst, install with ``pip``:\n\n.. code-block:: sh\n\n     python -m pip install flake8-logging\n\nSecond, if you define Flake8\u2019s ``select`` setting, add the ``L`` prefix to it.\nOtherwise, the plugin should be active by default.\n\n----\n\n**Linting a Django project?**\nCheck out my book `Boost Your Django DX <https://adamchainz.gumroad.com/l/byddx>`__ which covers Flake8 and many other code quality tools.\n\n----\n\nRules\n=====\n\nLOG001 use ``logging.getLogger()`` to instantiate loggers\n---------------------------------------------------------\n\nThe `Logger Objects documentation section <https://docs.python.org/3/library/logging.html#logger-objects>`__ starts:\n\n  Note that Loggers should NEVER be instantiated directly, but always through the module-level function ``logging.getLogger(name)``.\n\nDirectly instantiated loggers are not added into the logger tree.\nThis means that they bypass all configuration and their messages are only sent to the `last resort handler <https://docs.python.org/3/library/logging.html#logging.lastResort>`__.\nThis can mean their messages are incorrectly filtered, formatted, and sent only to ``stderr``.\nPotentially, such messages will not be visible in your logging tooling and you won\u2019t be alerted to issues.\n\nUse |getLogger()|__ to correctly instantiate loggers.\n\n.. |getLogger()| replace:: ``getLogger()``\n__ https://docs.python.org/3/library/logging.html#logging.getLogger\n\nThis rule detects any module-level calls to ``Logger()``.\n\nFailing example:\n\n.. code-block:: python\n\n    import logging\n\n    logger = logging.Logger(__name__)\n\nCorrected:\n\n.. code-block:: python\n\n    import logging\n\n    logger = logging.getLogger(__name__)\n\nLOG002 use ``__name__`` with ``getLogger()``\n--------------------------------------------\n\nThe `logging documentation <https://docs.python.org/3/library/logging.html#logger-objects>`__ recommends this pattern:\n\n.. code-block:: python\n\n    logging.getLogger(__name__)\n\n|__name__|__ is the fully qualified module name, such as ``camelot.spam``, which is the intended format for logger names.\n\n.. |__name__| replace:: ``__name__``\n__ https://docs.python.org/3/reference/import.html?#name__\n\nThis rule detects probably-mistaken usage of similar module-level dunder constants:\n\n* |__cached__|__ - the pathname of the module\u2019s compiled versio\u02dc, such as ``camelot/__pycache__/spam.cpython-311.pyc``.\n\n  .. |__cached__| replace:: ``__cached__``\n  __ https://docs.python.org/3/reference/import.html?#cached__\n\n* |__file__|__ - the pathname of the module, such as ``camelot/spam.py``.\n\n  .. |__file__| replace:: ``__file__``\n  __ https://docs.python.org/3/reference/import.html?#file__\n\nFailing example:\n\n.. code-block:: python\n\n    import logging\n\n    logger = logging.getLogger(__file__)\n\nCorrected:\n\n.. code-block:: python\n\n    import logging\n\n    logger = logging.getLogger(__name__)\n\nLOG003 ``extra`` key ``'<key>'`` clashes with LogRecord attribute\n-----------------------------------------------------------------\n\nThe |extra documentation|__ states:\n\n.. |extra documentation| replace:: ``extra`` documentation\n__ https://docs.python.org/3/library/logging.html#logging.Logger.debug\n\n    The keys in the dictionary passed in ``extra`` should not clash with the keys used by the logging system.\n\nSuch clashes crash at runtime with an error like:\n\n.. code-block:: text\n\n    KeyError: \"Attempt to overwrite 'msg' in LogRecord\"\n\nUnfortunately, this error is only raised if the message is not filtered out by level.\nTests may therefore not encounter the check, if they run with a limited logging configuration.\n\nThis rule detects such clashes by checking for keys matching the |LogRecord attributes|__.\n\n.. |LogRecord attributes| replace:: ``LogRecord`` attributes\n__ https://docs.python.org/3/library/logging.html#logrecord-attributes\n\nFailing example:\n\n.. code-block:: python\n\n    import logging\n\n    logger = logging.getLogger(__name__)\n\n    response = acme_api()\n    logger.info(\"ACME Response\", extra={\"msg\": response.msg})\n\nCorrected:\n\n.. code-block:: python\n\n    import logging\n\n    logger = logging.getLogger(__name__)\n\n    response = acme_api()\n    logger.info(\"ACME Response\", extra={\"response_msg\": response.msg})\n\nLOG004 avoid ``exception()`` outside of exception handlers\n----------------------------------------------------------\n\nThe |exception() documentation|__ states:\n\n.. |exception() documentation| replace:: ``exception()`` documentation\n__ https://docs.python.org/3/library/logging.html#logging.exception\n\n    This function should only be called from an exception handler.\n\nCalling ``exception()`` outside of an exception handler attaches ``None`` exception information, leading to confusing messages:\n\n.. code-block:: pycon\n\n    >>> logging.exception(\"example\")\n    ERROR:root:example\n    NoneType: None\n\nUse ``error()`` instead.\nTo log a caught exception, pass it in the ``exc_info`` argument.\n\nThis rule detects ``exception()`` calls outside of exception handlers.\n\nFailing example:\n\n.. code-block:: python\n\n    import logging\n\n    response = acme_api()\n    if response is None:\n        logging.exception(\"ACME failed\")\n\nCorrected:\n\n.. code-block:: python\n\n    import logging\n\n    response = acme_api()\n    if response is None:\n        logging.error(\"ACME failed\")\n\nLOG005 use ``exception()`` within an exception handler\n------------------------------------------------------\n\nWithin an exception handler, the |exception()|__ method is preferable over ``logger.error()``.\nThe ``exception()`` method captures the exception automatically, whilst ``error()`` needs it to be passed explicitly in the ``exc_info`` argument.\nBoth methods log with the level ``ERROR``.\n\n.. |exception()| replace:: ``exception()``\n__ https://docs.python.org/3/library/logging.html#logging.Logger.exception\n\nThis rule detects ``error()`` calls within exception handlers, excluding those with a falsy ``exc_info`` argument.\n\nFailing example:\n\n.. code-block:: python\n\n    try:\n        acme_api()\n    except AcmeError as exc:\n        logger.error(\"ACME API failed\", exc_info=exc)\n\nCorrected:\n\n.. code-block:: python\n\n    try:\n        acme_api()\n    except AcmeError:\n        logger.exception(\"ACME API failed\")\n\nOr alternatively, if the exception information is truly uninformative:\n\n.. code-block:: python\n\n    try:\n        acme_api()\n    except DuplicateError:\n        logger.error(\"ACME Duplicate Error\", exc_info=False)\n\nLOG006 redundant ``exc_info`` argument for ``exception()``\n----------------------------------------------------------\n\nThe |exception()2|__ method captures the exception automatically, making a truthy ``exc_info`` argument redundant.\n\n.. |exception()2| replace:: ``exception()``\n__ https://docs.python.org/3/library/logging.html#logging.Logger.exception\n\nThis rule detects ``exception()`` calls within exception handlers with an ``exc_info`` argument that is truthy or the captured exception object.\n\nFailing example:\n\n.. code-block:: python\n\n    try:\n        acme_api()\n    except AcmeError:\n        logger.exception(\"ACME API failed\", exc_info=True)\n\nCorrected:\n\n.. code-block:: python\n\n    try:\n        acme_api()\n    except AcmeError:\n        logger.exception(\"ACME API failed\")\n\nLOG007 use ``error()`` instead of ``exception()`` with ``exc_info=False``\n-------------------------------------------------------------------------\n\nThe |exception()3|__ method captures the exception automatically.\nDisabling this by setting ``exc_info=False`` is the same as using ``error()``, which is clearer and doesn\u2019t need the ``exc_info`` argument.\n\n.. |exception()3| replace:: ``exception()``\n__ https://docs.python.org/3/library/logging.html#logging.Logger.exception\n\nThis rule detects ``exception()`` calls with an ``exc_info`` argument that is falsy.\n\nFailing example:\n\n.. code-block:: python\n\n    logger.exception(\"Left phalange missing\", exc_info=False)\n\nCorrected:\n\n.. code-block:: python\n\n    logger.error(\"Left phalange missing\")\n\nLOG008 ``warn()`` is deprecated, use ``warning()`` instead\n----------------------------------------------------------\n\nThe ``warn()`` method is a deprecated, undocumented alias for |warning()|__\n``warning()`` should always be used instead.\nThe method was deprecated in Python 2.7, in commit `04d5bc00a2 <https://github.com/python/cpython/commit/04d5bc00a219860c69ea17eaa633d3ab9917409f>`__, and removed in Python 3.13, in commit `dcc028d924 <https://github.com/python/cpython/commit/dcc028d92428bd57358a5028ada2a53fc79fc365>`__.\n\n.. |warning()| replace:: ``warning()``\n__ https://docs.python.org/3/library/logging.html#logging.Logger.warning\n\nThis rule detects calls to ``warn()``.\n\nFailing example:\n\n.. code-block:: python\n\n    logger.warn(\"Cheesy puns incoming\")\n\nCorrected:\n\n.. code-block:: python\n\n    logger.warning(\"Cheesy puns incoming\")\n\nLOG009 ``WARN`` is undocumented, use ``WARNING`` instead\n--------------------------------------------------------\n\nThe ``WARN`` constant is an undocumented alias for |WARNING|__.\nWhilst it\u2019s not deprecated, it\u2019s not mentioned at all in the documentation, so the documented ``WARNING`` should always be used instead.\n\n.. |WARNING| replace:: ``WARNING``\n__ https://docs.python.org/3/library/logging.html#logging-levels\n\nThis rule detects any import or access of ``WARN``.\n\nFailing example:\n\n.. code-block:: python\n\n    import logging\n\n    logging.WARN\n\nCorrected:\n\n.. code-block:: python\n\n    import logging\n\n    logging.WARNING\n\nLOG010 ``exception()`` does not take an exception\n-------------------------------------------------\n\nLike other logger methods, the |exception()4|__ method takes a string as its first argument.\nA common misunderstanding is to pass it an exception instead.\nDoing so is redundant, as ``exception()`` will already capture the exception object.\nIt can also lead to unclear log messages, as the logger will call ``str()`` on the exception, which doesn\u2019t always produce a sensible message.\n\n.. |exception()4| replace:: ``exception()``\n__ https://docs.python.org/3/library/logging.html#logging.Logger.exception\n\nThis rule detects ``exception()`` calls with a first argument that is the current exception handler\u2019s capture variable.\n\nFailing example:\n\n.. code-block:: python\n\n    try:\n        shuffle_deck()\n    except Exception as exc:\n        logger.exception(exc)\n\nCorrected:\n\n.. code-block:: python\n\n    try:\n        shuffle_deck()\n    except Exception:\n        logger.exception(\"Failed to shuffle deck\")\n\nLOG011 avoid pre-formatting log messages\n----------------------------------------\n\nLogger methods support string formatting for `logging variable data <https://docs.python.org/3/howto/logging.html#logging-variable-data>`__, such as:\n\n.. code-block:: python\n\n    logger.info(\"Couldn\u2019t chop %s\", vegetable)\n\nLog-aggregating tools, such as `Sentry <https://sentry.io/>`__ can group messages based on their unformatted message templates.\nUsing a pre-formatted message, such as from an f-string, prevents this from happening.\nTools have to rely on imperfect heuristics, which can lead to duplicate groups.\n\nAdditionally, the logging framework skips formatting messages that won\u2019t be logged.\nUsing a pre-formatted string, such as from an f-string, has no such optimization.\nThis overhead can add up when you have a high volume of logs that are normally skipped.\n\nThis rule detects logger method calls with a ``msg`` argument that is one of:\n\n* an f-string\n* a call to ``str.format()``\n* a string used with the modulus operator (``%``)\n* a concatenation of strings with non-strings\n\nFailing examples:\n\n.. code-block:: python\n\n    logging.error(f\"Couldn\u2019t chop {vegetable}\")\n\n.. code-block:: python\n\n    logging.error(\"Couldn\u2019t chop {}\".format(vegetable))\n\n.. code-block:: python\n\n    logging.error(\"Couldn\u2019t chop %s\" % (vegetable,))\n\n.. code-block:: python\n\n    logging.error(\"Couldn\u2019t chop \" + vegetable)\n\nCorrected:\n\n.. code-block:: python\n\n    logging.error(\"Couldn\u2019t chop %s\", vegetable)\n\nLOG012 formatting error: ``<n>`` ``<style>`` placeholders but ``<m>`` arguments\n-------------------------------------------------------------------------------\n\nLogger methods support several string formatting options for messages.\nIf there\u2019s a mismatch between the number of parameters in the message and those provided, the call will error:\n\n.. code-block:: pycon\n\n    >>> logging.info(\"Sent %s to %s\", letter)\n    --- Logging error ---\n    Traceback (most recent call last):\n      File \"/.../logging/__init__.py\", line 1110, in emit\n        msg = self.format(record)\n              ^^^^^^^^^^^^^^^^^^^\n    ...\n\n      File \"/.../logging/__init__.py\", line 377, in getMessage\n        msg = msg % self.args\n              ~~~~^~~~~~~~~~~\n    TypeError: not enough arguments for format string\n    Call stack:\n      File \"<stdin>\", line 1, in <module>\n    Message: ' %s to %s'\n    Arguments: ('Red Letter',)\n\nThis will only happen when the logger is enabled since loggers don\u2019t perform string formatting when disabled.\nThus a configuration change can reveal such errors.\n\nAdditionally, if no arguments are provided, parametrized messages are silently unformatted:\n\n.. code-block:: pycon\n\n    >>> logging.info(\"Sent %s to %s\")\n    INFO:root:Sent %s to %s\n\nThis rule detects mismatches between the number of message parameters and those provided.\nAt the moment, it only supports ``%``-style formatting with at least one parameter.\n\nFailing examples:\n\n.. code-block:: python\n\n    logging.info(\"Blending %s\")\n\n.. code-block:: python\n\n    logging.info(\"Blending %s\", fruit.name, fruit.size)\n\nCorrected:\n\n.. code-block:: python\n\n    logging.info(\"Blending %s of size %r\", fruit.name, fruit.size)\n\nLOG013 formatting error: ``<missing/unreferenced>`` keys: ``<keys>``\n--------------------------------------------------------------------\n\nWhen using named ``%``-style formatting, if the message references a missing key, the call will error:\n\n.. code-block:: pycon\n\n    >>> logging.error(\"Hi %(name)s\", {\"nam\": \"hacker\"})\n    --- Logging error ---\n    Traceback (most recent call last):\n      File \"/.../logging/__init__.py\", line 1160, in emit\n        msg = self.format(record)\n              ^^^^^^^^^^^^^^^^^^^\n    ...\n\n      File \"/.../logging/__init__.py\", line 392, in getMessage\n        msg = msg % self.args\n              ~~~~^~~~~~~~~~~\n    KeyError: 'name'\n    Call stack:\n      File \"<stdin>\", line 1, in <module>\n    Message: 'Hi %(name)s'\n    Arguments: {'nam': 'hacker'}\n\nThis will only happen when the logger is enabled since loggers don\u2019t perform string formatting when disabled.\nThus a configuration change can reveal such errors.\n\nThis rule detects mismatches between the message parameter names and those provided.\nIt only works if there\u2019s a dict literal argument for the parameters.\n\nFailing example:\n\n.. code-block:: python\n\n    logging.info(\"Blending %(fruit)s\", {\"froot\": froot})\n\n.. code-block:: python\n\n    logging.info(\"Blending %(fruit)s\", {\"fruit\": fruit, \"colour\": \"yellow\"})\n\nCorrected:\n\n.. code-block:: python\n\n    logging.info(\"Blending %(fruit)s\", {\"fruit\": fruit})\n\nLOG014 avoid ``exc_info=True`` outside of exception handlers\n------------------------------------------------------------\n\nUsing ``exc_info=True`` outside of an exception handler attaches ``None`` as the exception information, leading to confusing messages:\n\n.. code-block:: pycon\n\n    >>> logging.warning(\"Uh oh\", exc_info=True)\n    WARNING:root:Uh oh\n    NoneType: None\n\nThis rule detects logging calls with ``exc_info=True`` outside of exception handlers.\n\nFailing example:\n\n.. code-block:: python\n\n    import logging\n\n    logging.warning(\"Uh oh\", exc_info=True)\n\nCorrected:\n\n.. code-block:: python\n\n    import logging\n\n    logging.warning(\"Uh oh\")\n\nLOG015 avoid logging calls on the root logger\n---------------------------------------------\n\nUsing the root logger means your messages have no source information, making them less useful for debugging.\nIt\u2019s better to always create a logger object, normally with:\n\n.. code-block:: python\n\n    logger = logging.getLogger(__name__)\n\nIf you really do need the root logger, use ``logging.getLogger(None)``.\n\nThis rule detects any call to a logging method directly on the ``logging`` module object.\n\nFailing examples:\n\n.. code-block:: python\n\n    import logging\n\n    logging.info(\"hello world\")\n\n.. code-block:: python\n\n    from logging import info\n\n    info(\"hello world\")\n\nCorrected:\n\n.. code-block:: python\n\n    import logging\n\n    logger = logging.getLogger(__name__)\n\n    logger.info(\"hello world\")\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "A Flake8 plugin that checks for issues using the standard library logging module.",
    "version": "1.6.0",
    "project_urls": {
        "Changelog": "https://github.com/adamchainz/flake8-logging/blob/main/CHANGELOG.rst",
        "Funding": "https://adamj.eu/books/",
        "Repository": "https://github.com/adamchainz/flake8-logging"
    },
    "split_keywords": [
        "flake8"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "d6a1c904c9a311042775c21a6efb29c417272351944e2cceadb9742126e37c53",
                "md5": "3439dfd49d1e9d8ea891cffe5fb46440",
                "sha256": "a13c9d1b0fbdece88ccd2b23ce4c83576f843d105f2a5bdf2c086b82f4fe51e2"
            },
            "downloads": -1,
            "filename": "flake8_logging-1.6.0-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "3439dfd49d1e9d8ea891cffe5fb46440",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.8",
            "size": 11190,
            "upload_time": "2024-03-20T20:47:05",
            "upload_time_iso_8601": "2024-03-20T20:47:05.996576Z",
            "url": "https://files.pythonhosted.org/packages/d6/a1/c904c9a311042775c21a6efb29c417272351944e2cceadb9742126e37c53/flake8_logging-1.6.0-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "da1ca8b2ab535a6559c60ae925f829153c90145f6cd7380b2b18fb3d05b77c52",
                "md5": "2ac3694783db2456bb24dc80c679f435",
                "sha256": "6ad85b386f3e5a65f44ea150dcc878920150813252cded0af7bc41f50be4082d"
            },
            "downloads": -1,
            "filename": "flake8-logging-1.6.0.tar.gz",
            "has_sig": false,
            "md5_digest": "2ac3694783db2456bb24dc80c679f435",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.8",
            "size": 16634,
            "upload_time": "2024-03-20T20:47:07",
            "upload_time_iso_8601": "2024-03-20T20:47:07.895623Z",
            "url": "https://files.pythonhosted.org/packages/da/1c/a8b2ab535a6559c60ae925f829153c90145f6cd7380b2b18fb3d05b77c52/flake8-logging-1.6.0.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-03-20 20:47:07",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "adamchainz",
    "github_project": "flake8-logging",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "tox": true,
    "lcname": "flake8-logging"
}
        
Elapsed time: 0.20663s