How to extend the logger.Logging Class?
<p>I would like to start with a basic logging class that inherits from Python's <code>logging.Logger</code> 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.</p>

<p>This is what I have so far in my <code>logger.py</code> file:</p>

<pre><code>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)
</code></pre>

<p>This is the main <code>myApp.py</code>:</p>

<pre><code>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()
</code></pre>

<p>And this is the <code>entity.py</code> module:</p>

<pre><code># 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"))
</code></pre>

<p>When I run the main app, I get this:</p>

<pre><code>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!
</code></pre>

<p>Everything is printed twice, as probably I have failed to set the logger class properly.</p>

<p>--- <strong>UPDATE (01): Clarifying My Goals</strong> ---</p>

<p><strong>(1)</strong> I would like to encapsulate the main logging functionality in one single location so I can do this:</p>

<pre><code> from mylogger import MyLogger
 my_logger = MyLogger("myApp")
 my_logger.info("Hello from %s!" % ("__CMD"))
</code></pre>

<p><strong>(2)</strong> I am planning to use <code>CustomFormatter</code> and <code>CustomAdapter</code> classes. This bit does not require a custom logging class, those can be plugged in straight away.</p>

<p><strong>(3)</strong> I probably do not need to go very deep in terms of customisation of the underlying logger class (records etc.), intercepting <code>logger.info</code>, <code>loggin.debug</code> etc. should be enough.</p>

<p>So referring back to <a href="http://code.activestate.com/recipes/474089-extending-the-logging-module/" rel="nofollow">this python receipt</a> that has been circulated many times on these forums:</p>

<p>I am trying to the find the sweet point between having a <code>Logger Class</code>, yet still be able to use the built in functions like assigning <code>Formatters</code> and <code>Adapters</code> etc. So everything stays compatible with the <code>logging</code> module.</p>

<pre><code>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
</code></pre>

<p>--- <strong>UPDATE (02): A work-in-progress possible solution</strong> ---</p>

<p>I have created a repo with a simple python app demonstrating a possible solution. Please feel free to peak and help me improve this.</p>

<p><a href="https://github.com/symbolix/xlog_example" rel="nofollow">xlog_example</a></p>

<p>This example effectively demonstrates the technique of overriding the <code>logging.Logger</code> class and the <code>logging.LogRecord</code> class through inheritance.</p>

<p>Two external items are mixed into the log stream: <code>funcname</code> and <code>username</code> without using any <code>Formatters</code> or <code>Adapters</code>.</p>

<p>At this stage, I believe that the research I have made so far and the example provided with the intention to wrap up the solution is sufficient to serve as an answer to my question. In general, there are many approaches that may be utilised to wrap a logging solution. This particular question aimed to focus on a solution that utilises <code>logging.Logger</code> class inheritance so that the internal mechanics can be altered, yet the rest of the functionality kept as it is since it is going to be provided by the original <code>logging.Logger</code> class.</p>

<p>Having said that, class inheritance techniques should be used with great care. Many of the facilities provided by the logging module are already sufficient to maintain and run a stable logging workflow. Inheriting from the <code>logging.Logger</code> class is probably good when the goal is some kind of a fundamental change to the way the log data is processed and exported.</p>

<p>To summarise this, I see that there are two approaches for wrapping logging functionality:</p>

<p><strong>1) The Traditional Logging:</strong></p>

<p>This is simply working with the provided logging methods and functions, but wrap them in a module so that some of the generic repetitive tasks are organised in one place. In this way, things like log files, log levels, managing custom <code>Filters</code>, <code>Adapters</code> etc. will be easy.</p>

<p><em>I am not sure if a <code>class</code> approach can be utilised in this scenario (and I am not talking about a super class approach which is the topic of the second item) as it seems that things are getting complicated when the logging calls are wrapped inside a class. I would like to hear about this issue and I will definitely prepare a question that explores this aspect.</em></p>

<p><strong>2) The Logger Inheritance:</strong></p>

<p>This approach is based on inheriting from the original <code>logging.Logger</code> class and adding to the existing methods or entirely hijacking them by modifying the internal behaviour. The mechanics are based on the following bit of code:</p>

<pre><code># Register our logger.
logging.setLoggerClass(OurLogger)
my_logger = logging.getLogger("main")
</code></pre>

<p>From here on, we are relying on our own Logger, yet we are still able to benefit from all of the other logging facilities:</p>

<pre><code># We still need a loggin handler.
ch = logging.StreamHandler()
my_logger.addHandler(ch)

# Confgure a formatter.
formatter = logging.Formatter('LOGGER:%(name)12s - %(levelname)7s - &lt;%(filename)s:%(username)s:%(funcname)s&gt; %(message)s')
ch.setFormatter(formatter)

# Example main message.
my_logger.setLevel(DEBUG)
my_logger.warn("Hi mom!")
</code></pre>

<p>This example is crucial as it demonstrates the injection of two data bits <code>username</code> and <code>funcname</code> without using custom <code>Adapters</code> or <code>Formatters</code>. </p>

<p>Please see the <a href="https://github.com/symbolix/xlog_example" rel="nofollow" title="xlog.py example">xlog.py repo</a> for more information regarding this solution. This is an example that I have prepared based on <a href="http://stackoverflow.com/questions/10973362/python-logging-function-name-file-name-line-number-using-a-single-file">other questions</a> and bits of code from other <a href="http://code.activestate.com/recipes/474089-extending-the-logging-module/" rel="nofollow">sources</a>.</p>

python logging