How to flush Python logger

Hello,

I need help from an expert in figuring out how to flush my logger. I see data failing to print to my stream handler “console” (self.console) and if I access the logging module and have it print more stuff with my main application all the data comes out correctly.

So I’m trying to figure out how to flush all my buffers when I call the “shutdown()” method. So how would I flush my two handlers - one file handler and one stream handler when I call my shutdown method? Any help is greatly appreciated!

#
# Title: LoggingUtils_USB20F
#
#
# Module Description:
# ----------------------
# Logging module message filters
#
#
# TODO:
# ----------------------
#
#
# ----------------------------------------------------------------
# Notes:
# ----------------------------------------------------------------
#
#


import logging
from logging.handlers import QueueHandler, QueueListener
try:
    import Queue as queue
except:
    import queue
import os
import sys
dir_path = 'logs/'





class MsgFilter(logging.Filter):
    """Filter only <logging.level> messages"""
    def __init__(self, level):
        self.__level = level


    # used to filter logging records and return true/false
    def filter(self, record):
        # format mod on record members
        if record.levelno == self.__level:
            record.levelname = '[%s]' % record.levelname
            record.name = '[%s]' % record.name
            return 1
        else:
            return 0


class MsgFilterAllPass(logging.Filter):
    """Filter only <logging.level> messages"""
    def __init__(self):
        pass

    # used to filter logging records and return true/false
    def filter(self, record):
        if record.levelno == logging.CRITICAL:
            # format mod on record members
            record.levelname = '[%s]' % record.levelname
            record.name = '[%s]' % record.name
        return 1



class StreamMsgFilter(logging.Filter):
    """Filter only <logging.level> messages"""
    def __init__(self, level_from):
        self.__level_from = level_from



    # used to filter logging records and return true/false
    def filter(self, record):
        # format mod on record members
        if (record.levelno > self.__level_from):
            record.levelname = '%s' % record.levelname
            record.name = '%s' % record.name
            return 1
        else:
            return 0



class StreamMsgFilterLess(logging.Filter):
    """Filter only <logging.level> messages"""
    def __init__(self, level_from):
        self.__level_from = level_from



    # used to filter logging records and return true/false
    def filter(self, record):
        # format mod on record members
        if (record.levelno < self.__level_from):
            record.levelname = '%s' % record.levelname
            record.name = '%s' % record.name
            return 1
        else:
            return 0


class StreamMsgFilterGreater(logging.Filter):
    """Filter only <logging.level> messages"""
    def __init__(self, level_from):
        self.__level_from = level_from



    # used to filter logging records and return true/false
    def filter(self, record):
        # format mod on record members
        if (record.levelno > self.__level_from):
            record.levelname = '%s' % record.levelname
            record.name = '%s' % record.name
            return 1
        else:
            return 0




class CustomConsoleLogHandler(logging.StreamHandler):
    def __init__(self, wxDest=None, wxLogEvent=None):
        logging.Handler.__init__(self)
        self.wxDest = wxDest
        self.wxLogEvent = wxLogEvent


    def flush(self):
        """
        Do nothing
        """


    def emit(self, record):
        """Constructor"""
        msg = self.format(record)
        evt = self.wxLogEvent(message=msg, levelname=record.levelname)
        wx.PostEvent(self.wxDest, evt)





class LogClass():

    def __init__(self, name, quiet=False):
        self.LOG_ROOT_PATH = 'logs/'
        self.NAME = name
        self.LoggerInit(quiet)


    #
    # Setup logging
    #
    def LoggerInit(self, quiet):
        

        # init log folder
        path = os.getcwd() + '/' + self.LOG_ROOT_PATH
        if(not os.path.isdir(path)):
            os.mkdir(path)

        self.q = queue.Queue(-1)
        self.queue_handler = QueueHandler(self.q)


        # setup formatter for handlers
        formatter = logging.Formatter('%(levelname)-10s:: %(name)-100s: %(message)s')

        #
        # setup handler5 - log all
        # - pass all leves (DEBUG, INFO, ERROR etc...) to the file handler
        self.handler5 = logging.FileHandler(self.LOG_ROOT_PATH + self.NAME + ".log", mode='w')
        self.handler5.setLevel(logging.INFO)
        self.handler5.setFormatter(formatter)
        self.handler5.addFilter(MsgFilterAllPass())
        

        #
        # Setup stream handler to get rid of console output
        #
        self.console = logging.StreamHandler(sys.stdout)


        # set quiet mode for no console printing if necessary
        if(quiet):
            self.console.addFilter(StreamMsgFilterLess(logging.NOTSET))   
        else:
            self.console.addFilter(StreamMsgFilterGreater(logging.DEBUG))




        # create listener
        self.listener = logging.handlers.QueueListener(self.q, self.handler5, self.console)       
        self.listener.start()


        # create root logger
        self.root = logging.getLogger(self.NAME)
        self.root.setLevel(logging.DEBUG)
        self.root.addHandler(self.queue_handler)





    def write(self, level, msg):
        # check level for valid values
        opts = ["DEBUG", "INFO", "WARNING", "ERROR"]
        if(level not in opts):
            self.root.error(f"write method received <{level}> value for level var!")

        else:
            # write output
            if(level == "DEBUG"):
                self.root.debug(msg)
            elif(level == "INFO"):
                self.root.info(msg)
            elif(level == "WARNING"):
                self.root.warning(msg)
            elif(level == "ERROR"):        
                self.root.error(msg)
        



    def writeUSBPacket(self, level, msg):
        opts = ["DEBUG", "INFO", "WARNING", "ERROR"]
        if(level not in opts):
            self.root.error(f"write method received <{level}> value for level var!")
        else:
            j = ""
            k = 0
            p = 1
            addr = 0
            j += f'[Addr {addr:#04x}] '

            for i in msg:
                j += f'{i:#04x}, '
                k += 1

                if(k >= 64):
                    self.write(level, f'packet {p}:\n{j}')
                    p += 1
                    k = 0
                    j = []
                    break

                if((k % 16) == 0):
                    addr += 16
                    j += "\n"
                    j += f'[Addr {addr:#04x}] '


    def shutdown_logging(self):
        #self.handler5.flush()
        self.console.flush()
        time.sleep(500)
        #self.listener.flush()
        #logging.shutdown()
        self.root.shutdown()



The StreamHandler class has a flush method. Call it!

If you’re using a wrapper containing a StreamHandler, provide a
flush method in the wrapper which calls the inner flush method.

Hi Cameraon,

Thank you for the reply! I’ve updated my shutdown method in my logging class but I still get the same error. Is this a blocking flush() call? I’m wondering if the python application is exiting before the logger can flush? I tried a delay from the ‘time’ library and that didn’t help.

    def shutdown_logging(self):
        self.handler5.flush()
        self.console.flush()
        #time.sleep(500)
        #self.listener.flush()
        logging.shutdown()
        #self.root.shutdown()

For anyone else who is stuck on this problem, I figured out my issue and hope this helps someone else.

My problem was a queuelistener was running which needed to be stopped as well. This allows you to service all the log writes with a separate thread. This is handy but make sure your exit method calls the ‘stop()’ method for the queulistener class. Fixed shutdown method I used below.

    def shutdown_logging(self):
        self.handler5.flush()
        self.console.flush()
        self.console.close()
        self.listener.stop()

        logging.shutdown()