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.
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:
Cela pourrait également expliquer pourquoi Lennart Regebro n'a pas pu le reproduire non plus.
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")
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.
....
},
},
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.
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
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)
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:
site.py
est nécessaire)Voir aussi The Illusive setdefaultencoding par Ian Bicking et liens connexes.