除日志文件外,使Python记录器还将所有消息输出到stdout

417 python logging error-logging

有没有办法使用logging模块自动将事物输出到stdout 以及它们应该去的日志文件?例如,我想所有呼叫logger.warning,logger.critical,logger.error去他们预期的地方,但除了总是被复制到stdout.这是为了避免重复消息,如:

mylogger.critical("something failed")
print "something failed"
Run Code Online (Sandbox Code Playgroud)

Mar*_*ers 576

所有日志记录输出都由处理程序处理; 只需logging.StreamHandler()在根记录器中添加一个.

以下是配置流处理程序(使用stdout而不是默认值stderr)并将其添加到根记录器的示例:

import logging
import sys

root = logging.getLogger()
root.setLevel(logging.DEBUG)

handler = logging.StreamHandler(sys.stdout)
handler.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)
root.addHandler(handler)
Run Code Online (Sandbox Code Playgroud)

  • @ user248237:通过添加*new*处理程序,如图所示.新的处理程序不会替换现有的处理程序,它们*也可以*处理日志条目. (48认同)
  • @PrakharMohanSrivastava我猜你可以把它添加到传递给`logging.Formatter`的字符串中. (5认同)
  • 这很好,但是如果已经重定向到文件,我怎么能将它打印到``stdout``? (4认同)
  • @himanshu219:记录器有一个级别,处理程序也有一个级别。记录器将处理该级别及更高级别的消息,处理程序将处理该级别及更高级别的消息。它可以让您区分不同的记录器和不同的处理程序。 (3认同)
  • @ himanshu219:用例是,一旦您开始添加多个处理程序,通常就希望与众不同。调试到控制台,警告和文件,等等。 (3认同)

小智 451

最简单的方法:

import logging
import sys
logging.basicConfig(stream=sys.stdout, level=logging.DEBUG)
Run Code Online (Sandbox Code Playgroud)

  • 嗯,但是这没有记录到文件中,对吗?问题是如何记录文件_and_到控制台. (52认同)
  • 至少在 Python 3 中,对于我来说,省略 `stream=sys.stdout` 仍然适用于登录到控制台。 (3认同)
  • @TaylorEdmiston是的,但这是stderr流AFAIK。尝试从外壳重定向输出。 (2认同)
  • 好的。这并不能同时回答:记录到文件和控制台,但很高兴在 3 行或更少的时间内找到我需要的内容。 (2认同)

Alo*_*hor 59

可以使用多个处理程序.

import logging
import auxiliary_module

# create logger with 'spam_application'
log = logging.getLogger('spam_application')
log.setLevel(logging.DEBUG)

# create formatter and add it to the handlers
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')

# create file handler which logs even debug messages
fh = logging.FileHandler('spam.log')
fh.setLevel(logging.DEBUG)
fh.setFormatter(formatter)
log.addHandler(fh)

# create console handler with a higher log level
ch = logging.StreamHandler()
ch.setLevel(logging.ERROR)
ch.setFormatter(formatter)
log.addHandler(ch)

log.info('creating an instance of auxiliary_module.Auxiliary')
a = auxiliary_module.Auxiliary()
log.info('created an instance of auxiliary_module.Auxiliary')

log.info('calling auxiliary_module.Auxiliary.do_something')
a.do_something()
log.info('finished auxiliary_module.Auxiliary.do_something')

log.info('calling auxiliary_module.some_function()')
auxiliary_module.some_function()
log.info('done with auxiliary_module.some_function()')

# remember to close the handlers
for handler in log.handlers:
    handler.close()
    log.removeFilter(handler)
Run Code Online (Sandbox Code Playgroud)

请参阅:https://docs.python.org/2/howto/logging-cookbook.html

  • 精彩的答案,虽然有点凌乱.喜欢你如何展示如何为流和文件使用不同的级别和格式.+1,但精神上+2. (4认同)

Ant*_*pov 53

您可以为file和stdout创建两个处理程序,然后创建一个带handlers参数的记录器basicConfig.如果两个处理程序具有相同的log_level和format输出,那么它可能很有用:

import logging
import sys

file_handler = logging.FileHandler(filename='tmp.log')
stdout_handler = logging.StreamHandler(sys.stdout)
handlers = [file_handler, stdout_handler]

logging.basicConfig(
    level=logging.DEBUG, 
    format='[%(asctime)s] {%(filename)s:%(lineno)d} %(levelname)s - %(message)s',
    handlers=handlers
)

logger = logging.getLogger('LOGGER_NAME')
Run Code Online (Sandbox Code Playgroud)


Wei*_*nde 31

记录到文件和stderr的最简单方法:

import logging

logging.basicConfig(filename="logfile.txt")
stderrLogger=logging.StreamHandler()
stderrLogger.setFormatter(logging.Formatter(logging.BASIC_FORMAT))
logging.getLogger().addHandler(stderrLogger)
Run Code Online (Sandbox Code Playgroud)


Mil*_*vić 12

有关更详细的解释 - 在该链接上有很好的文档。例如:很简单,你只需要设置两个记录器。

import sys
import logging

