Logging with multiple parameters in python - python

In python logging module Log is formatted using below :
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
**simple_example.py**
# 'application' code
logger.debug('debug message')
logger.info('info message')
logger.warning('warn message')
Which gives output as below :
OUTPUT:
2005-03-19 15:10:26,618 - simple_example - DEBUG - debug message
2005-03-19 15:10:26,620 - simple_example - INFO - info message
2005-03-19 15:10:26,695 - simple_example - WARNING - warn message
I am just wondering if there is any way to add multiple messages not at the end but in between i.e something like
My custom message 1 - simple_example - DEBUG - my custom message 2
Is there any way I could format it like:
formatter = logging.Formatter('%(message1)s - %(name)s - %(levelname)s - %(message2)s')
Any help would be appreciated

You could write your own Formatter class and pass your extra message as kwargs:
import logging
class MyFormatter(logging.Formatter):
def format(self, record):
record.message2 = record.args.get("message2")
return super().format(record)
logger = logging.getLogger('test')
ch = logging.StreamHandler()
formatter = MyFormatter('%(asctime)s - %(message2)s - %(name)s - %(levelname)s - %(message)s')
ch.setFormatter(formatter)
ch.setLevel(logging.ERROR)
logger.addHandler(ch)
logger.error("debug message", {"message2": "Blub"})
Output:
2019-02-08 14:33:50,487 - Blub - test - ERROR - debug message
Edit: I do not know, why this does not work out-of-the-box with INFO level, but you could do the following, which will work:
import logging
class MyFormatter(logging.Formatter):
def format(self, record):
record.message2 = record.args.get("message2")
return super().format(record)
logger = logging.getLogger('test')
ch = logging.StreamHandler()
ch.setFormatter(MyFormatter('%(asctime)s - %(message2)s - %(name)s - %(levelname)s - %(message)s'))
logging.basicConfig( level=logging.INFO, handlers=[ch] )
logger.info("debug message", {"message2": "Blub"})
Output:
2019-02-11 12:53:17,014 - Blub - test - INFO - debug message
Edit 2: For this to work w/o providing a dict with message2, you can change the code as follows:
import logging
class MyFormatter(logging.Formatter):
def format(self, record):
record.message2 = ""
if(record.args):
record.message2 = record.args.get("message2", "Fallback Value")
return super().format(record)
logger = logging.getLogger('test')
ch = logging.StreamHandler()
ch.setFormatter(MyFormatter('%(asctime)s - %(message2)s - %(name)s - %(levelname)s - %(message)s'))
logging.basicConfig( level=logging.INFO, handlers=[ch] )
logger.info("debug message", {"message2": "Blub"})
logger.info("This is my sample log")
logger.info("This is my sample log", {"hello": "World"})
Output:
2019-02-11 13:20:53,419 - Blub - test - INFO - debug message
2019-02-11 13:20:53,419 - - test - INFO - This is my sample log
2019-02-11 13:20:53,419 - Fallback Value - test - INFO - This is my sample log

You can create your logger class and decorate your methods with this:
#staticmethod
def combine(func):
def wrapped(self, *args, **kwargs):
for i,v in enumerate(args):
if v:
func(self,f"\n {chr(i+65)} : {v}",**kwargs)
return wrapped
example:
import logging
class my_log():
def __init__(self, name, debug=True):
self.logger = logging.getLogger(name)
self.logger.setLevel(logging.DEBUG)
formatter = logging.Formatter("%(asctime)s;%(levelname)s;%(message)s",
"%Y-%m-%d %H:%M:%S")
path = 'log.txt'
try:
fh = logging.FileHandler(path, 'a')
except FileNotFoundError:
fh = logging.FileHandler(path, 'w')
fh.setFormatter(formatter)
fh.setLevel(logging.DEBUG)
self.logger.addHandler(fh)
if debug:
# create console handler and set level to debug
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
# add formatter to ch
ch.setFormatter(formatter)
# add ch to logger
self.logger.addHandler(ch)
self.logger.removeHandler(fh)
#staticmethod
def combine(func):
def wrapped(self, *args, **kwargs):
for i, v in enumerate(args):
if v:
func(self, f"\n {chr(i+65)} : {v}", **kwargs)
return wrapped
def get_logger(self) -> logging.Logger:
return self.logger
#combine
def debug(self, string: str):
self.logger.debug(string)
l = my_log("test")
l.debug(1, 2, 3, 4)
output:
2022-01-24 21:23:59;DEBUG;
A : 1
2022-01-24 21:23:59;DEBUG;
B : 2
2022-01-24 21:23:59;DEBUG;
C : 3
2022-01-24 21:23:59;DEBUG;
D : 4

