Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Where to configure logging?

I'm wondering where to configure and initialize stuff related with logging module?

For example I writing some class and I want to log some info while method will be executed. Should I configure logging in init or above class on the top of module:

# LOGGING STUFF <--- Should be here ? 

class SomeClass:

    def __init__(self):
        # class stuff
        # LOGGING STUFF <--- Or should be here ? 

    def some_method(self):
        # method stuff
        # LOGGING SOME INFO    

    def some_method2(self):
        # method stuff
        # LOGGING SOME INFO

What is the best practice ?

like image 910
Symonen Avatar asked Feb 22 '17 10:02

Symonen


1 Answers

The logging package serves two purposes, assisting the author to produce logs and assisting the user to consume logs. You might perform both those roles but thinking about them separately will help you write clean, understandable code.

Authors Concerns

Author should instantiate the logger at the right level:

  • Package logger should go in the packages __init__ file. Note the use of __name__, it'll resolve to SomePackage:

      import logging
      package_logger = logging.getLogger(__name__)
    
  • Module logger at the top of your module. Note the power of __name__! Here it'll resolve to SomePackage.SomeModule.

      import logging
      module_logger = logging.getLogger(__name__)
    
  • Class level logger could go at the top of the class definition. Note the awesome power of __name__ enhanced with getLogger and __qualname__! The loggers name will be SomePackage.SomeModule.SomeClass. Also, not the underscore in _class_logger to signal that it is for internal use.:

      class SomeClass:
          _class_logger = logging.getLogger(__name__).getChild(__qualname__)
    
  • Instance logger in the classes __init__. Use ID to produce a unique identifier. Note the stupend... you get the idea. Logger name will be SomePackage.SomeModule.SomeClass.<large_unique_number>:

      class SomeClass:
          def __init__(self):
              self._instance_logger = logging.getLogger(__name__).getChild(self.__class__.__name__).getChild(str(id(self)))
    

The names may not suit your application. For instance you may want an instance logger that is derived from one of it's instantiating args. However, you should still aim to get a grip on your logger at the right level.

Users Concerns

It's the Users job to configure handlers. Generally, the author will have ensured that no handlers will be active by default. logging.basicConfig will dump all log records of level warning and above to stderr.

import SomePackage
import logging

logging.basicConfig()

Remember you can use logging.getLogger() to access the same loggers that the author defined.

from SomePackage import SomeModule
import logging

"""
module_logger debug to a file. Will capture all 
child loggers as well if used i.e class_logger and 
instance_logger
"""
h = logging.FileHandler('debug')
h.setLevel('DEBUG')
SomeModule.module_logger.addHandler(h)

"""
class_logger warning to stderr. Will capture all child loggers
as well if used i.e instance_logger Note: No need to import
SomeClass to access it's logger as Logger keeps track of them in
a dictionary. Typically the prefered approach if your not
actually using the class.
"""
h = logging.StreamHandler()
getLogger('SomePackage.SomeModule.SomeClass').addHandler(h)

If you're debugging your own modules by calling them directly, you probably have a def main() and they should go in there. This ensures a package user won't get unexpected log files or consoles messages.

Extra Marks

If using loggers at multiple levels, deriving from existing ones is more pythonic.

module_logger = logging.getLogger(__name__)

class SomeClass:
    _class_logger = module_logger.getChild(__qualname__)

    def __init__(self):
        self._instance_logger = self._class_logger.getChild(str(id(self)))
like image 67
Guy Gangemi Avatar answered Oct 30 '22 17:10

Guy Gangemi