(sans saigner des yeux)
Chercher des informations dans des logs informes ou dans des tracebacks est à bien des égards une activité frustrante. À Yaal nous utilisons donc sentry, qui est capable d'agréger, de contextualiser et de mettre en forme les logs et les tracebacks que nous générons. Couplé à un système d'alertes efficace, l'outil s'avère très utile pour savoir ce qui se passe en production, et diagnostiquer rapidement lorsque surgissent des problèmes.
Dans la phase itérative du développement d'une fonctionalité, on génère souvent beaucoup d'erreurs temporaires et sans grande valeur. Les capacités qu'a Sentry à contextualiser et mettre en forme une erreur sont intéressante, mais on se passerait bien de l'historique et des alertes dans ce cas de figure. En plus de ça, ça fait un outil (de plus) à ouvrir, une page à actualiser... Bref, lorsque l'on développe et qu'un bug se produit, on veut avoir une information riche et lisible rapidement.
D'aucuns argueront que pour débugger, on utilise un debugger. Certes, mais l'utilisation d'un debugger est assez lourde, et de fait elle n'est pas automatique. De plus, on peut chercher à obtenir une information claire et instantanée sur des exceptions dans des contextes où un debugger n'est pas forcément pertinent. Je pense notamment dans des cas où l'on exécute en même temps plusieurs programmes qui interagissent entre eux (des démons, des serveurs web...), on ne lance pas un débugger derrière chaque programme dans ces cas-là avant (du moins, pas avant d'avoir rencontré un bug).
Comparons quelques outils de mise en forme de backtrace python, en gardant quelques critères en tête :
- La capacité de mettre une traceback en couleur ;
- La capacité de contextualiser les appels (afficher les quelques lignes de code avant ou après) ;
- La capacité d'afficher les variables locales à chaque étape de la stack ;
- Les methodes d'utilisation et l'invasivité (hook qui s'installe et attrape toutes les exceptions, utilisation manuelle dans des try-except, utilisation comme module python ex:
python -m foobar monscript.py
) ; - L'intégration dans une stack web (middleware WSGI) ;
- Le packaging avec pip et les dépendances ;
- L'activité et la taille du projet.
Un exemple de traceback dans sentry
Nous ne nous intéresseront pas à l'affichage des tracebacks dans les programmes de test unitaires.
Outils orienté terminal
Prenons un exemple de code en python s'exécutant dans un terminal.
Un script simple
L'affichage par défaut des tracebacks python donne le minimum d'information, mais on pourrait sans doute faire mieux. Où est la mise en forme ? Quelles sont les variables locales à chaque étape de la pile ?
backtrace
backtrace est un petit projet (30 commits) disponible dans pip. Il s'utilise seulement en hook, s'axe sur une mise en forme succincte et colorée, avec quelques options de personnalisation. Les variables locales ne peuvent pas être affichées mais la fonctionnalité est envisagée.
backtrace comme hook
colored-traceback
colored-traceback est un petit projet (9 commits) disponible dans pip. Il se présente comme un module python ou un hook, et se contente de rajouter de la couleur aux tracebacks générées par python, en gardant la même mise en forme. Il y a un petit problème dans l'utilisation par le module python : colored-traceback
apparaît lui-même dans la traceback, ce qui n'est facilite pas sa lisibilité.
colored-traceback utilisé comme module
colored-traceback comme hook
pygments
pygments est un outil de coloration syntaxique en python actif, disponible sur pip. pygments est capable de colorer des tracebacks python mais se cantonne à son rôle de coloration syntaxique, donc aucune mise en forme ou contexte ne sont ajoutés. Aucune aide n'est disponible non plus pour attraper les exceptions de manière automatique dans le code. On peut s'en sortir en écrivant un hook, ou une fonction de traitement dans un block try-except. Le binaire pygmentize
fourni dans le paquet permet tout de coloriser la sortie standard d'un script python.
hook fait-main avec pygments
sortie standard capturée avec pygmentize
IPython ultratb
IPython est shell python assez puissant (avec coloration, autocomplétion et autres) disponible dans pip. Par défaut il met en forme les tracebacks avec son module ultratb. Ultratb peut s'utiliser soit comme un hook qui attrape et traîte les exception d'un script, ou dans un bloc try-except
. Ultratb est surtout utilisé par défaut dans IPython, donc utiliser ipython
comme interpréteur à la place de python
peut suffir à afficher une traceback mise en forme. Plusieurs thèmes de couleur, et plusieurs mises en formes sont disponibles. Parmi les mises en forme proposés, on peut afficher une traceback simple, colorée avec ColorTB, ou une traceback avec du contexte, de la couleur, et le contenu des variables locales avec VerboseTB. Il semblerait qu'on ne puisse pas installer ultratb sans installer IPython.
Un script simple avec IPython
ultratab comme hook
ultratab dans un try-except
ultratab en mode verbeux
Outils orienté web
cgitb
cgitb est un mystère. C'est un module cœur de python, originellement utilisé pour générer des pages HTML mettant en forme des tracebacks, mais il peut aussi être utilisé pour du texte. Le module existe depuis Python 2.2 mais il a l'air assez peu connu du web, les deux paragraphes de documentation n'inspirent pas confiance, (deux nouveaux paragraphes ont été écrits pour python 3.8 \o/). Il existe un hook, mais il ne fait qu'écrire sur la sortie standard, sans doute est-ce un reliquat de l'époque où on l'utilisait dans des CGI. Néanmoins, en utilisant les fonctions non documentées du module, on peut arriver à générer des backtraces plutôt riches et avec du contexte. Par contre, pour des couleurs ou des pages web lisibles, on repassera. Les pages web générées sont aussi hideuses que leur code, et la sortie texte est monochrome. Les hooks, l'affichage dans un try-except
sont possibles, mais en l'écrivant à la main.
try-except avec cgitb
Une magnifique page web générée par cgitb
django
Le célèbre framework web django permet de mettre en forme les tracebacks python qui peuvent survenir lors de la génération d'une page web. Le module est dans le cœur de django, et n'est pas facilement réutilisable hors django. Il ne génère une traceback monochrome mise en forme, avec du contexte et des variables locales.
Traceback web avec django
werkzeug
Werkzeug embarque un middleware WSGI qui produit des pages HTML riches et belles, avec des accordéons pour ne pas la charger. L'intégration est très facile. Werkzeug permet en plus d'ouvrir un debugger interactif si le serveur d'application le permet. On regrettera cependant l'absence de variables locales et de couleurs.
Traceback web avec werkzeug
Conclusion
Voici un tableau récapitulant les résultats des critères que nous avons observé sur les différents outils :
sentry | ultratb | backtrace | colored-traceback | pygments | cgitb | django | werkzeug | |
---|---|---|---|---|---|---|---|---|
couleur | ✓ | ✓ | ✓ | ✓ | ✓ | ✗ | ✗ | ✗ |
contexte | ✓ | ✓ | ✗ | ✗ | ✗ | ✓ | ✓ | ✓ |
variables locales | ✓ | ✓ | ✗ | ✗ | ✗ | ✓ | ✓ | ✗ |
hook | ✓ | ✓ | ✓ | ✓ | ✗ | ✗(pour CGI) | ✗ | ✗ |
try except | ✓ | ✓ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ |
module python ou interpréteur | ✗ | ✓ | ✗ | ✓(bof) | ✗ | ✗ | ✗ | ✗ |
lecture sur l'entrée standard | ✗ | ✗ | ✗ | ✗ | ✓ | ✗ | ✗ | ✗ |
middleware WSGI | ✓ | ✗ | ✗ | ✗ | ✗ | ✗ | ✓ | ✓ |
sortie texte | ✗ | ✓ | ✓ | ✓ | ✓ | ✓ | ✗ | ✗ |
sortie html | ✗ | ✗ | ✗ | ✗ | ✗ | ✓(mais hiddeuse) | ✓ | ✓ |
projet très actif | ✓ | ✓ | ✗ | ✗ | ✓ | ✓ dans python peu soutenu | ✓ | ✓ |
dédié à l'affichage de tracebacks | ✓ | ✗ | ✓ | ✓ | ✗ | ✓ | ✗ | ✗ |
paquet pip | ✓ | ✓ | ✓ | ✓ | ✓ | ✓(déjà dans python) | ✓ | ✓ |
La conclusion que l'on peut tirer est qu'il n'existe pas d'outil remplissant tous les critères. ultratb semble être la meilleure option pour l'affichage en console, même s'il est embarqué dans IPython. Quant au monde du web, seul werkzeug semble sortir du lot, malgré qu'il ne soit pas dédié à la fonction d'afficher des tracebacks.
L'outil idéal pour mettre en forme des tracebacks, polyvalent, lisible et puissant, reste à développer. On peut cependant s'en tirer relativement correctement en utilisant ultratb pour l'affichage en terminal et werkzeug pour l'affichage web.