import logging, logging.handlers
def main():
ntl = logging.handlers.NTEventLogHandler("Python Logging Test")
logger = logging.getLogger("")
logger.setLevel(logging.DEBUG)
logger.addHandler(ntl)
logger.error("This is a '%s' message", "Error")
if __name__ == "__main__":
main()
上面的Python(2.7.x)脚本将“这是'错误'消息”写入Windows事件查看器。当我将它作为脚本运行时,我得到了预期的输出。如果我通过PyInstaller将脚本转换为可执行文件,我会在事件日志中获得一个条目,但它会说完全不同的东西。
无法找到源(Python日志记录测试)中事件ID(1)的说明。本地计算机可能没有必要的注册表信息或消息DLL文件来显示来自远程计算机的消息。您可以使用/ AUXSOURCE =标志来检索此描述;请参阅帮助和支持以获取详细信以下信息是事件的一部分:这是一条“错误”消息。
这是我用来将脚本转换为可执行文件的命令:pyinstaller.py --onefile --noconsole my_script.py
虽然命令行参数似乎对此行为没有任何影响,只需调用pyinstaller.py my_script.py
即可。
我很感激任何帮助,以了解正在发生的事情以及如何解决这个问题。
最终解决方案
我不想走资源黑客路线,因为这将是一个自动化的艰难步骤。相反,我采用的方法是从c:\ Python27 \ Lib \ site-packages \ win32中获取win32service.pyd
文件,并将其放在我的可执行文件旁边。然后修改脚本将完整路径传递给win32service.pyd
文件的副本,这在脚本和exe形式中都有效。最终的脚本包含在下面:
import logging, logging.handlers
import os
import sys
def main():
base_dir = os.path.dirname(sys.argv[0])
dllname = os.path.join(base_dir, "win32service.pyd")
ntl = logging.handlers.NTEventLogHandler("Python Logging Test", dllname=dllname)
logger = logging.getLogger("")
logger.setLevel(logging.DEBUG)
logger.addHandler(ntl)
logger.error("This is a '%s' message", "Error")
if __name__ == "__main__":
main()
答案 0 :(得分:4)
通常,Windows事件日志不会以纯文本格式存储错误消息,而是存储消息ID引用和插入字符串。
它不存储类似Service foo crashed unexpectedly
的消息,而是存储指向存储在DLL中的资源字符串的消息ID。在这种情况下,资源类似Service %s crashed unexpectedly
,foo
将存储为插入字符串。写入消息的程序注册资源DLL。
原因是本地化。 DLL可以存储许多不同的资源(对话框布局,字符串,图标......),并且一个DLL可以包含许多不同语言的相同资源。操作系统会根据系统区域设置自动选择正确的资源。几乎所有Microsoft实用程序和核心实用程序都使用资源DLL。
附注:现在,本地化的首选(和跨平台)方式是gettext
。
这也用于消息日志 - 理想情况下,您可以使用英语中的所有消息从英语Windows安装中打开日志。
我怀疑pywin32实现只通过一个消息ID(1)就像"%s"
那样跳过了这种机制。它存储在win32service.pyd
中并由pywin32注册。只要此文件存在于文件系统上,这样就可以正常工作,但只要它隐藏在PyInstaller可执行文件中就会中断。我想你必须直接将消息ID嵌入到你的可执行文件中。
编辑:怀疑已确认,消息表确实存储在win32service.pyd
Resource Hacker showing the message table http://media.leoluk.de/evlog_rh.png
尝试将消息表资源从win32service.pyd
复制到PyInstaller可执行文件(例如使用Resource Hacker)。
查看日志记录处理程序实现,这可能有效:
def __init__(self, appname, dllname=None, logtype="Application"):
logging.Handler.__init__(self)
try:
import win32evtlogutil, win32evtlog
self.appname = appname
self._welu = win32evtlogutil
if not dllname:
dllname = os.path.split(self._welu.__file__)
dllname = os.path.split(dllname[0])
dllname = os.path.join(dllname[0], r'win32service.pyd')
您必须将dllname
设置为os.path.dirname(__file__)
。如果您希望它继续为未冻结的脚本工作,请使用类似的东西:
if getattr(sys, 'frozen', False):
dllname = None
elif __file__:
dllname = os.path.dirname(__file__)
ntl = logging.handlers.NTEventLogHandler("Python Logging Test", dllname=dllname)