0

I get a regular non-root logger of the default type:

logger = logging.getLogger('a')

Now I want to change the type and get a new logger for the same name:

logging.setLoggerClass(type('NewLoggerClass', (logging.Logger,), {}))
logger = logging.getLogger('a')

I would like the second call to getLogger to respect the new logger class I set. However, logger remains the same instance of logging.Logger instead of __main__.NewLoggerClass.

How do I either remove an existing logger for a given name, or replace it with one of a different type?

Mad Physicist
  • 107,652
  • 25
  • 181
  • 264
  • maybe this helps https://stackoverflow.com/questions/16686788/python-how-to-kill-a-class-instance-object – Marcus.Aurelianus Nov 04 '22 at 09:43
  • @Marcus.Aurelianus. Not really. There is a mapping of names to loggers, and removing it from there would be ideal. Figuring out a documented way of doing so would appear to be non-trivial. – Mad Physicist Nov 04 '22 at 09:44

2 Answers2

1

You can't do this (other than low-level hackery) once a logger has been created. You'll need to call setLoggerClass() before you instantiate any loggers via getLogger().

Vinay Sajip
  • 95,872
  • 14
  • 179
  • 191
  • I was afraid of that, but I'm wondering if there's any documentation for it? If not, I'll wait a bit to see if someone else knows. – Mad Physicist Nov 04 '22 at 11:28
  • @MadPhysicist as I'm the maintainer of stdlib logging, I _should_ know :-) There's no specific documentation for it, other than a statement that loggers are singletons (implying no recreation)- https://docs.python.org/3/howto/logging-cookbook.html#using-loggers-as-attributes-in-a-class-or-passing-them-as-parameters – Vinay Sajip Nov 04 '22 at 19:33
  • Fair enough :) What about the other answer? It appears to use a bunch of undocumented but technically public APIs. Did the manager show up in py3.6?, or was that only the one in LoggerAdapter? – Mad Physicist Nov 04 '22 at 19:44
  • @MadPhysicist If they are undocumented, they are subject to change. Although nowadays there is a convention of prefixing private APIs with _, it was not always so and especially not in very old code, as this part of logging is (it dates from even before logging was added to Python). See also the documentation for `setLoggerClass()`, which states "This function is typically called before any loggers are instantiated by applications which need to use custom logger behavior. After this call ... do not instantiate loggers directly using subclass: ... use the getLogger() API to get your loggers." – Vinay Sajip Nov 04 '22 at 20:02
  • Thanks. I think that explains everything. All this came about because I wrote a function that was doing another thing that the documentation says to avoid ([`haggis.logs.add_logging_level`](https://haggis.readthedocs.io/en/stable/api.html#haggis.logs.add_logging_level)), although I think I was pretty thorough, and was trying to write some unit tests that would leave the majority of the logging system intact. I really appreciate the time you took to explain the internals: it helped a lot. – Mad Physicist Nov 05 '22 at 13:25
1

If in doubt ... "Use the source, Luke!"

So spelunking into logging/__init__.py, one can see that a call to logging.getLogger actually invokes Logger.manager.getLogger.

Logger.manager is itself an instance of Manager that was instatiated on module load.

Hunting in the Manager class shows that it checks for existing logger objects in self.loggerDict. This attribute acts as the 'cache' for instantiated logger class.

So if you want to replace a certain logger with a new logger class, you probably can do it by deleting it first. Maybe like this:

    del logging.Logger.manager.loggerDict['a']
    logging.setLoggerClass(type('NewLoggerClass', (logging.Logger,), {}))
    logger = logging.getLogger('a')

Try it out!

pepoluan
  • 6,132
  • 4
  • 46
  • 76
  • Very nice. And no "private" attributes, though they aren't documented anywhere I could find (yet) – Mad Physicist Nov 04 '22 at 17:00
  • 1
    @MadPhysicist Not documented means you use at your own risk. This is but one example of "low-level hackery" I mention in my answer. – Vinay Sajip Nov 04 '22 at 19:35
  • I agree with @VinaySajip ... "Use this at your own risk" ... future changes _might_ make this not work or has unintended side-effects. That said, this is part of Python stdlib that's relatively mature and less likely to have breaking changes. – pepoluan Nov 05 '22 at 07:53