4
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 Logging Test ) のイベント ID ( 1 ) の説明が見つかりません。ローカル コンピュータに、リモート コンピュータからのメッセージを表示するために必要なレジストリ情報またはメッセージ DLL ファイルがない可能性があります。/AUXSOURCE= フラグを使用して、この説明を取得できる場合があります。詳細については、ヘルプとサポートを参照してください。次の情報はイベントの一部です: これは「エラー」メッセージです。

これは、スクリプトを実行可能ファイルに変換するために使用するコマンドです。pyinstaller.py --onefile --noconsole my_script.pyただし、コマンド ライン パラメータはこの動作に影響を与えないようで、pyinstaller.py my_script.py.

何が起こっているのか、そしてこれをどのように修正するのかを理解するのに助けていただければ幸いです。

最終的解決

リソース ハッカー ルートをたどりたくありませんでした。これは、自動化するのが難しいステップになるからです。代わりに、私がとったアプローチはwin32service.pyd、c:\Python27\Lib\site-packages\win32 からファイルを取得し、実行可能ファイルの隣に配置することでした。次に、スクリプトが変更され、ファイルのコピーへのフル パスが渡されました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()
4

1 に答える 1

4

通常、Windows イベント ログにはエラー メッセージがプレーン テキストで保存されるのではなく、メッセージ ID 参照と挿入文字列が保存されます。

のようなメッセージを格納する代わりにService foo crashed unexpectedly、DLL に格納されているリソース文字列を指すメッセージ ID を格納します。この場合、リソースは次のようService %s crashed unexpectedlyになりfoo、挿入文字列として保存されます。メッセージを書き込むプログラムは、リソース DLL を登録します。

その理由はローカリゼーションにあります。DLL はさまざまなリソース (ダイアログ レイアウト、文字列、アイコンなど) を格納でき、1 つの DLL に同じリソースをさまざまな言語で含めることができます。オペレーティング システムは、システム ロケールに応じて適切なリソースを自動的に選択します。リソース DLL は、事実上すべての Microsoft ユーティリティとコア ユーティリティで使用されます。

補足: 現在、ローカリゼーションに推奨される (そしてクロスプラットフォームの) 方法はgettext.

これはメッセージ ログにも使用されます。理想的には、すべてのメッセージが英語のドイツ語版 Windows インストールのログを英語版で開くことができます。

pywin32 の実装は、"%s". win32service.pydpywin32に格納され、登録されます。これは、このファイルがファイル システムに存在する限り正常に機能しますが、PyInstaller 実行可能ファイル内に隠されるとすぐに機能しなくなります。メッセージ ID を実行可能ファイルに直接埋め込む必要があると思います。

編集:疑いが確認されました。メッセージテーブルは実際に内部に保存されていますwin32service.pyd

メッセージ テーブルを示すリソース ハッカー http://media.leoluk.de/evlog_rh.png

メッセージ テーブル リソースをwin32service.pydPyInstaller 実行可能ファイルにコピーしてみてください (たとえば、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)
于 2012-10-08T17:05:54.837 に答える