Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/logging/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
来自Python可执行文件的NTEventLogHandler_Python_Logging_Pywin32_Pyinstaller - Fatal编程技术网

来自Python可执行文件的NTEventLogHandler

来自Python可执行文件的NTEventLogHandler,python,logging,pywin32,pyinstaller,Python,Logging,Pywin32,Pyinstaller,上面的Python(2.7.x)脚本将“这是一条‘错误’消息”写入Windows事件查看器。当我把它作为脚本运行时,我得到了预期的输出。如果我通过PyInstaller将脚本转换为可执行文件,我会在事件日志中得到一个条目,但它显示了完全不同的内容 找不到源(Python日志测试)中事件ID(1)的说明。本地计算机可能没有必要的注册表信息或消息DLL文件来显示来自远程计算机的消息。您可以使用/AUXSOURCE=标志来检索此描述;有关详细信息,请参阅帮助和支持。以下信息是事件的一部分:这是一条“错

上面的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

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

通常,Windows事件日志不以纯文本形式存储错误消息,而是存储消息ID引用和插入字符串

它存储的不是像
Service foo意外崩溃这样的消息,而是指向DLL中存储的资源字符串的消息ID。在这种情况下,资源类似于服务%s意外崩溃,
foo
将存储为插入字符串。写入消息的程序注册资源DLL

原因是本地化。DLL可以存储许多不同的资源(对话框布局、字符串、图标…),一个DLL可以包含许多不同语言的相同资源。操作系统会根据系统区域设置自动选择正确的资源。几乎所有Microsoft实用程序和核心实用程序都使用资源DLL

旁注:如今,本地化的首选(和跨平台)方式是
gettext

这也用于消息日志–理想情况下,您可以在英语日志上打开德语Windows安装的日志,其中所有消息均为英语

我怀疑pywin32实现跳过了这种机制,因为它只有一个消息ID(1),类似于
“%s”
。它存储在
win32service.pyd
中,并由pywin32注册。只要该文件存在于文件系统中,它就可以正常工作,但一旦它隐藏在PyInstaller可执行文件中,它就会中断。我想您必须将消息ID直接嵌入到可执行文件中

编辑:消息表确实存储在
win32service.pyd

尝试将消息表资源从
win32service.pyd
复制到PyInstaller可执行文件中(例如使用)

查看日志处理程序实现,这可能会起作用:

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()
您必须将
dllname
设置为
os.path.dirname(\uu文件\uu)
。如果希望它继续为未冻结的脚本工作,请使用类似以下内容:

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

这听起来有些道理。我该如何着手解决这个问题?是否手动注册win32service.pyd?如何在可执行文件中嵌入消息ID?有没有文章/教程可以演示这一点,因为大多数关于日志的文章都指向标准的Python类文档,这没有多大帮助。谢谢你的回答。它为我指明了正确的方向。
if getattr(sys, 'frozen', False):
    dllname = None
elif __file__:
    dllname = os.path.dirname(__file__)

ntl = logging.handlers.NTEventLogHandler("Python Logging Test", dllname=dllname)