Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use different loggers without passing logger variable? (Python logging)

Tags:

python

logging

I have multiple modules that use the same utils.py package. How do I make the logger in utils.py differ without having to pass the logger variable from the callers(i.e. ClassA or ClassB)?

Very simple sample codes are as following. In reality, I have a lot of functions and classes in utils.py, and that's why I don't want to pass the logger variable into utils.py.

~/test-two-loggers$ tree .

├── main.py
├── configs.py
├── ClassA.py
├── ClassB.py
└── utils.py

0 directories, 5 files

main.py

import ClassA
import ClassB

ClassA.func()
ClassB.func()

ClassA.py

import utils
import configs
import logging

def func():
    logger = logging.getLogger("classA")
    logger.info("in ClassA")
    utils.common_func(logger)  # I want to change this line!!!!

ClassB.py

import utils
import configs
import logging

def func():
    logger = logging.getLogger("classB")
    logger.info("in ClassB")
    utils.common_func(logger)  # I want to change this line!!!!

utils.py

def common_func(logger):  # I want to change this line!!!!
    # do a lot of things ClassA and ClassB both need to do
    logger.info("in utils - step one finished")
    # do a lot of things ClassA and ClassB both need to do
    logger.info("in utils - step two finished")
    # do a lot of things ClassA and ClassB both need to do
    logger.info("in utils - step three finished")

configs.py

import logging.config

logging_config = {
        "version": 1, 
        "formatters": {
            "formatter_a": {
                "format": u"[A][%(levelname)s] %(module)s.%(lineno)d: %(message)s"
            },
            "formatter_b": {
                "format": u"[B][%(levelname)s] %(module)s.%(lineno)d: %(message)s"
            },
        },
        "handlers": {
            "console_a": {
                "class": "logging.StreamHandler",
                "level": "DEBUG",
                "formatter": "formatter_a",
                "stream": "ext://sys.stdout"
            },
            "console_b": {
                "class": "logging.StreamHandler",
                "level": "DEBUG",
                "formatter": "formatter_b",
                "stream": "ext://sys.stdout"
            },
        },
        "loggers": {
            "classA": {
                "level": "DEBUG",
                "handlers": ["console_a"],
                "propagate": "no"
            },
            "classB": {
                "level": "DEBUG",
                "handlers": ["console_b"],
                "propagate": "no"
            },
        },
}

logging.config.dictConfig(logging_config)

Result I want:

~/test-two-loggers$ python main.py 
[A][INFO] ClassA.7: in ClassA
[A][INFO] utils.3: in utils - step one finished
[A][INFO] utils.5: in utils - step two finished
[A][INFO] utils.7: in utils - step three finished
[B][INFO] ClassB.7: in ClassB
[B][INFO] utils.3: in utils - step one finished
[B][INFO] utils.5: in utils - step two finished
[B][INFO] utils.7: in utils - step three finished

But I want another solution other than this. I don't want to pass the logger variable into utils.

like image 901
AnnieFromTaiwan Avatar asked May 03 '16 14:05

AnnieFromTaiwan


People also ask

How do I override a Python logger?

You can firstly implement your own logger class by deriving from logging. Logger, in which you override the methods you target. And then you pass the logger class to the logging system by using logging. setLoggerClass.

How do I create a multiple logging level in Python?

You can set a different logging level for each logging handler but it seems you will have to set the logger's level to the "lowest". In the example below I set the logger to DEBUG, the stream handler to INFO and the TimedRotatingFileHandler to DEBUG. So the file has DEBUG entries and the stream outputs only INFO.


1 Answers

Looks like you're looking for something like implicit parameters.

This is something Python doesn't have (Explicit is better than implicit).

But, as always, there is a more or less elegant way to emulate it:

class LoggerWrapper:
    def __init__(self, logger_name):
        self.logger = logging.getLogger(logger_name)

    def common_func(self):
        pass # do stuff here

logger = LoggerWrapper('classA')
logger.common_func()
like image 184
GingerPlusPlus Avatar answered Sep 17 '22 15:09

GingerPlusPlus