tags:

views:

39

answers:

2

I'm writing python package/module and would like the logging messages mention what module/class/function they come from. I.e. if I run this code:

import mymodule.utils.worker as worker

w = worker.Worker()
w.run()

I'd like to logging messages looks like this:

2010-06-07 15:15:29 INFO mymodule.utils.worker.Worker.run <pid/threadid>: Hello from worker

How can I accomplish this?

Thanks.

+3  A: 

I tend to use the logging module in my packages/modules like so:

import logging
log = logging.getLogger(__name__)
log.info("Whatever your info message.")

This sets the name of your logger to the name of the module for inclusion in the log message. You can control where the name is by where %(name)s is found in the format string. Similarly you can place the pid with %(process)d and the thread id with %(thread)d. See the docs for all the options.

Formatting example:

import logging
logging.basicConfig(format="%(asctime)s %(levelname)s %(name)s %(process)d/%(threadName)s: %(message)s")
logging.getLogger('this.is.the.module').warning('Testing for SO')

Gives me:

2010-06-07 08:43:10,494 WARNING this.is.the.module 14980/MainThread: Testing for SO
Mike Boers
Yes, I've thought about `__name__`, but it has only the name of the current module, not the full `<package>.<subpackage>.<subsubpackage>.<module>` string. Do you know how can I get such string?BTW, `%(process)d` is great hint, thanks!
Zaar Hai
@Zaar: `__name__` is the full name as imported, not just the last part (assuming the standard import mechanism). Do give it a try.
Mike Boers
Yes, I see it does! Now I'm all set. Thank you!
Zaar Hai
A: 

Here is my solution that came out of this discussion. Thanks to everyone for suggestions.

Usage:

>>> import logging
>>> logging.basicConfig(level=logging.DEBUG)
>>> from hierlogger import hierlogger as logger
>>> def main():
...     logger().debug("test")
...
>>> main()
DEBUG:main:test

By default it will name logger as ... You can also control the depth by providing parameter:
3 - module.class.method default
2 - module.class
1 - module only

Logger instances are also cached to prevent calculating logger name on each call. I hope someone will enjoy it.

The code:

import logging
import inspect

class NullHandler(logging.Handler):
        def emit(self, record): pass

def hierlogger(level=3):
    callerFrame = inspect.stack()[1]
    caller = callerFrame[0]
    lname = '__heirlogger'+str(level)+'__'
    if lname not in caller.f_locals:
        loggerName = str()
        if level >= 1:
            try:
                loggerName += inspect.getmodule(inspect.stack()[1][0]).__name__
            except: pass
        if 'self' in caller.f_locals and (level >= 2):
            loggerName += ('.' if len(loggerName) > 0 else '') + 
                          caller.f_locals['self'].__class__.__name__
        if callerFrame[3] != '' and level >= 3:
            loggerName += ('.' if len(loggerName) > 0 else '') + callerFrame[3]
        caller.f_locals[lname] = logging.getLogger(loggerName)
        caller.f_locals[lname].addHandler(NullHandler())
        return caller.f_locals[lname]
Zaar Hai