How can INFO and DEBUG logging message be sent to stdout and higher level message to stderr

后端 未结 7 970
悲哀的现实
悲哀的现实 2020-12-10 00:32

Is there an easy way with python\'s logging module to send messages with a DEBUG or INFO level and the one with a higher level to different streams?

Is it a good ide

相关标签:
7条回答
  • 2020-12-10 01:01

    Just for your convenience adding everything together with the formatter in one package:

    # shared formatter, but you can use separate ones:
    FORMAT = '%(asctime)s - %(name)s - %(levelname)s - %(threadName)s - %(message)s'
    formatter = logging.Formatter(FORMAT)
    
    # single app logger:
    log = logging.getLogger(__name__)
    log.setLevel(logging.INFO)
    
    # 2 handlers for the same logger:
    h1 = logging.StreamHandler(sys.stdout)
    h1.setLevel(logging.DEBUG)
    # filter out everything that is above INFO level (WARN, ERROR, ...)
    h1.addFilter(lambda record: record.levelno <= logging.INFO)
    h1.setFormatter(formatter)
    log.addHandler(h1)
    
    h2 = logging.StreamHandler(sys.stderr)
    # take only warnings and error logs
    h2.setLevel(logging.WARNING)
    h2.setFormatter(formatter)
    log.addHandler(h2)
    
    # profit:
    log.info(...)
    log.debug(...)
    

    My use case was to redirect stdout to a datafile while seeing errors on the screen during processing.

    0 讨论(0)
  • 2020-12-10 01:04
    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    import logging
    import sys
    
    class LessThenFilter(logging.Filter):
        def __init__(self, level):
            self._level = level
            logging.Filter.__init__(self)
    
        def filter(self, rec):
            return rec.levelno < self._level
    
    log = logging.getLogger()
    log.setLevel(logging.NOTSET)
    
    sh_out = logging.StreamHandler(stream=sys.stdout)
    sh_out.setLevel(logging.DEBUG)
    sh_out.setFormatter(logging.Formatter('%(levelname)s: %(message)s'))
    sh_out.addFilter(LessThenFilter(logging.WARNING))
    log.addHandler(sh_out)
    
    sh_err = logging.StreamHandler(stream=sys.stderr)
    sh_err.setLevel(logging.WARNING)
    sh_err.setFormatter(logging.Formatter('%(levelname)s: %(message)s'))
    log.addHandler(sh_err)
    
    logging.critical('x')
    logging.error('x')
    logging.warning('x')
    logging.info('x')
    logging.debug('x')
    
    0 讨论(0)
  • 2020-12-10 01:05

    Yes. You must define multiple handlers for your logging.

    http://docs.python.org/library/logging.html#logging-to-multiple-destinations

    http://docs.python.org/library/logging.handlers.html#module-logging.handlers

    0 讨论(0)
  • 2020-12-10 01:05

    I had the same problem and wrote a custom logging handler called SplitStreamHandler:

    import sys
    import logging
    
    class SplitStreamHandler(logging.Handler):
        def __init__(self):
            logging.Handler.__init__(self)
    
        def emit(self, record):
            # mostly copy-paste from logging.StreamHandler
            try:
                msg = self.format(record)
                if record.levelno < logging.WARNING:
                    stream = sys.stdout
                else:
                    stream = sys.stderr
                fs = "%s\n"
    
                try:
                    if (isinstance(msg, unicode) and
                        getattr(stream, 'encoding', None)):
                        ufs = fs.decode(stream.encoding)
                        try:
                            stream.write(ufs % msg)
                        except UnicodeEncodeError:
                            stream.write((ufs % msg).encode(stream.encoding))
                    else:
                        stream.write(fs % msg)
                except UnicodeError:
                    stream.write(fs % msg.encode("UTF-8"))
    
                stream.flush()
            except (KeyboardInterrupt, SystemExit):
                raise
            except:
                self.handleError(record)
    
    0 讨论(0)
  • 2020-12-10 01:09
    import logging
    import sys
    
    class LessThanFilter(logging.Filter):
        def __init__(self, exclusive_maximum, name=""):
            super(LessThanFilter, self).__init__(name)
            self.max_level = exclusive_maximum
    
        def filter(self, record):
            #non-zero return means we log this message
            return 1 if record.levelno < self.max_level else 0
    
    #Get the root logger
    logger = logging.getLogger()
    #Have to set the root logger level, it defaults to logging.WARNING
    logger.setLevel(logging.NOTSET)
    
    logging_handler_out = logging.StreamHandler(sys.stdout)
    logging_handler_out.setLevel(logging.DEBUG)
    logging_handler_out.addFilter(LessThanFilter(logging.WARNING))
    logger.addHandler(logging_handler_out)
    
    logging_handler_err = logging.StreamHandler(sys.stderr)
    logging_handler_err.setLevel(logging.WARNING)
    logger.addHandler(logging_handler_err)
    
    #demonstrate the logging levels
    logger.debug('DEBUG')
    logger.info('INFO')
    logger.warning('WARNING')
    logger.error('ERROR')
    logger.critical('CRITICAL')
    

    Implementation aside, I do think it is a good idea to use the logging facilities in python to output to the terminal, in particular because you can add another handler to additionally log to a file. If you set stdout to be INFO instead of DEBUG, you can even include additional DEBUG information that the user wouldn't standardly see in the log file.

    0 讨论(0)
  • 2020-12-10 01:20

    right from the updated docs, it cover this case pretty well now.

    http://docs.python.org/howto/logging.html#logging-advanced-tutorial

    import sys # Add this.
    import logging
    
    # create logger
    logger = logging.getLogger('simple_example')
    logger.setLevel(logging.DEBUG)
    
    # create console handler and set level to debug
    ch = logging.StreamHandler( sys.__stdout__ ) # Add this
    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
    logger.addHandler(ch)
    
    # 'application' code
    logger.debug('debug message')
    logger.info('info message')
    logger.warn('warn message')
    logger.error('error message')
    logger.critical('critical message')
    

    i've mentioned on comments the two changes required from the example to make the output go to stdout. you may also use filters to redirect depending on the level.

    more information to understand the changes is at http://docs.python.org/library/logging.handlers.html#module-logging.handlers

    0 讨论(0)
提交回复
热议问题