Bienvenue sur IndexError.

Ici vous pouvez poser des questions sur Python et le Framework Django.

Mais aussi sur les technos front comme React, Angular, Typescript et Javascript en général.

Consultez la FAQ pour améliorer vos chances d'avoir des réponses à vos questions.

logguer proprement des chaines unicode

+5 votes

Dans un script python 2.7 qui traite des centaines de fichiers, je souhaite ajouter le path du fichier traité a chaque lignes loggées pour pouvoir tracer finement l’exécution de mon programme.
Potentiellement, ces path peuvent contenir des caractères accentués.

Je pourrais utiliser le paramètre 'extra' comme décrit dans la doc de Logger.debug, mais pas tres DRY.

Naïvement, j'ai tenté d'ajouter ce path au niveau du name donné a logging.getLogger, qui se retrouverait a être de la forme 'mon.module.montraitement - le path'.

Sauf que dans le fichier python2.7/logging/init.py a la ligne 1032 , le nom est convertit en str :

if isinstance(name, unicode):
    name = name.encode('utf-8')

Ce qui pose fatalement des problemes d'UnicodeDecodeError au moment du formatage :

In [7]: import logging
In [8]: logging.basicConfig(level=logging.DEBUG)
In [9]: logger = logging.getLogger(u"accentué")
In [10]: logger.info(u"accentué")
Traceback (most recent call last):
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/logging/__init__.py", line 851, in emit
    msg = self.format(record)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/logging/__init__.py", line 724, in format
    return fmt.format(record)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/logging/__init__.py", line 467, in format
    s = self._fmt % record.__dict__
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 12: ordinal not in range(128)
Logged from file <ipython-input-10-74334e0a80e1>, line 1

Une solution qui a l'air de marcher, c'est de forcer le name du logger avec la chaine unicode :

In [11]: logger_name = u"accentué"
In [12]: logger = logging.getLogger(logger_name)
In [13]: type(logger.name)
Out[13]: str
In [14]: logger.name = logger_name # on force le name
In [15]: type(logger.name)
Out[15]: unicode
In [16]: logger.info(u"accentué") # fonctionne
INFO:accentué:accentué

Mais cette solution me semble un peu sale.

Est-il possible de facilement injecter une chaine de caractère unicode qui ne soit pas convertit en str a chaque ligne de log, sans utiliser ni le name ni les paramètre extra ?

edit: fixé typo dans le script d'exemple ligne 12

demandé 13-Fev-2015 par jc (2,674 points)
edité 14-Fev-2015 par jc

Mis a part la maniere dont tu t'y prends, je ne vois pas (du haut de ma petite experience) une autre façon de renommer le logger avec une chaine unicode.

Petite question, quel est l'objectif en voulant nommer le logger avec une chaine de type unicode? Parce qu'il est en general consideré "best practice" de nommer le logger "__name__" afin de reconnaitre plus facilement le module ayant initié la ligne de log.

D'habitude, j'aurais effectivement eu tendance a utiliser "__name__".

Dans mon cas, le logger est un attribut d'une classe de base dont tous mes workers héritent (chaque worker peut être dans un module différent).

Avoir un logger "__name__" n'est pas adapté car je souhaite avoir un log du type :

INFO:workers.type_du_worker.type_du_traitement:message

Cette approche marche bien, mais pour augmenter la traçabilité, je souhaite intégrer le path du fichier traité dans chaque ligne ligne de log, pour obtenir quelque chose du genre :

INFO:workers.type_du_worker.type_du_traitement - path:message

J'utilise donc le name pour ne pas avoir a ajouter le path du fichier traité dans chaque appel au méthodes du logger (info()/debug()/...) en créant mon logger avec un appel du type

logger_name = u"workers.%s - %s" % (self.type, self.path)
self.logger = logging.getLogger(logger_name)

Les path, pouvant contenir des accents, sont traités comme des chaines unicode : le name du logger devient par extension une chaine unicode.

Au delà du problème de conversion du name d'unicode vers str, il faut peut etre que je recentre ma recherche sur comment ajouter une chaine de caractère de manière DRY a chaque ligne de log sans utiliser le name (== sans ajouter d'extra a tous mes appels au logger existant)

une autre question en passant: tu es obligé d'utiliser python2.7 dans ton projet ?
En python 3, tu n'aurais pas le pb:

Python 3.4.2 (default, Jan 12 2015, 11:38:40) 
[GCC 4.9.2 20141224 (prerelease)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import logging
>>> logging.basicConfig(level=logging.DEBUG)
>>> logger = logging.getLogger(u"accentué")
>>> logger.info(u"accentué")
INFO:accentué:accentué
>>> 

(d'ailleurs tu n'as plus à spécifier unicode:

Python 3.4.2 (default, Jan 12 2015, 11:38:40) 
[GCC 4.9.2 20141224 (prerelease)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import logging
>>> logging.basicConfig(level=logging.DEBUG)
>>> logger = logging.getLogger("accentué")
>>> logger.info("accentué")
INFO:accentué:accentué
>>> 

2 Réponses

+2 votes
 
Meilleure réponse

A vu de nez, c'est une limitation du logger de 2.7, vu que ça ne le fait pas en 3.

Si tu trouves pas extra DRY, souvient toi que tu peux utiliser functools:

import functools
info = functools.partial(log.info, extra=...}

C'est plus un moyen de contournement qu'une solution mais ça peut aider.

répondu 30-Mar-2015 par Sam (4,980 points)
sélectionné 29-Mai-2015 par jc
0 votes

J'aurais envie de te dire 2 choses:

  1. Simplifie toi la vie: évite un maximum les caractères accentués, que ce soit dans tes loggers ou tes noms de dossier. Pendant que tu te bats avec ce genre de bug, tu ne fais pas des tâches à valeur ajoutée pour toi ou tes utilisateurs.
  2. Utilise Python 3: Il y a pas mal de bugs à la consommation qui ont
    disparu en Python 3. De plus, la très grosse majorité des librairies
    supportent Python 3. Pour l'instant, j'ai migré il y a un an, pas de
    soucis.
répondu 19-Mar-2015 par GMLudo (108 points)
edité 19-Mar-2015 par GMLudo

Ta première remarque fonctionne quand ton code s’exécute en environnement maitrisé.
Ici, je n'ai pas la main sur la manière dont les utilisateurs nomment leurs fichiers.

Je comprend l'argument python 3, mais "change la stack" ne peut malheureusement pas être considéré comme une réponse acceptable ;)

...