Strange behaviour on my logging class - python

I will try to resume as much as possible. I have this class I wrote:
Logging class
import logging, logging.handlers.TimedRotatingFileHandler
class Logger(object):
def __init__(self, log_filename):
logging.basicConfig(format='%(asctime)s %(message)s')
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
loghandler = TimedRotatingFileHandler(
log_filename, when="midnight", backupCount=50
)
loghandler.setFormatter(formatter)
self.logger = logging.getLogger()
self.logger.setLevel(logging.INFO)
self.logger.addHandler(loghandler)
def getLogger(self):
return self.logger
It works good indeed, now the problem arises when I have a script that uses a Logger instance and within that script I instantiate a class that uses a Logger too, something like this:
Script
import ClassA
A = ClassA()
log = Logger(log_filename='script_logger.log')
logger = log.getLogger()
logger.info('Initiated Script')
while True:
logger.info('Looping')
A.run()
What my class looks like:
ClassA module
class ClassA(object):
def __init__(self):
log = Logger(log_filename='class_logger.log')
self.logger = log.getLogger()
self.logger.info('Started ClassA')
def run(self):
self.logger.info('Into method run')
Now I expect to have 2 separate log files, class_logger.log and script_logger.log that works OK, but both files have exactly the same content line by line.
So script_logger.log and class_logger.log have the following content:
Started classA
Initiated Script
Looping
Into method run
Looping
Into method run
...
Any clues ?

The reason is the class and script have the same logger object when you do logging.getLogger(). It's a singleton. If you want different loggers then you should pass the logger name e.g logging.getLogger('logger1')
Typically libraries do logging.getLogger(__name__) so that each module gets a different logger. refer http://docs.python.org/2/library/logging.html#logger-objects
I have modified your code so that it works as expected now
import logging, logging.handlers
from logging.handlers import TimedRotatingFileHandler
class Logger(object):
def __init__(self, log_filename, name):
logging.basicConfig(format='%(asctime)s %(message)s')
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
loghandler = TimedRotatingFileHandler(
log_filename, when="midnight", backupCount=50
)
loghandler.setFormatter(formatter)
self.logger = logging.getLogger(name)
self.logger.setLevel(logging.INFO)
self.logger.addHandler(loghandler)
def getLogger(self):
return self.logger
class ClassA(object):
def __init__(self):
log = Logger(log_filename='class_logger.log', name="Class")
self.logger = log.getLogger()
self.logger.info('Started ClassA')
def run(self):
self.logger.info('Into method run')
A = ClassA()
log = Logger(log_filename='script_logger.log', name="Script")
logger = log.getLogger()
logger.info('Initiated Script')
for x in range(5):
logger.info('Looping')
A.run()

Related

is this a bad approach to creating a global logger, how can I improve it

Given my code, I would create a root logger instance logger = GlobalLogger(level=10).logger in __init__.py and include it in submodules where I need logging. Is there a better way to create this class instead of calling the attribute .logger to get the root logging class, or a better design approach overall?
import typing
import logging
class GlobalLogger:
MINIMUM_GLOBAL_LEVEL = logging.DEBUG
GLOBAL_HANDLER = logging.StreamHandler()
LOG_FORMAT = "[%(asctime)s] - %(levelname)s - [%(name)s.%(funcName)s:%(lineno)d] - %(message)s"
LOG_DATETIME_FORMAT = "%Y-%m-%d %H:%M:%S"
def __init__(self, level: typing.Union[int, str] = MINIMUM_GLOBAL_LEVEL):
self.level = level
self.logger = self._get_logger()
self.log_format = self._log_formatter()
self.GLOBAL_HANDLER.setFormatter(self.log_format)
def _get_logger(self):
logger = logging.getLogger(__name__)
logger.setLevel(self.level)
logger.addHandler(self.GLOBAL_HANDLER)
return logger
def _log_formatter(self):
return logging.Formatter(fmt=self.LOG_FORMAT, datefmt=self.LOG_DATETIME_FORMAT)
There is no point to do this, because loggers in Python are already managed by one global manager instance in the stdlib logging/__init__.py file.
Instead, just use logger = logging.getLogger(name) to get/create a logger at the top of each module which needs to log events. If you provide the same name you will get the same logger back, using logging.getLogger(__name__) to have one logger per module is just a popular convention.

How to print current file name with logger?

I am using the Logger model in Python.
I want the log to also print the file name from which the logger is called.
How could this be done?
This is my current implemation:
class Logger(object):
def __init__(self, name):
self.logger = logging.getLogger(name)
self.logger.setLevel(logging.INFO)
# create the logging file handler
fh = logging.FileHandler("{}.log".format(name))
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
fh.setFormatter(formatter)
# Add handle to logger object
self.logger.addHandler(fh)
def get_logger(self):
return self.logger
import logging
LOG = logging.getLogger(__name__)
if __name__ == "__main__":
logging.basicConfig()
LOG.error("Foo")
LOG.error(__file__)
Results in:
ERROR:__main__:Foo
ERROR:__main__:mylog.py
For something more complex, you can use the logger configuration to set a a log message formatter that uses the filename attribute of the LogRecords to include the filename in every log line without making it part of the log message body.
E.g.
logging.basicConfig(format="%(filename)s: %(message)s")
will result in log lines:
mylog.py: Foo
mylog.py: mylog.py

