How to print current file name with logger? - python

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

Related

Logging multiple scripts from the same directory in python

I have two python scripts in the same directory. I try to catch logging messages from both of them:
#script.py
import requests
import logging
logger = logging.getLogger(__name__)
class Downloader:
def __init__(self, url):
self.url = url
def download(self):
logger.debug(f'Downloading {self.url}')
req = requests.get(self.url, timeout=1)
return req
#main.py
import logging
from script import Downloader
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
handler = logging.StreamHandler()
handler.setLevel(logging.DEBUG)
logger.addHandler(handler)
logger.debug('create object')
d = Downloader('https://www.google.com')
res = d.download()
Basically I want to get rid of the debug-messages from the requests-module, so using logging.basicConfig() is not an option. But the way I do it, I do not get the debug-message from the imported script. Apparently because in script.py __name__ is not main.script.
How can I achieve this without hard coding anything to a string?
In a different module (e.g. logger.py):
import logging
def setup_logger(name, logfile, formatter, stream_handler=False, level=logging.DEBUG):
"""Function to create loggers."""
file_handler = logging.FileHandler(log_file)
stdout_handler = logging.StreamHandler()
file_handler.setFormatter(formatter)
stdout_handler.setFormatter(formatter)
logger = logging.getLogger(name)
if not logger.handlers:
logger.setLevel(level)
logger.addHandler(file_handler)
if stream_handler:
logger.addHandler(stdout_handler)
return logger
# Example formatter
formatter = logging.Formatter('%(asctime)s - %(levelname)s -> %(message)s\n')
# Generate the log object
log = setup_logger('logger_name', 'path_to_logfile', formatter)
Import this log object from your other modules to use it: from logger import log

Logging - How to use the same logger as a class for different files?