logger = logging.getLogger('')
logger.setLevel(logging.DEBUG)
fh = logging.FileHandler('my_log_info.log')
sh = logging.StreamHandler(sys.stdout)
formatter = logging.Formatter('[%(asctime)s] %(levelname)s [%(filename)s.%(funcName)s:%(lineno)d] %(message)s', datefmt='%a, %d %b %Y %H:%M:%S')
fh.setFormatter(formatter)
sh.setFormatter(formatter)
logger.addHandler(fh)
logger.addHandler(sh)

def hello_logger():
    logger.info("Hello info")
    logger.critical("Hello critical")
    logger.warning("Hello warning")
    logger.debug("Hello debug")

if __name__ == "__main__":
    print(hello_logger())
Run Code Online (Sandbox Code Playgroud)

输出 - 终端:

[Mon, 10 Aug 2020 12:44:25] INFO [TestLoger.py.hello_logger:15] Hello info
[Mon, 10 Aug 2020 12:44:25] CRITICAL [TestLoger.py.hello_logger:16] Hello critical
[Mon, 10 Aug 2020 12:44:25] WARNING [TestLoger.py.hello_logger:17] Hello warning
[Mon, 10 Aug 2020 12:44:25] DEBUG [TestLoger.py.hello_logger:18] Hello debug
None
Run Code Online (Sandbox Code Playgroud)

输出 - 在文件中:

登录文件


更新:彩色终端

包裹:

pip install colorlog
Run Code Online (Sandbox Code Playgroud)

代码:

import sys
import logging
import colorlog

logger = logging.getLogger('')
logger.setLevel(logging.DEBUG)
fh = logging.FileHandler('my_log_info.log')
sh = logging.StreamHandler(sys.stdout)
formatter = logging.Formatter('[%(asctime)s] %(levelname)s [%(filename)s.%(funcName)s:%(lineno)d] %(message)s', datefmt='%a, %d %b %Y %H:%M:%S')
fh.setFormatter(formatter)
sh.setFormatter(colorlog.ColoredFormatter('%(log_color)s [%(asctime)s] %(levelname)s [%(filename)s.%(funcName)s:%(lineno)d] %(message)s', datefmt='%a, %d %b %Y %H:%M:%S'))
logger.addHandler(fh)
logger.addHandler(sh)

def hello_logger():
    logger.info("Hello info")
    logger.critical("Hello critical")
    logger.warning("Hello warning")
    logger.debug("Hello debug")
    logger.error("Error message")

if __name__ == "__main__":
    hello_logger()
Run Code Online (Sandbox Code Playgroud)

输出: 在此处输入图片说明

推荐:

INI文件完成记录器配置,其中还包括设置stdoutdebug.log

  • handler_file
    • level=WARNING
  • handler_screen
    • level=DEBUG


Eli*_*hle 7

这是基于强大的,但记录不完整的解决方案logging.config.dictConfig的方法。而不是将所有日志消息发送到stdout,而是将日志级别ERROR和更高级别的消息发送stderrstdout。如果系统的其他部分正在收听stderr或,则此功能很有用stdout

import logging
import logging.config
import sys

class _ExcludeErrorsFilter(logging.Filter):
    def filter(self, record):
        """Filters out log messages with log level ERROR (numeric value: 40) or higher."""
        return record.levelno < 40


config = {
    'version': 1,
    'filters': {
        'exclude_errors': {
            '()': _ExcludeErrorsFilter
        }
    },
    'formatters': {
        # Modify log message format here or replace with your custom formatter class
        'my_formatter': {
            'format': '(%(process)d) %(asctime)s %(name)s (line %(lineno)s) | %(levelname)s %(message)s'
        }
    },
    'handlers': {
        'console_stderr': {
            # Sends log messages with log level ERROR or higher to stderr
            'class': 'logging.StreamHandler',
            'level': 'ERROR',
            'formatter': 'my_formatter',
            'stream': sys.stderr
        },
        'console_stdout': {
            # Sends log messages with log level lower than ERROR to stdout
            'class': 'logging.StreamHandler',
            'level': 'DEBUG',
            'formatter': 'my_formatter',
            'filters': ['exclude_errors'],
            'stream': sys.stdout
        },
        'file': {
            # Sends all log messages to a file
            'class': 'logging.FileHandler',
            'level': 'DEBUG',
            'formatter': 'my_formatter',
            'filename': 'my.log',
            'encoding': 'utf8'
        }
    },
    'root': {
        # In general, this should be kept at 'NOTSET' to ensure it does
        # not interfere with the log levels set for each handler
        'level': 'NOTSET',
        'handlers': ['console_stderr', 'console_stdout', 'file']
    },
}

logging.config.dictConfig(config)
Run Code Online (Sandbox Code Playgroud)

  • 哇哦,以前从来没有意识到`dictConfig`的存在!!非常感谢! (3认同)

小智 5

由于没有人共享两个整齐的班轮,我将分享我自己的:

logging.basicConfig(filename='logs.log', level=logging.DEBUG, format="%(asctime)s:%(levelname)s: %(message)s")
logging.getLogger().addHandler(logging.StreamHandler())
Run Code Online (Sandbox Code Playgroud)