web-dev-qa-db-fra.com

UTF-8 In Python logging, comment?

J'essaie de consigner une chaîne codée UTF-8 dans un fichier à l'aide du package de journalisation de Python. Comme exemple de jouet:

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 __name__ == "__main__":
    logging_test()

Cela explose avec UnicodeDecodeError sur l'appel logging.info ().

À un niveau inférieur, le package de journalisation de Python utilise le package de codecs pour ouvrir le fichier journal, en transmettant l'argument "UTF-8" comme encodage. C'est bien beau, mais il essaie d'écrire des chaînes d'octets dans le fichier au lieu d'objets unicode, ce qui explose. Essentiellement, Python fait ceci:

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

Quand devrait-il le faire:

file_handler.write(unicode_string)

Est-ce un bug en Python, ou est-ce que je prends des pilules folles? FWIW, il s'agit d'une installation standard Python 2.6.

45
Ted Dziuba

Vérifiez que vous disposez de la dernière Python 2.6 - certains bogues Unicode ont été trouvés et corrigés depuis la sortie de la version 2.6. Par exemple, sur mon système Ubuntu Jaunty, j'ai exécuté votre script copié et collé, en supprimant uniquement le '/ home/ted /' préfixe du nom du fichier journal. Résultat (copié et collé depuis une fenêtre de terminal):

 vinay @ eta-jaunty: ~/projets/scratch $ python --version 
 Python 2.6.2 
 vinay @ eta-jaunty: ~/projects/scratch $ python utest.py 
 objet unicode imprimé: ô 
 vinay @ eta-jaunty: ~/projects/scratch $ cat logfile.txt 
 ô 
 vinay @ eta-jaunty: ~/projets/scratch $ 

Sur une boîte Windows:

 C:\temp> python --version 
 Python 2.6.2 
 
 C:\temp> python utest.py 
 Objet unicode imprimé : ô 

Et le contenu du fichier:

alt text

Cela pourrait également expliquer pourquoi Lennart Regebro n'a pas pu le reproduire non plus.

16
Vinay Sajip

Avoir du code comme:

raise Exception(u'щ')

Causé:

  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)

Cela se produit car la chaîne de format est une chaîne d'octets, tandis que certains des arguments de chaîne de format sont des chaînes unicode avec des caractères non ASCII:

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

Rendre la chaîne de format unicode résout le problème:

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

Donc, dans votre configuration de journalisation, rendez toutes les chaînes de format unicode:

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

Et corrigez le formateur logging par défaut pour utiliser la chaîne de format unicode:

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

J'ai eu un problème similaire lors de l'exécution de Django en Python3: mon enregistreur est mort en rencontrant des trémas (äöüß) mais, sinon, je me suis bien débrouillé. J'ai examiné beaucoup de résultats et je n'ai trouvé aucun résultat. J'ai essayé

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

que j'ai obtenu du commentaire ci-dessus. Cela n'a pas fonctionné. Regarder les paramètres régionaux actuels m'a donné quelque chose d'ANSI fou, qui s'est avéré signifier simplement "ASCII". Cela m'a envoyé dans la mauvaise direction.

Changer les chaînes de format de journalisation en Unicode n'aiderait pas. La définition d'un commentaire d'encodage magique au début du script n'aiderait pas. La définition du jeu de caractères sur le message de l'expéditeur (le texte provenait d'une requête HTTP) n'a pas aidé.

Quel travail DID a été de définir l'encodage sur le gestionnaire de fichiers sur UTF-8 dans settings.py. Comme je n'avais rien défini, la valeur par défaut deviendrait None. Ce qui finit apparemment par être ASCII (ou comme j'aimerais y penser: ASS-KEY)

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

Essaye ça:

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 __name__ == "__main__":
    logging_test()

Pour ce que ça vaut, je m'attendais à devoir utiliser codecs.open pour ouvrir le fichier avec l'encodage utf-8, mais c'est la valeur par défaut ou quelque chose d'autre se passe ici, car cela fonctionne comme ceci.

2
John

Je suis un peu en retard, mais je suis juste tombé sur ce post qui m'a permis de configurer la connexion à utf-8 très facilement

Voici le lien vers le post

ou ici le code:

root_logger= logging.getLogger()
root_logger.setLevel(logging.DEBUG) # or whatever
handler = logging.FileHandler('test.log', 'w', 'utf-8') # or whatever
formatter = logging.Formatter('%(name)s %(message)s') # or whatever
handler.setFormatter(formatter) # Pass handler as a parameter, not assign
root_logger.addHandler(handler)
2
Ephie

Si j'ai bien compris votre problème, le même problème devrait survenir sur votre système lorsque vous faites simplement:

str(u'ô')

Je suppose que l'encodage automatique vers l'encodage local sur Unix ne fonctionnera pas tant que vous n'aurez pas activé la branche if sensible aux paramètres régionaux dans la fonction setencoding dans votre site = module via locale . Ce fichier réside généralement dans /usr/lib/python2.x, ça vaut quand même le coup d'inspecter. AFAIK, les paramètres régionaux setencoding sont désactivés par défaut (c'est vrai pour mon Python 2.6).

Les choix sont:

  • Laissez le système trouver la bonne façon de coder des chaînes Unicode en octets ou faites-le dans votre code (une configuration dans un site spécifique site.py est nécessaire)
  • Encodez des chaînes Unicode dans votre code et affichez uniquement des octets

Voir aussi The Illusive setdefaultencoding par Ian Bicking et liens connexes.

1