How can I use the same logger as a class for different files?
Example:
mylog.py
import logging
class MyLog():
def __init__(self):
pass
def getLogger():
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
ch.setFormatter(formatter)
logger.addHandler(ch)
logger.addHandler(file)
#etc list of formatting that I want to be the same for all the files using this class
How can I call the same logger for this file? (I'm guessing I need to pass the file name rather than using name__)
myfile.py
import mylog
logger = mylog.MyLog() #? how can I get correctly the logger from the other file?
logger.info("Test Message") #I want this logger info to use the formatting specified in mylog.py
'''
A module can contains member variables in addition to definitions. So you could do (more or less):
mylog.py
import logging
def getLogger():
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
ch.setFormatter(formatter)
logger.addHandler(ch)
logger.addHandler(file)
#etc list of formatting that I want to be the same for all the files using this class
return logger
logger = getlogger()
myfile.py
from mylog import logger
logger.info("Test Message") #I want this logger info to use the formatting specified in mylog.py

Logging across sub-modules – how to access parent logger name from sub-modules?

I am trying to add logging to my python application that has several modules and submodules. Several sites say to create child loggers in modules. The advantage I see is that the child logger inheriting the parent logging config, it will provide consistency for the logging output (handlers, formatters, ...).
So far I am defining the __main__ class logger name in each class and concatenate it with the current class’ name (parentName.childName) to get the module’s class loggers. It does not feel right, nor scalable. How could I improve this so I don’t have to hard code the __main__ class logger name in each class? Here is what my code looks like:
Py file that I run:
###app.py
import logging
from SubModule1 import *
class myApplication():
loggerName='myAPI'
def __init__(self, config_item_1=None,config_item_2=None,...config_item_N=None):
self.logger=self.logConfig()
self.logger.info("Starting my Application")
self.object1=mySubClass1(arg1, arg2, ... argM)
def logConfig(self):
fileLogFormat='%(asctime)s - %(levelname)s - %(message)s'
consoleLogFormat='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
# create logger
#logger = logging.getLogger(__name__)
logger = logging.getLogger(self.loggerName)
logger.setLevel(logging.DEBUG)
####CONSOLE HANDLER####
# create console handler and set level to debug
ch = logging.StreamHandler()
ch.setLevel(logging.INFO)
# create formatter
consoleFormatter = logging.Formatter(consoleLogFormat)
# add formatter to ch
ch.setFormatter(consoleFormatter)
# add ch to logger
logger.addHandler(ch)
####FILE HANDLER####
# create file handler and set level to debug
fh = logging.FileHandler('API_Log.txt')
fh.setLevel(logging.DEBUG)
# create formatter
fileFormatter = logging.Formatter(fileLogFormat)
# add formatter to ch
fh.setFormatter(fileFormatter)
# add ch to logger
logger.addHandler(fh)
logger.info('Logging is started!!')
return logger
def connect(self):
self.logger.info("Connecting to API")
self.object1.connect()
if __name__=="__main__":
config={"config_item_1": x,
"config_item_2": y,
...
"config_item_N": z
}
myApp=myApplication(config['config_item_1'],config['config_item_2'],...config['config_item_N'])
myApp.connect()
Module:
###SubModule1.py
import logging
import app
class mySubClass1():
appRootLoggerName='myAPI'
def __init__(self,item1):
self.logger=logging.getLogger(self.appRootLoggerName + '.'+mySubClass1.__name__)
self.logger.debug("mySubClass1 object created - mySubClass1")
self.classObject1=item1
The line below is the one that is bothering me. What alternative to self.appRootLoggerName + '.'+mySubClass1.__name__) would keep the same logging configuration shared across my application’s modules/classes?
logging.getLogger(self.appRootLoggerName + '.'+mySubClass1.__name__)

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()

Python - Avoid passing logger reference between functions?

I have a simple Python script that uses the in-built logging.
I'm configuring logging inside a function. Basic structure would be something like this:
#!/usr/bin/env python
import logging
import ...
def configure_logging():
logger = logging.getLogger("my logger")
logger.setLevel(logging.DEBUG)
# Format for our loglines
formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
# Setup console logging
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
ch.setFormatter(formatter)
logger.addHandler(ch)
# Setup file logging as well
fh = logging.FileHandler(LOG_FILENAME)
fh.setLevel(logging.DEBUG)
fh.setFormatter(formatter)
logger.addHandler(fh)
return logger
def count_parrots():
...
logger.debug??
if __name__ == '__main__':
logger = configure_logging()
logger.debug("I'm a log file")
parrots = count_parrots()
I can call logger fine from inside __main__. However, how do I call logger from inside the count_parrots() function? What's the most pythonic way of handling configuring a logger like this?
You can either use the root (default) logger, and thus the module level functions logging.debug, ... or get your logger in the function using it.
Indeed, the getLogger function is a factory-like function with a registry (singleton like), i.e. it always returns the same instance for the given logger name.
You can thus get your logger in count_parrots by simply using
logger = logging.getLogger("my logger")
at the beginning. However, the convention is to use a dotted hierarchical name for your logger. See http://docs.python.org/library/logging.html#logging.getLogger
EDIT:
You can use a decorator to add the logging behaviour to your individual functions, for example:
def debug(loggername):
logger = logging.getLogger(loggername)
def log_(enter_message, exit_message=None):
def wrapper(f):
def wrapped(*args, **kargs):
logger.debug(enter_message)
r = f(*args, **kargs)
if exit_message:
logger.debug(exit_message)
return r
return wrapped
return wrapper
return log_
my_debug = debug('my.logger')
#my_debug('enter foo', 'exit foo')
def foo(a, b):
return a+b
you can "hardcode" the logger name and remove the top-level closure and my_debug.
You can just do :
logger = logging.getLogger("my logger")
in your count_parrots() method. When you pass the name that was used earlier (i.e. "my logger") the logging module would return the same instance that was created corresponding to that name.
Update: From the logging tutorial
(emphais mine)
getLogger() returns a reference to a
logger instance with the specified
name if it is provided, or root if
not. The names are period-separated
hierarchical structures. Multiple
calls to getLogger() with the same
name will return a reference to the
same logger object.
The typical way to handle logging is to have a per-module logger stored in a global variable. Any functions and methods within that module then just reference that same logger instance.
This is discussed briefly in the intro to the advance logging tutorial in the documentation:
http://docs.python.org/howto/logging.html#advanced-logging-tutorial
You can pass logger instances around as parameters, but doing so is typically rare.
I got confused by how global variables work in Python. Within a function you only need to declare global logger if you were doing something like logger = logging.getLogger("my logger") and hoping to modify the global logger.
So to modify your example, you can create a global logger object at the start of the file. If your module can be imported by another one, you should add the NullHandler so that if the importer of the library doesn't want logging enabled, they don't have any issues with your lib (ref).
#!/usr/bin/env python
import logging
import ...
logger = logging.getLogger("my logger").addHandler(logging.NullHandler())
def configure_logging():
logger.setLevel(logging.DEBUG)
# Format for our loglines
formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
# Setup console logging
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
ch.setFormatter(formatter)
logger.addHandler(ch)
# Setup file logging as well
fh = logging.FileHandler(LOG_FILENAME)
fh.setLevel(logging.DEBUG)
fh.setFormatter(formatter)
logger.addHandler(fh)
def count_parrots():
...
logger.debug('counting parrots')
...
return parrots
if __name__ == '__main__':
configure_logging()
logger.debug("I'm a log file")
parrots = count_parrots()
If you don't need the log messages on your console, you can use in a minimalist way.
Alternatively you can use tail -f myapp.log to see the messages on the console.
import logging
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', \
filename='myapp.log', \
level=logging.INFO)
def do_something():
logging.info('Doing something')
def main():
logging.info('Started')
do_something()
logging.info('Finished')
if __name__ == '__main__':
main()
You can give logger as argument to count_parrots() Or, what I would do, create class parrots and use logger as one of its method.

Categories

Resources