1

For simplicity, assume my application logs only dictionaries. I want to add a step to Python logging for my application to prevent logging any dictionary with the key password, i.e.,

def clean_log(blob):
     if 'password' in blob:
         blob['password'] = 'REDACTED'
     return blob

One thing I could do is put clean_log in its own file clean_log.py, import that in all my other files that call the logger, then add it into the function call, e.g.,

import logging
import clean_log
LOGGER = logging.getLogger()
def process(event):
    LOGGER.info(clean_log.clean_log(event))
    return event

Is there a nicer way to do this? It would be cool if I could overwrite getLogger somehow so that anytime logging.getLogger is called in the source code, it could return a modified logger that just knows to clean_logs first. For example

import logging
import clean_log
class MyLogger(logging.Logger):
    def info(self, blob):
        return super().info(clean_log.clean_log(blob))

Is there a way to always just get this logger in the source code from something like getLogger, using handlers or filters or something?

Its not totally clear to me if this is a good idea, but I thought it would be an educational experience to try to find some kind of optimal/Pythonic way to do this. I can't be the first one to want to do this.

2 Answers 2

1

There is no good way to do this in Python 3.7 or less. However, since Python 3.8, you can create a wrapper that looks like this:

import logging
import clean_log
LOGGER = logging.getLogger()
def my_info(msg):
    return LOGGER.info(clean_log.clean_log(blob), stacklevel=2)

This wrapper would still give you stack info on where my_info was called. Without that stacklevel arg, it would look like all the logs come from the wrapper, which defeats many of the good features of the logging module.

1

This is exactly what setLoggerClass is for.

E.g. I often use this snippet to allow lazy evaluation of parameters:

import logging


class LazyLogger(logging.getLoggerClass()):
    def _log(self, level, msg, args, **kwargs):
        def maybe_callable(x):
            return x() if callable(x) else x

        super()._log(
            level,
            maybe_callable(msg),
            tuple(maybe_callable(i) for i in args),
            **kwargs
        )


logging.setLoggerClass(LazyLogger)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.