简体   繁体   English

Python 使用 basicConfig 方法登录到控制台和文件

[英]Python using basicConfig method to log to console and file

I don't know why this code prints to the screen, but not to the file?我不知道为什么这段代码会打印到屏幕上,而不是打印到文件中? File "example1.log" is created, but nothing is written there.文件“example1.log”已创建,但没有写入任何内容。

#!/usr/bin/env python3
import logging

logging.basicConfig(level=logging.DEBUG,
                    format='%(asctime)s %(message)s',
                    handlers=[logging.FileHandler("example1.log"),
                              logging.StreamHandler()])
logging.debug('This message should go to the log file and to the console')
logging.info('So should this')
logging.warning('And this, too')

I have "bypassed" this problem by creating a logging object, but it keeps bugging me why basicConfig() approach failed?我通过创建日志对象“绕过”了这个问题,但它一直困扰着我为什么basicConfig()方法失败?

PS.附注。 If I change basicConfig call to:如果我将 basicConfig 调用更改为:

logging.basicConfig(level=logging.DEBUG,
                    filename="example2.log",
                    format='%(asctime)s %(message)s',
                    handlers=[logging.StreamHandler()])

then all logs are in the file and nothing is displayed in the console.然后所有日志都在文件中,控制台中不显示任何内容。

Try this working fine(tested in python 2.7) for both console and file试试这个工作正常(在 python 2.7 中测试)对于控制台和文件

# set up logging to file
logging.basicConfig(
     filename='log_file_name.log',
     level=logging.INFO, 
     format= '[%(asctime)s] {%(pathname)s:%(lineno)d} %(levelname)s - %(message)s',
     datefmt='%H:%M:%S'
 )

# set up logging to console
console = logging.StreamHandler()
console.setLevel(logging.DEBUG)
# set a format which is simpler for console use
formatter = logging.Formatter('%(name)-12s: %(levelname)-8s %(message)s')
console.setFormatter(formatter)
# add the handler to the root logger
logging.getLogger('').addHandler(console)

logger = logging.getLogger(__name__)

I can't reproduce it on Python 3.3.我无法在 Python 3.3 上重现它。 The messages are written both to the screen and the 'example2.log' .消息同时写入屏幕和'example2.log' On Python <3.3 it creates the file but it is empty.在 Python <3.3 上,它创建文件但它是空的。

The code:代码:

from logging_tree import printout  # pip install logging_tree
printout()

shows that FileHandler() is not attached to the root logger on Python <3.3.显示FileHandler()未附加到 Python <3.3 上的根记录器。

The docs for logging.basicConfig() say that handlers argument is added in Python 3.3. logging.basicConfig()的文档说在 Python 3.3 中添加了handlers参数。 The handlers argument isn't mentioned in Python 3.2 documentation. Python 3.2 文档中没有提到handlers参数。

In the example below, you can specify the log destination based on its level.在下面的示例中,您可以根据级别指定日志目标。 For example, the code below lets all logs over the INFO level go to the log file, and all above ERROR level goes to the console.例如,下面的代码让所有超过INFO级别的日志进入日志文件,所有高于ERROR级别的日志都进入控制台。

import logging
logging.root.handlers = []
logging.basicConfig(format='%(asctime)s : %(levelname)s : %(message)s', level=logging.INFO , filename='ex.log')

# set up logging to console
console = logging.StreamHandler()
console.setLevel(logging.ERROR)
# set a format which is simpler for console use
formatter = logging.Formatter('%(asctime)s : %(levelname)s : %(message)s')
console.setFormatter(formatter)
logging.getLogger("").addHandler(console)

logging.debug('debug')
logging.info('info')
logging.warning('warning')
logging.error('error')
logging.exception('exp')

Another technique using the basicConfig is to setup all your handlers in the statement and retrieve them after the fact, as in...另一种使用basicConfig技术是在语句中设置所有处理程序并在事后检索它们,如...

import logging

logging.basicConfig(level=logging.DEBUG,
                    format='%(asctime)s %(levelname)s %(module)s %(funcName)s %(message)s',
                    handlers=[logging.FileHandler("my_log.log", mode='w'),
                              logging.StreamHandler()])
stream_handler = [h for h in logging.root.handlers if isinstance(h , logging.StreamHandler)][0]
stream_handler.setLevel(logging.INFO)

More sensibly though is to construct your stream handler instance outside and configure them as standalone objects that you pass to the handlers list as in...更明智的是在外部构造您的流处理程序实例并将它们配置为您传递给处理程序列表的独立对象,如...

import logging

stream_handler = logging.StreamHandler()
stream_handler.setLevel(logging.INFO)

logging.basicConfig(level=logging.DEBUG,
                    format='%(asctime)s %(levelname)s %(module)s %(funcName)s %(message)s',
                    handlers=[logging.FileHandler("my_log.log", mode='w'),
                              stream_handler])

WOOAH!哇!

I just spent about 20 minutes being baffled by this.我只是花了大约 20 分钟对此感到困惑。

Eventually I worked out that the StreamHandler was outputting to stderr , not stdout (in a 'Doze DOS screen these have the same font colour!).最终我发现StreamHandler正在输出到stderr ,而不是stdout (在“Doze DOS 屏幕中,它们具有相同的字体颜色!)。

This resulted in me being able to run the code perfectly OK and get sensible results, but in a pytest function things going awry.这导致我能够完美地运行代码并获得合理的结果,但是在 pytest 函数中事情出错了。 Until I changed from this:直到我改变了这个:

out, _ = capsys.readouterr()
assert 'test message check on console' in out, f'out was |{out}|'

to this:对此:

_, err = capsys.readouterr()
assert 'test message check on console' in err, f'err was |{err}|'

NB the constructor of StreamHandler is注意 StreamHandler 的构造函数是

class logging.StreamHandler(stream=None)

and, as the docs say, "If stream is specified, the instance will use it for logging output; otherwise, sys.stderr will be used."并且,正如文档所说,“如果指定了流,则实例将使用它来记录输出;否则,将使用 sys.stderr。”

NB it seems that supplying the level keyword does not run setLevel on the handlers: you'd need to iterate on the resulting handlers and run setLevel on each, if it matters to you.注意,似乎提供level关键字不会在处理程序上运行setLevel :如果对您很重要,您需要迭代结果处理程序并在每个处理程序上运行setLevel

Reusable logger function.可重复使用的记录器功能。

def logger(logPath,fileName):
    logging.basicConfig(
        format='%(asctime)s - %(levelname)s - %(message)s',
        level=logging.INFO,
        handlers=[
            logging.FileHandler("{0}/{1}.log".format(logPath,fileName)),
            logging.StreamHandler()
        ])
    return logging

On Other python file, import the logger在其他 python 文件上,导入logger

logger().info("this is info")
logger().critical('404')
logger().error("this is error")

在此处输入图片说明

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM