Wrap "py.warnings" logger with structlog to add processors

352 Views Asked by At

I have a custom logger including processors, handlers etc. . I would like to wrap the "py.warnings" logger which I understand is used by the warnings so that my processors are 'injected'. However, running the following code does not change anything to the warning that is printed to the console.


import logging
import structlog
import warnings

structlog.wrap_logger(
    logging.getLogger("py.warnings"),
    # processors etc. ...
)

warnings.warn("abc")
2

There are 2 best solutions below

0
On BEST ANSWER

This is not gonna work like this. structlog.wrap_logger() is specifically made such that it has no side-effects and you have to use its return value for logging, to use structlog.

You will have to configure logging to use structlog which is documented here. In a nutshell, structlog comes with a logging-style formatter that allows you to use structlog to format all logging entries with a dedicated processor chain.

0
On

I had the same request and made it this way, enjoy!

my_warnings.py

import logging
import warnings
from typing import Final, Union, Type

import structlog

from structlog.stdlib import _SENTINEL

WARNINGS_LOGGER_NAME: Final = "py.warnings"
FORMATTER: Final = structlog.stdlib.ProcessorFormatter(processor=YourProcessor(), foreign_pre_chain=[your_chain...])
FORMATTER.logger = logging.getLogger(WARNINGS_LOGGER_NAME)
old_format_warning = warnings.formatwarning


class MyPendingDeprecationWarning(UserWarning):
    pass



MY_WARNING_CATEGORIES = [MyPendingDeprecationWarning]


def _format_warnings(message, category, filename, lineno, line=None):
    if category not in MY_WARNING_CATEGORIES:
        return old_format_warning(message, category, filename, lineno, line)
    new_message = f"{category.__name__}: {message}"
    record = logging.LogRecord(
        FORMATTER.logger.name,
        pathname=filename,
        lineno=lineno,
        msg=new_message,
        args=(),
        exc_info=(category, category(), None),
        level=logging.WARNING,
    )
    record._logger = _SENTINEL
    record._name = _SENTINEL

    return FORMATTER.format(record)


def set_deprecation_warnings():
    warnings.formatwarning = _format_warnings
    logging.captureWarnings(True)

main.py

if __name__ == "__main__":
    my_warnings.set_deprecation_warnings()
    #... your code