Create a logger that prefix the log messages

Let's say I have a client and server class:

import logging

class Client:
    def __init__(self, name):
        self.logger = logging.getLogger(self.__class__.__name__)
        self.name = name

    def foo(self):
        self.logger.warn('[%s] foo', self.name)

class Server:
    def __init__(self):
        self.logger = logging.getLogger(self.__class__.__name__)

    def bar(self):
        self.logger.warn('bar')

      

How can I make a logger for the client that takes care of the prefix [self.name]

magically? The only idea I would like would be to set a global format including %(client)

-prefix and use a custom filter for Client

. It seems overly complex and global. I feel like there must be an easy way that I just can't see.

+4


source to share


5 answers


This can be done using a library logaugment

(disclaimer: I made this library to simplify it, but it can also be done using the standard library).

Filling in the customer name can be done as part of the log Formatter

, which formats the log entry to string, at which point you can prefix the message.

Create a logger:

import logging

logger = logging.getLogger()
handler = logging.StreamHandler()
formatter = logging.Formatter("%(client)s: %(message)s")
handler.setFormatter(formatter)
logger.addHandler(handler)

      



Provide the client name for this logger (that is, the value to fill for %(client)s

in the above formatted log line:

logaugment.set(logger, client='Client')

      

Now you can call the registration methods as usual and the customer name will be filled in automatically:

logger.warn("My message")

      

+1


source


This can be done without any additional dependency using a custom one LoggerAdapter

:

import logging

class LoggerAdapter(logging.LoggerAdapter):
    def __init__(self, prefix, logger):
        super(LoggerAdapter, self).__init__(logger, {})
        self.prefix = prefix

    def process(self, msg, kwargs):
        return '[%s] %s' % (self.prefix, msg), kwargs

class Client:
    def __init__(self, name):
        logger = logging.getLogger(self.__class__.__name__) 
        self.logger = LoggerAdapter(name, logger)
        self.name = name

    def foo(self):
        self.logger.warning('foo: %s', 'bar')

client = Client('client1')
logging.basicConfig(format='%(message)s')
client.foo()

      



For print

[client1] foo: bar

      

+10


source


Another solution is to use an additional dict to specify the prefix.

First, create your logger as usual, for example:

logger = logging.getLogger(__name__)
# Add any custom handlers, formatters for this logger
myHandler = logging.StreamHandler()
myFormatter = logging.Formatter('%(myApp)s - %(message)s') # Note the myApp key here
myHandler.setFormatter(myFormatter)
logger.addHandler(myHandler)
logger.setLevel(logging.INFO)

      

Notice how we added a new key, myApp , in. Then from the client we create a LoggerAdapter and specify an additional dict with a custom value for myApp:

logger = logging.LoggerAdapter(logger, extra={'myApp': 'Client'})
logger.info('All your base are belong to us')

      

This will print:

Client - All your base are belong to us

      

Similarly, from the server, you can log in like this:

logger = logging.LoggerAdapter(logger, extra={'myApp': 'Server'})
logger.info('All your base are belong to us')

      

This will print:

Server - All your base are belong to us

      

+1


source


I wanted to use something like PrefixedLog, wrapping a log instance in the C # world, but in the Python world, logging is set up differently (instead of passing the instance, it seems to usually be called getLogger

and relying on the handlers defined for your library).

Vitali answers well, but he fails if you try to plug in adapters because he logging.LoggerAdapter

doesn't inherit from logging.Logger

. My solution is this:

import logging

class PrefixedLogger(logging.LoggerAdapter):
    def __init__(self, prefix, logger):
        prefix = "[{}]".format(prefix)

        #hack because LoggerAdapter in not inherited from Logger
        if isinstance(logger, PrefixedLogger):
            inner_prefix = logger.prefix
            inner_logger = logger.logger
            prefix = "{} {}".format(inner_prefix, prefix)
            logger = inner_logger

        super(PrefixedLogger, self).__init__(logger, {})
        self.logger = logger
        self.prefix = prefix

    def process(self, msg, kwargs):
        return '%s %s' % (self.prefix, msg), kwargs

      

0


source


Another way is to update the current handlers to have the new format. This adds the function name pytest to the output:

@pytest.fixture(scope="function", autouse=True)
def setLoggerPrefix(request):
    ''' add the test name to the logging messages '''

    # Prepare the test name.
    testName = request.function.__name__[len("test_"):]

    logger = logging.getLogger()
    myFormatter = logging.Formatter('%(levelname)s:[' + testName + '] - %(message)s')

    # Set each logger to have this new format
    myHandler = logging.StreamHandler()
    myHandler.setFormatter(myFormatter)
    for handler in logger.handlers:
        handler.setFormatter(myHandler)

      

The output now looks like this:

INFO:[validate_page] - <some message here>
INFO:[validate_page] - <some other message here>
PASSED                                                                   [ 14%]
------------------------------ live log teardown ------------------------------
INFO:[validate_all_pages] - <some teardown message here>

tests/application/test_users/test_create_users.py::test_create_users
------------------------------- live log setup --------------------------------
INFO:[create_users] - <some message here>
INFO:[create_users] - <some other message here>
..

      

0


source







All Articles