2009-10-09 5 views
28

J'essaie d'enregistrer une chaîne codée en UTF-8 dans un fichier à l'aide du package de journalisation de Python. A titre d'exemple de jouet:UTF-8 En journalisation Python, comment?

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

Ce explose avec UnicodeDecodeError sur l'appel logging.info(). À un niveau inférieur, le package de journalisation Python utilise le package codecs pour ouvrir le fichier journal, en transmettant l'argument "UTF-8" comme encodage. C'est très bien, mais il essaie d'écrire des chaînes d'octets dans le fichier à la place des objets Unicode, ce qui explose. Essentiellement, Python fait ceci:

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

Quand il faut faire ceci:

file_handler.write(unicode_string) 

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

+0

Votre code fonctionne parfaitement bien ici . J'ai essayé de le faire échouer, mais je n'ai pas réussi. –

+0

Et vous avez raison, python l'encodage avec UTF-8, parce qu'il demande au outfile quel encodage utiliser, et vous avez spécifié UTF-8, donc c'est tout et bien. –

+1

J'ai dû frapper la machine de wayback pour trouver le [exemple] (http://web.archive.org/web/20100107060919/http://tony.czechit.net/2009/02/unicode-support-for-pythons -logging-library /) vous avez mentionné. Intéressant. – Epu

Répondre

13

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

 
[email protected]:~/projects/scratch$ python --version 
Python 2.6.2 
[email protected]:~/projects/scratch$ python utest.py 
printed unicode object: ô 
[email protected]:~/projects/scratch$ cat logfile.txt 
ô 
[email protected]:~/projects/scratch$ 

Sur une boîte de Windows:

 
C:\temp>python --version 
Python 2.6.2 

C:\temp>python utest.py 
printed unicode object: ô 

Et le contenu du fichier:

alt text

Cela pourrait aussi expliquer pourquoi Lennart Regebro ne pouvait pas non plus le reproduire.

+0

Oui c'était ça. Il y avait un bug dans le paquet de journalisation python qui a été corrigé dans une version ultérieure. –

+0

Je courais Python 2.6.1 (R261: 67515 11 fév 2010, 00:51:29) [GCC 4.2.1 (Apple Inc. construire 5646)] sur darwin sur mon iMac, et je encore obtenir la même erreur. Le bug a-t-il vraiment été corrigé? – Tsf

+1

Oui, c'était - c'est arrivé entre 2.6.1 et 2.6.2, à la révision 69448: http://svn.python.org/view?view=rev&revision=69448 - donc vous devez passer à une révision ultérieure. –

1

Essayez ceci:

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 comptais avoir à utiliser codecs.open pour ouvrir le fichier avec le codage utf-8, mais soit c'est la valeur par défaut ou quelque chose d'autre qui se passe ici, puisque ça marche comme ça.

+0

NameError: le nom global 'unicode' n'est pas défini – Gank

+0

@Gank vous utilisez python 3 Je suppose – warvariuc

1

Si je comprends bien votre problème correctement, le même problème devrait se poser sur votre système lorsque vous faites juste:

str(u'ô') 

je suppose que le codage automatique à l'encodage des paramètres régionaux sur Unix ne fonctionnera pas jusqu'à ce que vous avez activé des locales conscient if branchez dans la setencoding fonction dans votre module site via locale. Ce fichier se trouve généralement dans /usr/lib/python2.x, cela vaut la peine d'être inspecté. AFAIK, le setencoding tenant compte des paramètres régionaux est désactivé par défaut (c'est vrai pour mon installation de Python 2.6).

Les choix sont:

  • Laissez le système déterminer la bonne façon de coder les chaînes Unicode en octets ou le faire dans votre code (une configuration site.py site spécifique est nécessaire)
  • Encode Unicode chaînes dans votre code et la sortie juste des octets

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

21

code ayant comme:

raise Exception(u'щ') 

CAUSÉS:

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 parce que la chaîne de format est une chaîne d'octets, alors 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) 

La création de la chaîne de format unicode résout le problème:

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

Ainsi, dans la configuration de la journalisation faire toute chaîne de format unicode:

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

et le patch par défaut logging formatter utiliser la chaîne de format unicode:

logging._defaultFormatter = logging.Formatter(u"%(message)s") 
+1

Qu'en est-il de Python 3.5? Toutes les chaînes ne devraient-elles pas être unicode par défaut? –

+0

@JanuszSkonieczny avez-vous le même problème avec Python 3 – warvariuc

+0

Oui, je l'ai fait dans le conteneur docker. Je l'ai résolu en mettant en place un tas de variables env liées à l'encodage os. Pour quiconque trébuche ici avec le même problème, voir http://stackoverflow.com/a/27931669/260480. –

Questions connexes