Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Storing logger messages in a string

Tags:

python

logging

I wanted to store all the intermediate log messages (warn, info, error) to a string in Python, and report those log messages to the console at the end of program.

I tried to follow the steps outlined in http://opensourcehacker.com/2011/02/23/temporarily-capturing-python-logging-output-to-a-string-buffer/ but was unsuccessful .

Could somebody tell me a short, clean way to do this?

This is what I've tried for now:

log = logging.getLogger('basic_logger')
log.setLevel(logging.DEBUG)
report = ""

memory_handler = logging.handlers.MemoryHandler(1024*20, logging.ERROR, report)
memory_handler.setLevel(logging.DEBUG)
log.addHandler(memory_handler)
    
log.info("hello world")
    
memory_handler.flush()
    
print "report:", report
like image 548
trup Avatar asked Aug 13 '15 22:08

trup


People also ask

Are loggers thread safe Python?

You can log directly from multiple threads because the logging module is thread-safe. In this tutorial you will discover how to log safely from many threads.

What are logging messages?

The message logging facility, when active, writes messages to the log data set containing all data that simulated resources transmit or receive in a specified network. Most users use the message logging facility because of its usefulness for analyzing network simulations.

What is getLogger in Python?

To start logging using the Python logging module, the factory function logging. getLogger(name) is typically executed. The getLogger() function accepts a single argument - the logger's name. It returns a reference to a logger instance with the specified name if provided, or root if not.

Why is logging better than printing?

One of the advantages of using the logging module to track our codes is the ability to format the messages based on our needs. For example, in my code, I would like to log the date and time with appropriate messages. Here is an example. And here is the output.


3 Answers

It can be as simple as logging to a StringIO object:

import logging
try:
    from cStringIO import StringIO      # Python 2
except ImportError:
    from io import StringIO

log_stream = StringIO()    
logging.basicConfig(stream=log_stream, level=logging.INFO)

logging.info('hello world')
logging.warning('be careful!')
logging.debug("you won't see this")
logging.error('you will see this')
logging.critical('critical is logged too!')

print(log_stream.getvalue())

Output

INFO:root:hello world
WARNING:root:be careful!
ERROR:root:you will see this
CRITICAL:root:critical is logged too!


If you want to log only those messages at levels WARN, INFO and ERROR you can do it with a filter. LevelFilter below checks each log record's level no, allowing only those records of the desired level(s):

import logging
try:
    from cStringIO import StringIO      # Python 2
except ImportError:
    from io import StringIO

class LevelFilter(logging.Filter):
    def __init__(self, levels):
        self.levels = levels

    def filter(self, record):
        return record.levelno in self.levels

log_stream = StringIO()    
logging.basicConfig(stream=log_stream, level=logging.NOTSET)
logging.getLogger().addFilter(LevelFilter((logging.INFO, logging.WARNING, logging.ERROR)))

logging.info('hello world')
logging.warning('be careful!')
logging.debug("you won't see this")
logging.error('you will see this')
logging.critical('critical is no longer logged!')

print(log_stream.getvalue())

Output

INFO:root:hello world
WARNING:root:be careful!
ERROR:root:you will see this

like image 159
mhawke Avatar answered Oct 28 '22 19:10

mhawke


Note that solutions involving basicConfig set attributes of the root logger which all other loggers inherit from, this can be unwanted because libraries will also log to it. My use case is a website that calls a data processing module, and I only want to capture that module's logs specifically. This also has the advantage of allowing existing handlers that log to file and the terminal to persist:

import io, logging
from django.http import HttpResponse

log_stream = io.StringIO()
log_handler = logging.StreamHandler(log_stream)
logging.getLogger('algorithm.user_output').addHandler(log_handler)

algorithm()
return HttpResponse(f'<pre>{log_stream.getvalue()}</pre>')

In algorithm.py:

logger = logging.getLogger(__name__ + '.user_output')  # 'algorithm.user_output'
like image 35
xjcl Avatar answered Oct 28 '22 18:10

xjcl


You can also write your own stream class. As https://docs.python.org/2/library/logging.handlers.html says, only writeand flushare used for the streaming.

Example:

import logging

class LogStream(object):
    def __init__(self):
        self.logs = ''

    def write(self, str):
        self.logs += str

    def flush(self):
        pass

    def __str__(self):
        return self.logs

log_stream = LogStream()
logging.basicConfig(stream=log_stream, level=logging.DEBUG)

log = logging.getLogger('test')
log.debug('debugging something')
log.info('informing user')

print(log_stream)

Outputs:

DEBUG:test:debugging something
INFO:test:informing user
like image 20
Azial Avatar answered Oct 28 '22 19:10

Azial