I would like to start with a basic logging class that inherits from Python's logging.Logger
class. However, I am not sure about how I should be constructing my class so that I can establish the basics needed for customising the inherited logger.
This is what I have so far in my logger.py
file:
import sys
import logging
from logging import DEBUG, INFO, ERROR
class MyLogger(object):
def __init__(self, name, format="%(asctime)s | %(levelname)s | %(message)s", level=INFO):
# Initial construct.
self.format = format
self.level = level
self.name = name
# Logger configuration.
self.console_formatter = logging.Formatter(self.format)
self.console_logger = logging.StreamHandler(sys.stdout)
self.console_logger.setFormatter(self.console_formatter)
# Complete logging config.
self.logger = logging.getLogger("myApp")
self.logger.setLevel(self.level)
self.logger.addHandler(self.console_logger)
def info(self, msg, extra=None):
self.logger.info(msg, extra=extra)
def error(self, msg, extra=None):
self.logger.error(msg, extra=extra)
def debug(self, msg, extra=None):
self.logger.debug(msg, extra=extra)
def warn(self, msg, extra=None):
self.logger.warn(msg, extra=extra)
This is the main myApp.py
:
import entity
from core import MyLogger
my_logger = MyLogger("myApp")
def cmd():
my_logger.info("Hello from %s!" % ("__CMD"))
entity.third_party()
entity.another_function()
cmd()
And this is the entity.py
module:
# Local modules
from core import MyLogger
# Global modules
import logging
from logging import DEBUG, INFO, ERROR, CRITICAL
my_logger = MyLogger("myApp.entity", level=DEBUG)
def third_party():
my_logger.info("Initial message from: %s!" % ("__THIRD_PARTY"))
def another_function():
my_logger.warn("Message from: %s" % ("__ANOTHER_FUNCTION"))
When I run the main app, I get this:
2016-09-14 12:40:50,445 | INFO | Initial message from: __THIRD_PARTY!
2016-09-14 12:40:50,445 | INFO | Initial message from: __THIRD_PARTY!
2016-09-14 12:40:50,445 | WARNING | Message from: __ANOTHER_FUNCTION
2016-09-14 12:40:50,445 | WARNING | Message from: __ANOTHER_FUNCTION
2016-09-14 12:40:50,445 | INFO | Hello from __CMD!
2016-09-14 12:40:50,445 | INFO | Hello from __CMD!
Everything is printed twice, as probably I have failed to set the logger class properly.
Update 1
Let me clarify my goals.
(1) I would like to encapsulate the main logging functionality in one single location so I can do this:
from mylogger import MyLogger
my_logger = MyLogger("myApp")
my_logger.info("Hello from %s!" % ("__CMD"))
(2) I am planning to use CustomFormatter
and CustomAdapter
classes. This bit does not require a custom logging class, those can be plugged in straight away.
(3) I probably do not need to go very deep in terms of customisation of the underlying logger class (records etc.), intercepting logger.info
, loggin.debug
etc. should be enough.
So referring back to this python receipt that has been circulated many times on these forums:
I am trying to the find the sweet point between having a Logger Class
, yet still be able to use the built in functions like assigning Formatters
and Adapters
etc. So everything stays compatible with the logging
module.
class OurLogger(logging.getLoggerClass()):
def makeRecord(self, name, level, fn, lno, msg, args, exc_info, func=None, extra=None):
# Don't pass all makeRecord args to OurLogRecord bc it doesn't expect "extra"
rv = OurLogRecord(name, level, fn, lno, msg, args, exc_info, func)
# Handle the new extra parameter.
# This if block was copied from Logger.makeRecord
if extra:
for key in extra:
if (key in ["message", "asctime"]) or (key in rv.__dict__):
raise KeyError("Attempt to overwrite %r in LogRecord" % key)
rv.__dict__[key] = extra[key]
return rv
Update 2
I have created a repo with a simple python app demonstrating a possible solution. However, I am keen to improve this.
This example effectively demonstrates the technique of overriding the logging.Logger
class and the logging.LogRecord
class through inheritance.
Two external items are mixed into the log stream: funcname
and username
without using any Formatters
or Adapters
.