从您的问题可以清楚地看出,创建记录器名称没有问题,
记录文件名并记录到文件,所以我将简化这部分以保持我的代码简洁。
第一件事:对我来说,您的解决方案似乎是正确的,因为 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.log 和stderr 上。
注意,如果您在 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 解决方案
试图公平比较 logbook 和 logging 我将最终的 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 开始时要简单得多,但现在有点慢并且缺少
字典配置。无论如何,这些差异与您的问题无关。