Hi you can use your custom message directly in Formatter and use %(message)s to place your logging message
See below example
formatter = logging.Formatter('My custome message 1 - %(name)s - %(levelname)s - %(message)s my custom message 2')

Related

why logger dont print anything?

import logging
import sys
class A(object):
def __init__(self):
ch = logging.StreamHandler(stream=sys.stdout)
ch.setLevel(logging.DEBUG)
formatter = logging.Formatter("%(asctime)s %(name)s %(levelname)s: %(message)s")
ch.setFormatter(formatter)
logger = logging.getLogger("logger_a")
logger.setLevel(logging.DEBUG)
logger.addHandler(ch)
self.logger = logger
def Xprint(self):
self.logger.info("this log a!!")
Xprint()
def Xprint():
logger = logging.getLogger("logger_b")
print logger.info("this log b!!")
a = A()
a.Xprint()
the output:
2019-10-17 19:02:20,574 logger_a INFO: this log a!!
None
why doesn't logger_b print anything?
The default loglevel is WARNING. If you want to make the logger_b to log too, then you need to do something like,
$ cat log.py
import logging
import sys
class A(object):
def __init__(self):
ch = logging.StreamHandler(stream=sys.stdout)
ch.setLevel(logging.DEBUG)
formatter = logging.Formatter("%(asctime)s %(name)s %(levelname)s: %(message)s")
ch.setFormatter(formatter)
logger = logging.getLogger("logger_a")
logger.setLevel(logging.DEBUG)
logger.addHandler(ch)
self.logger = logger
self.handler = ch
def Xprint(self):
self.logger.info("this log a!!")
Xprint(self.handler)
def Xprint(handler):
logger = logging.getLogger('logger_b') # no handler is configured yet
logger.setLevel(logging.DEBUG) # set the level
logger.addHandler(handler) # added handler
logger.info("this log b!!")
a = A()
a.Xprint()
Output:
$ python log.py
2019-10-17 17:02:26,418 logger_a INFO: this log a!!
2019-10-17 17:02:26,418 logger_b INFO: this log b!!

Right way to insert a variable in python logging format

