Trop souvent laissés de côté, des logs applicatifs bien pensés s’avérereront précieux en cas problème. Nous vous proposons dans cet article de vous présenter différentes techniques utilisables dans vos programme Node.js pour logger efficacement les informations dont vous aurez besoin lors de séances de troubleshooting / audit de vos programmes.
Notes :
- La question des frameworks de logs est volontairement laissée de côté et pourra faire l’objet d’un prochain article.
- Les extraits de code présentés sont réalisés en CoffeeScript.
Lorsqu’il est nécessaire de pouvoir logger le contenu d’un objet ou bien une de ses propriétés, le premier réflexe sera d’en afficher le contenu en le concaténant dans une chaîne de caractères comme suit:
foobar = foo: "bar" console.log "Contenu de la variable foobar: '#{foobar}'"
Malheureusement, le résultat ne correspond pas toujours à ce qu’on imagine:
Contenu de la variable foobar: '[object Object]'
Vu comme ça, on n’en fera pas grand chose si on met le nez dans les logs. Bien sûr, il est possible d’afficher la valeur de la propriété ‘foobar’ de l’objet:
console.log "Contenu de la variable foobar: '#{foobar.foo}'"
Le résultat sera un peu meilleur car on sera en mesure de logger la valeur de foo:
Contenu de la variable foobar: 'bar'
Dans notre cas, nous avons de la chance, car la variable ‘foo’ est de type String. Mais si la variable en question est un objet ou bien un tableau, nous tomberons sur le même problème.
JavaScript et Node.js nous proposent au moins deux solutions pour nous aider à produire un log convenable: La fonction ‘JSON.stringify’, et la fonction ‘inspect’ du module ‘util’.
JSON.stringify()
Bien connu dans le monde du JavaScript côté browser pour sérialiser le contenu d’une requête AJAX par exemple, on ne pense pas toujours à utiliser cette méthode pour sérialiser un objet ou bien un tableau JavaScript dans le but d’en logger son contenu :
console.log "Contenu de la variable foobar: '#{JSON.stringify(foobar})'"
On obtient déjà quelque chose de bien plus intéressant, c’est à dire un log avec un descriptif complet de l’objet au format JSON :
Contenu de la variable foobar: '{"foo":"bar"}'
Des objets plus complexes peuvent néanmoins devenir difficile à relire dans les logs, il ne faut donc pas hésiter à renseigner les informations de formatage du JSON lors de l’appel de la méthode ‘stringify’, qui permettent d’obtenir un log plus lisible avec un contenu bien indenté:
console.log "Contenu de la variable foobar: '#{JSON.stringify(foobar, undefined, 2})'"
Ce qui donne :
Contenu de la variable foobar: '{ "foo": "bar" }'
Le dernier paramètre permet de spécifier l’indentation souhaitée.
La documentation complète de la méthode stringify peut être consulté sur le site de Mozilla : https://developer.mozilla.org/fr/docs/JavaScript/Reference/Objets_globaux/JSON/stringify
Les tableaux
La méthode JSON.stringify peut être utilisée également avec des tableaux :
foo = [ "foo", "bar" ] console.log "Contenu de la variable foobar: '#{JSON.stringify(foo, undefined, 2)}'"
Ce qui donne :
Contenu de la variable foobar: '[ "foo", "bar" ]'
Les fonctions
Malheureusement, cela ne fonctionne pas avec les fonctions qui ne font pas partie du standard JSON :
bar = (foo, bar) -> "#{foo}-#{bar}" console.log "Contenu de la variable foobar: '#{JSON.stringify(bar, undefined, 2)}'"
Ce qui donne :
Contenu de la variable foobar: 'undefined'
Il ne faut pas oublier que nous détournons une fonctionnalité qui n’est pas spécialement faite au départ pour le logger… Heureusement un appel à la fonction toString() nous sortira d’affaire :
console.log "Contenu de la variable foobar: '#{bar.toString()}'"
ou tout simplement :
console.log "Contenu de la variable foobar: '#{bar}'"
Donne :
Contenu de la variable foobar: 'function (foo, bar) { return "" + foo + "-" + bar; }'
Attributs non listables
Enfin, il est possible d’afficher les champs non listables d’un objet grâce au second paramètre de la fonction stringify(). Cela peut s’avérer très utile, par exemple, pour logger différents attributs non listables des objets de type Error.
L’exécution du code suivant :
err = new Error("Some bad error") console.log "Contenu de la variable err: '#{JSON.stringify(err)}'"
donne :
Contenu de la variable err: '{}'
alors que le code suivant:
err = new Error("Some bad error") console.log "Contenu de la variable err: '#{JSON.stringify(err, ["message"])}'"
donne :
Contenu de la variable err : '{"message":"Some bad error"}'
util.inspect()
Le module util de Node.js fournit quelques fonctions intéressantes, dont la fonction: inspect. Elle permet de formatter un contenu de la même façon que le réalise la fonction JSON.stringify que ce soit un objet ou bien un tableau. Elle a l’avantage d’être plus souple en proposant différentes options de sérialisation telles qu’une profondeur limite d’inspection ou bien encore la capacité d’appeler la méthode inspect() sur les sous-objets de l’arbre, lorsqu’ils la proposent, afin de produire un contenu sérialisé spécifique.
Par exemple, les appels suivants :
util = require 'util' console.log "Contenu de la variable foobar: '#{util.inspect(foobar)}'" console.log "Contenu de la variable foobar: '#{util.inspect(foo)}'" console.log "Contenu de la variable foobar: '#{util.inspect(bar)}'"
donneront :
Contenu de la variable foobar: '{ foo: 'bar' }' Contenu de la variable foobar: '[ 'foo', 'bar' ]' Contenu de la variable foobar: '[Function]'
Et la customisation de la sérialisation comme suit :
qix = { foo:"bar", foobar: { inspect: () -> "Some custom representation of foobar" } console.log "Contenu de la variable foobar: '#{util.inspect(qix)}'"
donnera :
Contenu de la variable foobar: '{ foo: 'bar', foobar: Some custom representation of foobar }'
On notera avec ce dernier exemple que la fonction util.inspect() n’a pas vocation à produire un JSON valide, mais elle produira par défaut une notation proche du JSON.
Il est possible entre autre d’afficher les propriétés non-enumerables d’un objet, de coloriser le résultat de l’appel ou bien encore de désactiver l’appel des inspections customisées sur les objets.
Pour approfondir : http://nodejs.org/api/util.html#util_util_inspect_object_options
Pour aller plus loin …
Sachez qu’il existe des modules Node.js dédiés à la sérialisation d’objets à des fins d’analyse. Par exemple, le module nodedump permet de logger dans une sortie HTML un tableau qui détaille le contenu de l’objet :
Bien entendu, vous pourrez toujours préférer le debugger de votre IDE favori, son côté interactif apporte de nombreux bénéfices, et l’évaluation d’expressions se révèle être d’une grande aide :