web-dev-qa-db-ja.com

UTF-8 In Python logging、how?

Pythonのログパッケージを使用して、UTF-8でエンコードされた文字列をファイルに記録しようとしています。おもちゃの例として:

import logging

def logging_test():
    handler = logging.FileHandler("/home/ted/logfile.txt", "w",
                                  encoding = "UTF-8")
    formatter = logging.Formatter("%(message)s")
    handler.setFormatter(formatter)
    root_logger = logging.getLogger()
    root_logger.addHandler(handler)
    root_logger.setLevel(logging.INFO)

    # This is an o with a hat on it.
    byte_string = '\xc3\xb4'
    unicode_string = unicode("\xc3\xb4", "utf-8")

    print "printed unicode object: %s" % unicode_string

    # Explode
    root_logger.info(unicode_string)

if __== "__main__":
    logging_test()

これは、logging.info()呼び出しでUnicodeDecodeErrorで爆発します。

下位レベルでは、Pythonのロギングパッケージはコーデックパッケージを使用してログファイルを開き、エンコーディングとして「UTF-8」引数を渡します。すべてうまくいきましたが、Unicodeオブジェクトの代わりにファイルにバイト文字列を書き込もうとしているため、爆発します。基本的に、Pythonはこれを行っています:

file_handler.write(unicode_string.encode("UTF-8"))

これを実行する必要がある場合:

file_handler.write(unicode_string)

これはPythonのバグですか、それともクレイジーピルを服用していますか? FWIW、これは在庫Python 2.6インストールです。

41
Ted Dziuba

最新のPython 2.6-2.6がリリースされてからUnicodeのバグがいくつか見つかって修正されていることを確認してください。たとえば、Ubuntu Jauntyシステムでは、スクリプトをコピーして貼り付け、ログファイル名の「/ home/ted /」プレフィックス結果(ターミナルウィンドウからコピーして貼り付けた):

 vinay @ eta-jaunty:〜/ projects/scratch $ python --version 
 Python 2.6.2 
 vinay @ eta-jaunty: 〜/ projects/scratch $ python utest.py 
 printed unicode object:ô
 vinay @ eta-jaunty:〜/ projects/scratch $ cat logfile.txt 
ô
 vinay @ eta-jaunty:〜/ projects/scratch $ 

Windowsボックスの場合:

 C:\ temp> python --version 
 Python 2.6.2 
 
 C:\ temp> python utest.py 
 printed unicode object :ô

そして、ファイルの内容:

alt text

これも、レナート・レゲブロがそれを再現できなかった理由を説明するかもしれません。

14
Vinay Sajip

次のようなコードを持つ:

raise Exception(u'щ')

原因:

  File "/usr/lib/python2.7/logging/__init__.py", line 467, in format
    s = self._fmt % record.__dict__
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-3: ordinal not in range(128)

これは、フォーマット文字列がバイト文字列であり、フォーマット文字列引数の一部が非ASCII文字を含むユニコード文字列であるために発生します。

>>> "%(message)s" % {'message': Exception(u'\u0449')}
*** UnicodeEncodeError: 'ascii' codec can't encode character u'\u0449' in position 0: ordinal not in range(128)

フォーマット文字列をユニコードにすることで問題が修正されます。

>>> u"%(message)s" % {'message': Exception(u'\u0449')}
u'\u0449'

したがって、ロギング構成ですべてのフォーマット文字列をユニコードにします:

'formatters': {
    'simple': {
        'format': u'%(asctime)-s %(levelname)s [%(name)s]: %(message)s',
        'datefmt': '%Y-%m-%d %H:%M:%S',
    },
 ...

そして、デフォルトのloggingフォーマッターにパッチを当てて、Unicodeフォーマット文字列を使用します。

logging._defaultFormatter = logging.Formatter(u"%(message)s")
27
warvariuc

Python3でDjangoを実行すると同様の問題が発生しました:ロガーはウムラウト(äöüß)に遭遇すると死亡しましたが、それ以外は問題ありませんでした。

import locale; 
if locale.getpreferredencoding().upper() != 'UTF-8': 
    locale.setlocale(locale.LC_ALL, 'en_US.UTF-8') 

上記のコメントから得ました。それは動かなかった。現在のロケールを見ると、おもしろいANSIのことがわかりました。これは基本的に「ASCII」だけを意味することがわかりました。それは全く間違った方向に私を送りました。

ロギングのフォーマット文字列をUnicodeに変更しても解決しません。スクリプトの先頭にマジックエンコーディングのコメントを設定しても役に立ちません。送信者のメッセージに文字セットを設定しても(テキストはHTTP要求から送られてきた)、助けにはなりませんでした。

何がDID作業はsettings.pyでファイルハンドラーのエンコーディングをUTF-8に設定していました。何も設定されていなかったため、デフォルトはNoneになります。どうやらASCII(または私が考えたいように:ASS-KEY)

    'handlers': {
        'file': {
            'level': 'DEBUG',
            'class': 'logging.handlers.TimedRotatingFileHandler',
            'encoding': 'UTF-8', # <-- That was missing.
            ....
        },
    },
3
Chris

これを試して:

import logging

def logging_test():
    log = open("./logfile.txt", "w")
    handler = logging.StreamHandler(log)
    formatter = logging.Formatter("%(message)s")
    handler.setFormatter(formatter)
    root_logger = logging.getLogger()
    root_logger.addHandler(handler)
    root_logger.setLevel(logging.INFO)

    # This is an o with a hat on it.
    byte_string = '\xc3\xb4'
    unicode_string = unicode("\xc3\xb4", "utf-8")

    print "printed unicode object: %s" % unicode_string

    # Explode
    root_logger.info(unicode_string.encode("utf8", "replace"))


if __== "__main__":
    logging_test()

価値があることについては、utcs-8エンコーディングでファイルを開くためにcodecs.openを使用する必要があると期待していましたが、これはデフォルトであるか、ここで行われています。

2
John

私があなたの問題を正しく理解していれば、あなたがちょうどするときあなたのシステムで同じ問題が起こるはずです:

str(u'ô')

Unixのロケールエンコーディングへの自動エンコーディングは、 ifsetencoding関数でロケール対応のsiteブランチを有効にするまで機能しないと思います。 = locale 経由のモジュール。このファイルは通常、/usr/lib/python2.x、とにかく検査する価値があります。私の知る限り、ロケールに対応したsetencodingはデフォルトで無効になっています(私のPython 2.6インストール)には当てはまります)。

選択肢は次のとおりです。

  • システムにUnicode文字列をバイトにエンコードする正しい方法を理解させるか、コードでそれを実行させます(サイト固有のsite.pyが必要です)
  • コード内のUnicode文字列をエンコードし、バイトだけを出力します

Illusive setdefaultencoding by Ian Bickingおよび関連リンクも参照してください。

1