Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using python 'logging' when logging.root has been redefined (Kivy)

Tags:

I've already seen Using Python logging in multiple modules and similar which recommend using:-

import logging
logger = logging.getLogger(__name__)

in every module. However I'm also using Kivy as a front-end, which has its own logging code. To summarise, Kivy's logging code sets logging.root to its own class.

What this means for me is that the handlers which Kivy assigns aren't inherited by my child modules.

How do I get around this? I've even tried manually assigning handlers as below:-

logging.getLogger(my.module.name).addHandler(handler)

and it still doesn't seem to show up in my console log. Obviously I'd prefer not to do the above once per module, as well.

EDIT

To clarify, I'd like to set up the Kivy app to be able to handle modules which use the 'recommended' log method, without any change to the module code.

like image 321
Ng Oon-Ee Avatar asked Mar 19 '16 19:03

Ng Oon-Ee


1 Answers

Why not use logging.Logger.getChild? In particular, in your module, call

mylogger = logging.getLogger().getChild(__name__)

If the module is imported after kivy.logger has been imported, the root logger's name will be kivy, and thus mylogger's name will be kivy.module.name, and more importantly, it will propagate its messages to the root logger, kivy. On the other hand, if kivy.logger has not been imported, mylogger's name will just be module.name, and thus come with no handlers attached by default.

Example: test.py:

import logging

class MyClass(object):
    def __init__(self):
        super(MyClass, self).__init__()
        self.logger = logging.getLogger().getChild(__name__)
        self.value = 0

    def act(self):
        self.value += .5
        self.logger.warning("MyClass.act: value: {}".format(self.value))

if __name__ == '__main__':
    mc = MyClass()
    mc.act()
    mc.act()
    mc.act()

And app.py:

from kivy.app import App
from kivy.logger import Logger
from kivy.clock import Clock
from kivy.properties import ObjectProperty, NumericProperty
from kivy.uix.widget import Widget
from test import MyClass

class AnApp(App):
    mc = ObjectProperty()
    value = NumericProperty(0)
    def action_c(self, dt=0):
        self.value += 1
        self.mc.act()
        Logger.info("action_c: value now {}".format(self.value))
        
    def build(self):
        self.mc = MyClass()
        Clock.schedule_interval(self.action_c, 1)
        return Widget()
AnApp().run()

And app_wo_kivy.py:

import logging
from test import MyClass
logging.getLogger().addHandler(logging.StreamHandler())

mc = MyClass()
mc.act()
mc.act()

Edit

Another approach: In the kivy app, put this first:

from kivy.logger import Logger
import logging
logging.Logger.manager.root = Logger # before importing modules using logging

With this, you should be able to just use the standard way of getting a logger (logger = logging.getLogger(__name__)). This will work for existing modules (assuming they do not mess with logging themselves ...). logging.Logger.manager holds the logger dictionary and determines the hierarchy. Whenever a new logger is created with logging.getLogger, logging.Manager._fixupParents gets called, which, when no other parent logger can be found, assigns logging.Logger.manager.root as parent. Without the above line, this is still the original root logger, hence that will be used.

Now I have no idea whether this might have unintended consequences; but at least logger name parsing should not be compromised, as the root logger's name never gets checked.

like image 130
zeeMonkeez Avatar answered Nov 09 '22 23:11

zeeMonkeez