Python日志在Windows上运行,但不适用于Mac OS

use*_*827 8 python windows macos logging

# Logging
cur_flname = os.path.splitext(os.path.basename(__file__))[0]
LOG_FILENAME = constants.log_dir + os.sep + 'Log_' + cur_flname + '.txt'
util.make_dir_if_missing(constants.log_dir)
logging.basicConfig(filename=LOG_FILENAME, level=logging.INFO, filemode='w',
                    format='%(asctime)s %(levelname)s %(module)s - %(funcName)s: %(message)s',
                    datefmt="%m-%d %H:%M")  # Logging levels are DEBUG, INFO, WARNING, ERROR, and CRITICAL
# Output to screen
logger = logging.getLogger(cur_flname)
logger.addHandler(logging.StreamHandler())
Run Code Online (Sandbox Code Playgroud)

我使用上面的代码创建了一个logger我可以在我的模块中使用的方法来将消息打印到屏幕以及同时打印到文本文件.

在Windows上,消息将输出到文件和屏幕.但是,在Mac OS X 10.9.5上,它们只能输出到文件.我使用的是Python 2.7.

有想法该怎么解决这个吗?

Jan*_*sky 3

从你的问题可以清楚地看出,创建记录器名称、日志文件名和记录到文件没有问题,所以我将简化这部分以保持我的代码简洁。

第一件事:对我来说,您的解决方案似乎是正确的,默认情况下logging.StreamHandler应将输出发送到sys.stderr。您可能有一些正在修改的代码(在您的问题中未显示),sys.stderr或者您在 OSX 上以这种方式运行代码,未显示到 stderr 的输出(但确实是输出)。

解决方案与logging

将以下代码放入with_logging.py

import logging
import sys

logformat = "%(asctime)s %(levelname)s %(module)s - %(funcName)s: %(message)s"
datefmt = "%m-%d %H:%M"

logging.basicConfig(filename="app.log", level=logging.INFO, filemode="w",
                    format=logformat, datefmt=datefmt)

stream_handler = logging.StreamHandler(sys.stderr)
stream_handler.setFormatter(logging.Formatter(fmt=logformat, datefmt=datefmt))

logger = logging.getLogger("app")
logger.addHandler(stream_handler)

logger.info("information")
logger.warning("warning")


def fun():
    logger.info(" fun inf")
    logger.warning("fun warn")

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

运行它:$ python with_logging.py您将在日志文件app.log等中看到预期的日志记录(格式正确) stderr

请注意,如果您在 上没有看到它stderr,则说明有东西隐藏了您的 stderr 输出。要查看某些内容,请将StreamHandler流更改为sys.stdout

解决方案与logbook

logbook是提供替代日志记录方式的 python 包。我在这里展示它是为了展示与 stdlib 的主要区别logginglogbook使用和配置对我来说似乎更简单。

先前的解决方案重写为with_logbook.py

import logbook
import sys

logformat = ("{record.time:%m-%d %H:%M} {record.level_name} {record.module} - "
             "{record.func_name}: {record.message}")
kwargs = {"level": logbook.INFO, "format_string": logformat, "bubble": True}

logbook.StreamHandler(sys.stderr, **kwargs).push_application()
logbook.FileHandler("app.log", **kwargs).push_application()

logger = logbook.Logger("app")

logger.info("information")
logger.warning("warning")


def fun():
    logger.info(" fun inf")
    logger.warning("fun warn")

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

主要区别在于,您不必将处理程序附加到记录器,您的记录器只需发出一些日志记录。

如果这些记录到位,则将由处理人员处理。一种方法是创建一个处理程序并调用push_application()它。这会将处理程序放入正在使用的处理程序堆栈中。

和以前一样,我们需要两个处理程序,一个用于文件,另一个用于 stderr。

如果运行此脚本$ python with_logbook.py,您将看到与日志记录完全相同的结果。

将日志记录设置与模块代码分开:short_logbook.py

对于 stdlib,logging我真的不喜欢让记录器工作的介绍性舞蹈。我只想发出一些日志记录,并且希望尽可能简单地做到这一点。

下面的例子是对前一个例子的修改。我没有在模块的最开始设置日志记录,而是在代码运行之前进行设置 - 在模块内部if __name__ == "__main__"(您可以在任何其他地方执行相同的操作)。

出于实际原因,我将模块和调用代码分离到两个文件中:

文件funmodule.py

from logbook import Logger

log = Logger(__name__)

log.info("information")
log.warning("warning")


def fun():
    log.info(" fun inf")
    log.warning("fun warn")
Run Code Online (Sandbox Code Playgroud)

您可以注意到,实际上只有两行与使日志记录可用相关的代码。

然后,创建调用代码,放入short_logbook.py

import sys
import logbook

if __name__ == "__main__":
    logformat = ("{record.time:%m-%d %H:%M} {record.level_name} {record.module}"
                 "- {record.func_name}: {record.message}")
    kwargs = {"level": logbook.INFO, "format_string": logformat, "bubble": True}

    logbook.StreamHandler(sys.stderr, **kwargs).push_application()
    logbook.FileHandler("app.log", **kwargs).push_application()

    from funmodule import fun
    fun()
Run Code Online (Sandbox Code Playgroud)

运行代码,您将看到它的工作方式与以前相同,只有记录器名称是funmodule.

请注意,我是from funmodule import fun在设置日志记录后执行的。如果我在文件顶部执行此操作,则不会看到short_logbook.py前两条日志记录,因为它们是在模块导入期间发生的。funmodule.py

编辑:添加了另一个logging解决方案以进行公平比较

另一种 stdliblogging解决方案

试图进行公平的比较logbooklogging我将最后一个logbook示例重写为 logging.

funmodule_logging.py好像:

import logging

log = logging.getLogger(__name__)

log.info("information")
log.warning("warning")


def fun():
    log.info(" fun inf")
    log.warning("fun warn")
    log.error("no fun at all")
Run Code Online (Sandbox Code Playgroud)

看起来short_logging.py如下:

import sys
import logging

if __name__ == "__main__":
    logformat = "%(asctime)s %(levelname)s %(module)s - %(funcName)s: %(message)s"
    datefmt = "%m-%d %H:%M"

    logging.basicConfig(filename="app.log", level=logging.INFO, filemode="w",
                        format=logformat, datefmt=datefmt)

    stream_handler = logging.StreamHandler(sys.stderr)
    stream_handler.setFormatter(logging.Formatter(fmt=logformat, datefmt=datefmt))

    logger = logging.getLogger("funmodule_logging")
    logger.addHandler(stream_handler)

    from funmodule_logging import fun
    fun()
Run Code Online (Sandbox Code Playgroud)

功能非常相似。

我仍然在日志方面挣扎。stdliblogging不容易掌握,但它位于 stdlib 中并提供了一些不错的东西,例如logging.config.dictConfig允许通过字典配置日志记录。 logbook开始要简单得多,但目前速度有点慢并且缺少 dictConfig。无论如何,这些差异与你的问题无关。