Not able to get this one to work. Probably lack of understanding of python logging module.
Use case - print one variable on all log messages. i.e. "jobID". When multiple instances of this utility will run in parallel in same server - syslog or ../log/messages can be parsed live based on this jobID. Here is attempt with LoggerAdapter method (error lines commented) -
def startlog(self, log_folder, testname=None):
if not os.path.exists(log_folder):
os.makedirs(log_folder)
ltime = datetime.datetime.now().strftime('%Y-%m-%d_%H:%M:%S')
if testname:
logfile = "%s/%s_log_%s.log" % (log_folder, testname, ltime)
else:
logfile = "%s/log_%s.log" % (log_folder, ltime)
logger = logging.getLogger()
logger.setLevel(logging.INFO)
formated = logging.Formatter('%(asctime)s - %(levelname)s - %(module)10s - %(funcName)10s - %(message)s')
#What is desired is -
#formated = logging.Formatter('%(asctime)s - %(jobid)s - %(levelname)s - %(module)10s - %(funcName)10s - %(message)s')
if not testname:
fh = logging.FileHandler(filename=logfile)
fh.setFormatter(formated)
logger.addHandler(fh)
ch = logging.StreamHandler(sys.stdout)
ch.setFormatter(formated)
logger.addHandler(ch)
# Next line errors -
# logger = logging.LoggerAdapter(logger, {"jobid": self.jobdata.jobid})
return logger
else:
fh = logging.FileHandler(filename=logfile)
fh.setFormatter(formated)
self.log.addHandler(fh)
# Next line errors -
# logger = logging.LoggerAdapter(self.log, {"jobid": self.jobdata.jobid})
return fh
2nd Try with Filters:
def startlog(self, log_folder, t_name=None):
if not os.path.exists(log_folder):
os.makedirs(log_folder)
ltime = datetime.datetime.now().strftime('%Y-%m-%d_%H:%M:%S')
if t_name:
logfile = "%s/%s_log_%s.log" % (log_folder, t_name, ltime)
else:
logfile = "%s/log_%s.log" % (log_folder, ltime)
root = logging.getLogger()
root.setLevel(logging.INFO)
root.addFilter(ContextFilter(self.jobdata.jobid))
formated = logging.Formatter('%(asctime)s - %(jobid)s - %(levelname)s - %(module)10s - %(funcName)10s - %(message)s')
if not t_name:
fh = logging.FileHandler(filename=logfile)
fh.setFormatter(formated)
root.addHandler(fh)
ch = logging.StreamHandler(sys.stdout)
ch.setFormatter(formated)
root.addHandler(ch)
return root
else:
fh = logging.FileHandler(filename=logfile)
fh.setFormatter(formated)
self.log.addHandler(fh)
return fh
class ContextFilter(logging.Filter):
"""
This is a filter which injects contextual information into the log.
"""
def __init__(self, jobid):
self.jobid = jobid
def filter(self, record):
record.jobid = self.jobid
return True
Issue faced with filters is 'keyerror' from other modules(paramiko - transport.py). Similar to How to properly add custom logging filters in Python modules
Traceback (most recent call last):
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/logging/__init__.py", line 859, in emit
msg = self.format(record)
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/logging/__init__.py", line 732, in format
return fmt.format(record)
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/logging/__init__.py", line 474, in format
s = self._fmt % record.__dict__
KeyError: 'jobid'
Logged from file transport.py, line 1567
Dumb me, i need to add filter to the log handler. This is working so far -
def startlog(self, log_folder, t_name=None):
if not os.path.exists(log_folder):
os.makedirs(log_folder)
ltime = datetime.datetime.now().strftime('%Y-%m-%d_%H:%M:%S')
if t_name:
logfile = "%s/%s_log_%s.log" % (log_folder, t_name, ltime)
else:
logfile = "%s/log_%s.log" % (log_folder, ltime)
logger = logging.getLogger()
logger.setLevel(logging.INFO)
formated = logging.Formatter('%(asctime)s - %(jobid)s - %(levelname)s - %(module)10s - %(funcName)10s - %(message)s')
formatted_file = logging.Formatter('%(asctime)s - %(levelname)s - %(module)10s - %(funcName)10s - %(message)s')
if not t_name:
fh = logging.FileHandler(filename=logfile)
fh.setFormatter(formatted_file)
fh.addFilter(ContextFilter(self.jobdata.jobid))
logger.addHandler(fh)
ch = logging.StreamHandler(sys.stdout)
ch.setFormatter(formated)
ch.addFilter(ContextFilter(self.jobdata.jobid))
logger.addHandler(ch)
return logger
else:
fh = logging.FileHandler(filename=logfile)
fh.setFormatter(formatted_file)
fh.addFilter(ContextFilter(self.jobdata.jobid))
self.log.addHandler(fh)
return fh

Refining the logging method in python tornado

