Search code examples
python-3.xloggingfilehandler

Python logger confusion


I am trying to understand the python logging, and I have this:

Main.py:

import logging
from Foo import Foo

logging.basicConfig(level=logging.DEBUG)

fh_formatter = logging.Formatter('[%(asctime)s : %(levelname)s : %(name)s] : %(message)s')
file_handler = logging.FileHandler('logger.log', mode='w')
file_handler.setFormatter(fh_formatter)
file_handler.setLevel(logging.DEBUG)

sh_formatter = logging.Formatter('%(message)s')
stream_handler = logging.StreamHandler()
stream_handler.setFormatter(sh_formatter)
stream_handler.setLevel(logging.DEBUG)

logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
logger.addHandler(file_handler)
logger.addHandler(stream_handler)

logger.info('Running main')
Foo().print_foo()

Foo.py

import logging
from Bar import Bar

logger = logging.getLogger(__name__)

class Foo():

    def print_foo(self):
        Bar().print_bar()
        logger.info('Inside Foo.foo')

Bar.py:

import logging

logger = logging.getLogger(__name__)

class Bar():

    def print_bar(self):
        logger.info('Inside Bar.bar')

When I run the code, on the console I see this output:

Running main
INFO:__main__:Running main
INFO:Bar:Inside Bar.bar
INFO:Foo:Inside Foo.foo

But when I check the logger.log, I see only one line

[2019-03-23 18:46:01,276 : INFO : __main__] : Running main

How can I make it so that I see all the lines in both the places? Do I need to set a file handler and stream handler for each logger in each file? If I have a python project, and I want to have a single log file along with console output, what is the right way to do this?


Solution

  • The problem is on this line in Foo and Bar:

    logger = logging.getLogger(__name__)
    

    Note that you only reset the logging handler in Main.py, with the logger name of __name__, which resolved to "__main__" as you can see in the output. When you get the logger from Foo.py, your logger name is "Foo" and hence everything you set to "__main__" is not applied.

    What you want to do: Change the __name__ part. For example, your app is called fubar then you do this in Main.py:

    logger = logging.getLogger("fubar")
    

    and in Foo.py, as a submodule in the app, do this:

    logger = logging.getLogger("fubar.Foo")
    

    Or you can even use the same name if you wish. This way, the handler assigned at higher level will pass on to sub-levels.