【问题标题】:Python logging working on Windows but not Mac OSPython 日志记录适用于 Windows 但不适用于 Mac OS
【发布时间】:2016-07-02 22:37:08
【问题描述】:
# 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())

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

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

关于如何解决这个问题的任何想法?

【问题讨论】:

  • 你能显示其他变量,尤其是constants吗?看起来 constants.log_dir 包含反斜杠 `\`,因此在 Mac 上会被误解。
  • 感谢@EugeneLisitsky,constants.log_dir 看起来像这样:'/Users/ri/Documents/Projects//GL/Output//GL//Logs'
  • 好的。我试过你的代码,它为我记录得很好,除了没有日期严重性和其他信息的屏幕日志。您能否检查一下 sys.stdout 或 sys.stderr 是否被重定向到 /dev/null 或其他地方?

标签: python windows macos logging


【解决方案1】:

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

第一件事:对我来说,您的解决方案似乎是正确的,因为 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()

运行它:$ python with_logging.py,您将看到预期的日志记录(格式正确) 在日志文件app.logstderr 上。

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

logbook 的解决方案

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

之前的解决方案重写为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()

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

这些记录将由处理程序处理,如果它们到位的话。一种方法 是创建一个处理程序并在其上调用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")

您可以注意到,实际上只有两行代码与制作相关 记录可用。

然后,创建调用代码,放入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()

运行代码你会看到它的工作方式和以前一样,只有记录器名称是funmodule

请注意,我在设置日志记录后执行了from funmodule import fun。如果我在 如果是short_logbook.py 文件,则不会看到来自funmodule.py 的前两条日志记录 就像它们在模块导入期间发生的那样。

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

另一个 stdlib logging 解决方案

试图公平比较 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")

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()

功能非常相似。

我仍然在努力记录日志。 stdlib logging 不容易掌握,但它在 stdlib 中并提供 一些不错的东西,例如logging.config.dictConfig 允许通过字典配置日志记录。 logbook 开始时要简单得多,但现在有点慢并且缺少 字典配置。无论如何,这些差异与您的问题无关。

【讨论】:

  • “我不知道为什么,但这里有 3 个版本的我写的一些新代码似乎可以工作”是一个非常糟糕的答案 IMO
  • @xavier 你可能误解了我的最后一点——这是关于我对logging 的感受,而不是承认我的代码有效,但我不知道为什么。上面的所有示例都是工作示例(安装logbook 包,然后您可以尝试所有这些示例),如果您需要,我可以向您解释任何行。但请在发送下一个反应之前先阅读并测试它。
  • 我不是这个意思。问题是关于“为什么我的代码不能在 OSX 上运行”。您的回答是“我不知道为什么,也许还有其他代码导致了问题。”这应该作为评论留下,而不是就日志与日志的切线。
  • @xavier 我明白了。你是对的,我没有发现它在 OSX 上不起作用的确切原因。很难强迫作者提供完整的运行示例。我的猜测是,问题的真正根源是难以以简单的方式使用logging 库——这通常会导致代码混乱,没人理解,发生了什么。这就是为什么我使用一个答案试图提供更干净的代码,这将是可行的。这是我能做的最好的事情(因为我在 Linux 上并且无法访问 OSX)。
猜你喜欢
  • 2017-05-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-02-08
  • 1970-01-01
  • 2017-08-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多