Pankaj Sharma
Pankaj Sharma

Reputation: 146

Custom log level not working with structlog

I am working with python custom log - TRACE is custom log level in below code.

With default logger, its working fine

But when I change it for structlog it is giving error.

structlog not able to identify TRACE in below code.

It seems that structlog doesn't support custom log levels ?

Already tried workaround mentioned https://github.com/hynek/structlog/issues/47

i.e

    structlog.stdlib.TRACE = TRACE = 5
    structlog.stdlib._NAME_TO_LEVEL['trace'] = TRACE

But it is not working

    TRACE = 19


    logging.addLevelName(TRACE, "TRACE")

    logging.basicConfig(
        level=os.environ.get("LOGLEVEL", "TRACE"),
        format=os.environ.get("LOGFORMAT", '%(levelname)-8s= %(asctime)-15s = %(message)s'))



    structlog.configure(
        processors=[
            structlog.stdlib.filter_by_level,
            structlog.stdlib.add_logger_name,
            structlog.stdlib.add_log_level,
            structlog.stdlib.PositionalArgumentsFormatter(),
            structlog.processors.StackInfoRenderer(),
            structlog.processors.format_exc_info,
            structlog.processors.UnicodeDecoder(),
            structlog.stdlib.render_to_log_kwargs,
        ],
        context_class=dict,
        logger_factory=structlog.stdlib.LoggerFactory(),
        wrapper_class=structlog.stdlib.BoundLogger,
        cache_logger_on_first_use=True,
    )




    #LOG = logging.getLogger() ->> Working fine
    LOG = structlog.getLogger()



    LOG.log(TRACE, "hello") ->> error on this line
    LOG.info("testing")

Upvotes: 3

Views: 3420

Answers (2)

ptd
ptd

Reputation: 3053

It is possible to create a custom level, but not very easy.

You need to remember to not only change structlog.stdlib._NAME_TO_LEVEL, but also structlog.stdlib._LEVEL_TO_NAME.

Then, you need to add a trace method to at least structlog.stdlib._FixedFindCallerLogger (if you add a trace method to structlog.stdlib.BoundLogger as well you can then call LOG.trace, which is nice).

This code should work:

import logging
import os

import structlog

TRACE = 19

structlog.stdlib.TRACE = TRACE = 5 # this overrides the 19 above with a 5, is that right?
structlog.stdlib._NAME_TO_LEVEL['trace'] = TRACE
structlog.stdlib._LEVEL_TO_NAME[TRACE] = 'trace'


def trace(self, msg, *args, **kw):
    return self.log(TRACE, msg, *args, **kw)


structlog.stdlib._FixedFindCallerLogger.trace = trace
structlog.stdlib.BoundLogger.trace = trace

logging.basicConfig(
    level=int(os.environ.get("LOGLEVEL", TRACE)),
    format=os.environ.get("LOGFORMAT", '%(levelname)-8s= %(asctime)-15s = %(message)s'))

structlog.configure(
    processors=[
        structlog.stdlib.filter_by_level,
        structlog.stdlib.add_logger_name,
        structlog.stdlib.add_log_level,
        structlog.stdlib.PositionalArgumentsFormatter(),
        structlog.processors.StackInfoRenderer(),
        structlog.processors.format_exc_info,
        structlog.processors.UnicodeDecoder(),
        structlog.stdlib.render_to_log_kwargs,
    ],
    context_class=dict,
    logger_factory=structlog.stdlib.LoggerFactory(),
    wrapper_class=structlog.stdlib.BoundLogger,
    cache_logger_on_first_use=True,
)

logging.addLevelName(TRACE, "TRACE")

LOG = structlog.getLogger()
LOG.trace('test')

Upvotes: 3

hynek
hynek

Reputation: 4126

Currently there is no official way currently as you probably have gathered from the related issue that you commented on.

You should be able to circumvent that for now by monkeypatching structlog.stdlib._NAME_TO_LEVEL.

Upvotes: 0

Related Questions