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.

asked Aug 5, 2020 at 3:42

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.

answered Aug 5, 2020 at 18:31
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)
answered Aug 28, 2020 at 13:14

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.