I am trying to log the actions in a function and I have written the following function to log responses to different files based on the type of response i.e. info,error,debug and warning.
logging.basicConfig(filename='indivi_service.log',
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' )
def setup_logger(logger_name, log_file, level=logging.DEBUG):
l = logging.getLogger(logger_name)
formatter = logging.Formatter()
fileHandler = logging.FileHandler(log_file)
fileHandler.setFormatter(formatter)
handler = TimedRotatingFileHandler(logger_name,
when="d",
interval=1,
backupCount=100)
l.setLevel(level)
l.addHandler(fileHandler)
l.addHandler(handler)
setup_logger('debug', r'debug')
setup_logger('error', r'error')
setup_logger('warning', r'warning')
setup_logger('info', r'info')
debug = logging.getLogger('debug')
error = logging.getLogger('error')
warning = logging.getLogger('warning')
info = logging.getLogger('info')
class Info(APIHandler):
#gen.coroutine
def post(self):
req = json.loads(self.request.body)
resp, content = client(item_id=req['item_id'])
debug.debug(content)
info.info(hello world)
warning.warn('warning message')
error.error('error message')
The problem that I am facing is that a response is printed twice each time I call a function.
for example:
info.log
hello world
hello world
Can anyone tell me why is it happening like. This is the case with all the log files.
Thanks
try:
import logging
import logging.handlers
logging.basicConfig(filename='indivi_service.log',
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' )
def setup_logger(logger_name, log_file, level=logging.DEBUG):
l = logging.getLogger(logger_name)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
handler = logging.handlers.TimedRotatingFileHandler(str(log_file)+'.log', when="d", interval=1, backupCount=100)
handler.setFormatter(formatter)
l.setLevel(level)
l.addHandler(handler)
setup_logger('debug', r'debug')
setup_logger('error', r'error')
setup_logger('warning', r'warning')
setup_logger('info', r'info')
debug = logging.getLogger('debug')
error = logging.getLogger('error')
warning = logging.getLogger('warning')
info = logging.getLogger('info')
if __name__ == "__main__":
info.info('hello world')
error.info('hello world')
after run this script, file info.log has one 'hello world' and error.log also has only one 'hello world'.

How to decorate console logger messages in Python?

I am using a logger in Python, and I want it to show some messages (not all) with colors in console.
Now I do this:
print('\033[0;41;97m')
logger.info('Colored message')
print('\033[0m')
It works well, but the problem is that there are blank lines above and below the message. I can't do this:
print('\033[0;41;97m', end='')
logger.info('Colored message')
print('\033[0m')
Because this way there is a blank line below the message. And I can't do this neither:
logger.info('\033[0;41;97m' + 'Colored message' + '\033[0m')
Because then, when I open the log file I see weird symbols. Any solution, please?
EDIT
ch = logging.StreamHandler()
fh = logging.handlers.RotatingFileHandler(log_file, 'a', max_bytes, 10)
ch_formatter = logging.Formatter('%(asctime)s - %(levelname)-4s - '
'%(message)s',
datefmt='%Y.%m.%d_%H:%M:%S_%Z')
fh_formatter = logging.Formatter('%(asctime)s - %(levelname)-4s - '
'%(message)s',
datefmt='%Y.%m.%d_%H:%M:%S_%Z')
ch.setFormatter(ch_formatter)
fh.setFormatter(fh_formatter)
logger.addHandler(ch)
logger.addHandler(fh)
UPDATE
OP says I am using a RotatingFileHandler. :)
Define a new FileHandler which escapes ansi color
# file handler for **ansi_escape**
class MyFileHandler(logging.handlers.RotatingFileHandler):
import re
ansi_escape = re.compile(r'\x1b[^m]*m')
def emit(self, record):
record.msg = self.ansi_escape.sub('', record.msg)
logging.handlers.RotatingFileHandler.emit(self, record)
Then add this and StreamHandler to logger
ch = logging.StreamHandler()
fh = MyFileHandler('file.log')
ch_formatter = logging.Formatter('%(asctime)s - %(levelname)-4s - '
'%(message)s',
datefmt='%Y.%m.%d_%H:%M:%S_%Z')
fh_formatter = logging.Formatter('%(asctime)s - %(levelname)-4s - '
'%(message)s',
datefmt='%Y.%m.%d_%H:%M:%S_%Z')
ch.setFormatter(ch_formatter)
fh.setFormatter(fh_formatter)
logger.addHandler(ch)
logger.addHandler(fh)
Define infoc... see below:
logger.infoc = lambda msg: logger.info('\033[0;41;97m' + msg + '\033[0m')
Finally you can archive...
# for colored console but not file
logger.infoc('Colored message')
# for normal
logger.info('Normal message')
EDIT
How about using wrapper for the colored messages.
logger.infoc = lambda msg: logger.info('\033[0;41;97m' + msg + '\033[0m')
logger.infoc('Colored message')
Use basicConfig as
fmt = '\033[0;41;97m %(message)s \033[0m'
logging.basicConfig(level=logging.DEBUG, format=fmt)
logger = logging.getLogger(__file__)
logger.info('hello')
Or
console = logging.StreamHandler()
console.setFormatter(logging.Formatter(fmt))
logger.addHandler(console)
logger.error('hello')
UPDATE
The color code should be in StreamHandler only.
Modify FileHandler like this:
fileh = logging.FileHandler('file.log')
fileh.setFormatter(logging.Formatter('%(message)s')) # remove the color code.
logger.addHandler(fileh)
No more weird symbols in your logfile.

Python: logging into a file and also do sys.stdout

I need to write a function that logs into a file (using logging module) and also prints the same content on the console at the same time.
What I have is :
def printScreenAndLog(msg):
log = logging.getLogger()
log.info(msg)
now = str(datetime.datetime.now())
print now,"%s" % msg
def main():
options, args = usage()
log = logging.getLogger("CMDR")
log.setLevel(logging.DEBUG)
fh = logging.FileHandler('cmdr.log')
fh.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
fh.setFormatter(formatter)
log.addHandler(fh)
printScreenAndLog("Testing")
if __name__ == "__main__":
main()
This function should do what you require:
def configure_logging():
log_format = '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
# log to stdout
logging.basicConfig(level=logging.DEBUG, format=log_format)
# also log to file
formatter = logging.Formatter(log_format)
handler = logging.FileHandler("cmdr.log")
handler.setLevel(logging.DEBUG)
handler.setFormatter(formatter)
logging.getLogger('').addHandler(handler)
did you try to set the logging level to info instead of debug?
or use log.debug(msg) in your printScreenAndLog function?

Categories

Resources