INFO level logging message not printed on DEBUG mode when defined inside class method

I just started experimenting with the logging module in python and I find it really nice. I run into a weird problem though.
I know that when on DEBUG mode logs from all levels should be outputted to console. But this doesn't seem to be the case.
Do you see anything wrong with the code bellow?
import logging
import os
class MyClass():
def __init__(self):
self.logger = logging.getLogger(
self.__class__.__name__ + '_{0}'.format(os.getpid()))
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
# create formatter
formatter = logging.Formatter(
'%(asctime)s - %(name)s - %(levelname)s - %(message)s')
# add formatter to ch
ch.setFormatter(formatter)
# add ch to logger
self.logger.addHandler(ch)
self.logger.info('server is up and running, congrats!')
In my understanding when creating an instance of MyClass an info message should be printed to my console. But that doesn't happen for some reason.
If I change the level of the message to error then I get the expected output.
If I follow the same procedure for creating the logger but not inside a class member method, then it works.
You have to setLevel to your logger self.logger as well not only your handler ch
import logging
import os
class MyClass():
def __init__(self):
self.logger = logging.getLogger(
self.__class__.__name__ + '_{0}'.format(os.getpid()))
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
self.logger.setLevel(logging.DEBUG)
# create formatter
formatter = logging.Formatter(
'%(asctime)s - %(name)s - %(levelname)s - %(message)s')
# add formatter to ch
ch.setFormatter(formatter)
# add ch to logger
self.logger.addHandler(ch)
self.logger.debug('server is up and running, congrats!')
myclass = MyClass()
The output is:
2016-03-24 10:11:44,319 - MyClass_73042 - DEBUG - server is up and running, congrats!
My own Logger(use StreamHandler and FileHandler, the log lines show on console and write to file as well):
import logging
import os
class Logger:
DEFAULT_LOG_OUTPUT = "/home/haifzhan/"
def __init__(self, logger_name, log_file_name, log_dir=DEFAULT_LOG_OUTPUT, log_level=logging.DEBUG):
self.logger = logging.getLogger(logger_name,)
self.formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
self.file_handler = logging.FileHandler(os.path.join(log_dir, log_file_name))
self.file_handler.setFormatter(self.formatter)
self.logger.setLevel(log_level)
self.logger.addHandler(self.file_handler)
self.console_handler = logging.StreamHandler()
self.console_handler.setFormatter(self.formatter)
self.console_handler.setLevel(logging.DEBUG)
self.logger.addHandler(self.console_handler)
def get_logger(self):
return self.logger
To use it, put below at the beginning of your script:
logger = Logger("my logger", "logfile.log", log_dir="/path/to/", log_level=logging.INFO).get_logger()

How to get a object from another module?

A.py
# logging object
logger = ""
def log():
"""
a log handle
"""
global logger, doc_log
import logging.handlers
logger = logging.getLogger("autons_log")
logger.setLevel(logging.DEBUG)
MAX_SIZE = 800 * 1024 * 1024
LOG_PATH = doc_log + "/autons_log.log"
fh = logging.handlers.RotatingFileHandler(LOG_PATH, maxBytes=MAX_SIZE, backupCount=8)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
fh.setFormatter(formatter)
logger.addHandler(fh)
def get_log():
"""
get the object of logger
"""
global logger
return logger
and B.py
def hello():
"""
"""
import autons_nc
print autons_nc.get_log()
print type(autons_nc.get_log())
autons_nc.get_log().debug('hello')
I want to use the object of logger in B.py,But this way can't work.
the type of get_log() is "type 'str'" not "class 'logging.Logger'".
So, another way can solve it? Thank you
By the way, autons_nc.py is A.py
You initialise logger to a string:
logger = ""
This is changed in log() to refer to an instance of logging.Logger. You don't say that you're actually calling log() before checking the type of logger -- are you?

How this logging way can be corrected

My logging module: MyLog.py
import logging
class MyLogC(logging.Filterer):
def __init__(self):
self.myLogger = logging.getLogger('')
self.myLogger.setLevel(logging.DEBUG)
self.myLogFile = logging.FileHandler("./ex.log","w")
self.myLogger.addHandler(self.myLogFile)
self.myLogFormatter= logging.Formatter('%(asctime)s %(levelname)s %(message)s')
self.myLogFile.setLevel(logging.DEBUG)
self.myLogFile.setFormatter(self.myLogFormatter)
def MyLogger(self):
return self.myLogger
In other module: MyTh.py
import MyLog
class MyThread(threading.Thread):
def __init__(self,name,value):
threading.Thread.__init__(self,None,MyClient,name,(),None,None)
...
self.logger=MyLog.MyLogC.MyLogger
def run(self):
...
self.logger.info("abc")
and using
self.logger=MyLog.MyLogC.MyLogger
But I am getting ('function' object has no attribute 'info') error while using:
self.logger.info("abc")
How can I make this work?
You are expecting self.logger to be a logger instance, but currently MyLog.MyLogC is a class, and the MyLogger is a method on that class.
Try self.logger = MyLog.MyLogC().MyLogger() (note the parens).
This first creates a MyLogC object, then invokes the MyLogger method on it to get the actual logger instance.

Categories

Resources