[Maya-Python] Encapsulating wrapped logger

77 views
Skip to first unread message

Marcus Ottosson

unread,
May 14, 2014, 5:44:34 AM5/14/14
to python_in...@googlegroups.com

I hope this is clear enough, I’m some having trouble myself in breaking this down properly and I’m not finding my way around the logging module well enough to properly find a good solution.

Basic

I’ve got two packages, one which defines some functionality, like “shout()” and another which wraps the original to provide additional functionality.

>>> import original
>>> original.shout()
[original] - Heyaa

What happens

The wrapper then “wraps” the original, and adds another line to shot()

>>> import wrapper
>>> wrapper.shout()
[original] - Heyaa
[wrapper] - Woorld

Here, the wrapper introduces it’s own logging, distinguished by the [wrapper] - prefix.

What I’m looking for

However, what I’d like is for the wrapper to “take over” the original logging, and log it as its own.

>>> import wrapper
>>> wrapper.shout()
[wrapper] - Heyaa
[wrapper] - Woorld

So as to fully encapsulate the original, and not leave any hint towards it being a wrapper to begin with.

Questions

  1. Does it make sense?
  2. What are the alternatives?
  3. How do I make it work?

Implementation

Let me illustrate how I’m doing it currently, without fully encapsulating the wrapper; 4 files @ 2 inits and 2 implementations.

original/__init__.py

# original/__init__.py

def setup_log():
    import logging

    formatter = logging.Formatter('[original] - %(message)s')

    log = logging.getLogger('original')
    log.setLevel(logging.INFO)

    stream_handler = logging.StreamHandler()
    stream_handler.setFormatter(formatter)
    log.addHandler(stream_handler)

wrapper/__init__.py

# wrapper/__init__.py

def setup_log():
    import logging

    formatter = logging.Formatter('[original] - %(message)s')

    log = logging.getLogger('wrapper')
    log.setLevel(logging.INFO)

    stream_handler = logging.StreamHandler()
    stream_handler.setFormatter(formatter)
    log.addHandler(stream_handler)

original/player.py

# original/player.py

import logging
log = logging.getLogger('original')

def shout():
    log.info("Heyaa")

if __name__ == '__main__':
    import original
    original.setup_log()
    shout()

# [original] - Heyaa

wrapper/player.py

# wrapper/player.py
import logging
log = logging.getLogger('wrapper')

from original import player

def shout():
    player.shout()
    log.info("Woorld")

if __name__ == '__main__':
    import wrapper
    import original
    wrapper.setup_log()
    original.setup_log()
    shout()

# [original] - Heyaa
# [wrapper] - Woorld

Best,
Marcus

--
Marcus Ottosson
konstr...@gmail.com

Justin Israel

unread,
May 14, 2014, 7:58:21 AM5/14/14
to python_in...@googlegroups.com
Hey Marcus,

My opinion is that if its a library (I am not exactly sure from the small examples, if they are) should not install handlers on the logger. They should just establish one and log to it. Then the entry point / application can set up the handlers. Something like this?

## wrapper.py ##
import logging
import original

log = logging.getLogger(__name__)
# Prevent library messages about not having a log handler
log.addHandler(logging.NullHandler())

def shout():
    original.shout()
    log.info("Woorld")

def setup_log():
    formatter = logging.Formatter('[wrapper] - %(message)s')

    root = logging.getLogger()
    root.setLevel(logging.INFO)

    stream_handler = logging.StreamHandler()
    stream_handler.setFormatter(formatter)
    root.addHandler(stream_handler)

if __name__ == '__main__':
    setup_log()
    shout()
## original.pyimport logging

log = logging.getLogger(__name__)
# Prevent library messages about not having a log handler
log.addHandler(logging.NullHandler())

def shout():
    log.info("Heyaa")

def setup_log():

    formatter = logging.Formatter('[original] - %(message)s'
)

    root = logging.getLogger()
    root.setLevel(logging.INFO)

    stream_handler = logging.StreamHandler()
    stream_handler.setFormatter(formatter)
    root.addHandler(stream_handler)

if __name__ == '__main__':
    setup_log()
    shout()
$ python wrapper.py 
[wrapper] - Heyaa
[wrapper] - Woorld

$ python original.py
[original] - Heyaa
There are probably other ways to go about it I am sure. 

(By the way, that Markdown thing is pretty awesome. Thanks)

-- justin


--
You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_m...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/python_inside_maya/CAFRtmOBN9e2Df-fVTCn49hJdrh8j174NrJgg-L370YK7P7oKBw%40mail.gmail.com.
For more options, visit https://groups.google.com/d/optout.

Marcus Ottosson

unread,
May 14, 2014, 8:38:53 AM5/14/14
to python_in...@googlegroups.com

The result looks to be what I’m looking for, but I’m struggling to make sense out of the difference. :)

Thanks, I’ll take a deeper look and try to understand. I also think libraries shouldn’t install loggers, but only log to the pre-defined ones that the user can manually set-up. But, isn’t that what is happening in my example? Without the call to setup_log(), no logging happens (except for the error messages that you muted in yours, thanks for the hint).




For more options, visit https://groups.google.com/d/optout.



--
Marcus Ottosson
konstr...@gmail.com

Justin Israel

unread,
May 14, 2014, 3:33:37 PM5/14/14
to python_in...@googlegroups.com

Its close to what you were doing. Each module can create logger instances and set some setting on it. But instead of calling a setup method for every single module you import, to install library defined logging handlers, you just install handlers on the root logger in your entry point. Now all the log messages bubble up to that single there are no other handlers catching and stopping them along the way.
You could still have methods that control finer grained handlers in the libraries but I think they should by default not produce output that might be unwanted. And then you could call something to activate say debug logging on just one library logger.

I've had some situations where I had to monkey patch a library logger to get it to stop making output on me. That means I had to look up the logger instance and modify it's handler early in the code.

Marcus Ottosson

unread,
May 17, 2014, 5:56:34 AM5/17/14
to python_in...@googlegroups.com

(By the way, that Markdown thing is pretty awesome. Thanks)

Glad you like it. :) Thought I’d mention they hotkey here as well, as the right-click menu can get quite frequent;

Ctrl+Alt+M to toggle between parsed/unparsed markdown.

Best,
Marcus




For more options, visit https://groups.google.com/d/optout.



--
Marcus Ottosson
konstr...@gmail.com

Justin Israel

unread,
May 17, 2014, 6:19:18 AM5/17/14
to python_in...@googlegroups.com
Oh trust me...I found that little nugget of joy. 


Marcus Ottosson

unread,
May 17, 2014, 6:38:37 AM5/17/14
to python_in...@googlegroups.com
Aw, I found it just now. :( Bye bye right-click.



For more options, visit https://groups.google.com/d/optout.



--
Marcus Ottosson
konstr...@gmail.com

Reply all
Reply to author
Forward
0 new messages