Bibliothèque vs application : des besoins de journalisation fondamentalement différents
- Journalisation d’application : configuration et gestion explicites dans un environnement directement contrôlé par le développeur
- Journalisation de bibliothèque : intégrée au projet d’autrui, elle doit respecter l’environnement et la liberté de choix de l’utilisateur
- Limites des approches existantes : appliquer à une bibliothèque des loggers centrés sur l’application (winston, Pino) pose un problème d’imposition
- Le dilemme des auteurs de bibliothèques : fournir des informations de débogage sans faire peser de charge sur l’utilisateur
Les problèmes actuels de la journalisation des bibliothèques
- Écosystème fragmenté : Express utilise
DEBUG=express:*, Mongoose utilise mongoose.set('debug', true), etc., chacun avec sa propre méthode
- Dilemme des dépendances : utiliser des bibliothèques centrées sur l’application comme winston ou Pino impose aux utilisateurs des dépendances et des configurations non désirées
- Silence vs imposition : soit renoncer complètement à la journalisation, soit imposer son propre mode de logging aux utilisateurs
- Complexité de l’injection de dépendances : approche plus raffinée, mais qui alourdit l’API et augmente la charge côté utilisateur
La philosophie « library-first » de LogTape
- Activation conditionnelle : si la journalisation n’est pas configurée, elle reste totalement inactive ; si elle l’est, elle est gérée de manière intégrée
- Préservation du choix utilisateur : la bibliothèque n’impose pas sa façon de journaliser et n’active le logging que lorsque l’utilisateur le souhaite
- Zéro dépendance : 5,3 KB, ce qui élimine les risques de sécurité de la supply chain et évite les conflits de version
- Prise en charge complète d’ESM/CJS : résolution des problèmes de chaîne de compatibilité et optimisation du bundle grâce au tree shaking
Des avantages concrets
- Optimisation des performances : surcharge quasi nulle lorsqu’il est désactivé, et excellentes performances de sortie console lorsqu’il est activé
- Séparation par espaces de noms : catégories hiérarchiques sous la forme
["my-lib", "feature"] pour éviter les collisions
- Conception TypeScript d’abord : sécurité de type complète sans paquet de types supplémentaire
- Pont avec les systèmes existants : adaptation progressive via des adaptateurs winston et Pino
Considérations réalistes
- Le sens des adaptateurs : reconnaissance du fait qu’il ne s’agit pas encore d’un standard de l’écosystème, avec un compromis pragmatique
- Inspiration de l’écosystème Python : référence au succès de Python, unifié autour de la bibliothèque standard
logging
- Approche tournée vers l’avenir : proposée comme une option pour améliorer progressivement l’écosystème des bibliothèques
7 commentaires
Je comprends mal en quoi le fait qu’il ne fonctionne pas sans configuration serait différent du simple fait de ne rien faire.
Quand on appelle déjà
getLoggerpour créer le logger puis qu’on émet undebug, ça fonctionne pourtant.À la lecture du code, j’ai surtout l’impression que ça donne juste l’apparence de ne pas fonctionner,
et comme ça ne retarde pas non plus les opérations sur les chaînes,
je comprends mal en quoi c’est différent d’autres bibliothèques qui n’affichent simplement rien quand on configure le niveau de log.
Hein, des logs s’affichent même sans avoir appelé
configure()/configureSync()? Où s’affichent-ils ? Dans la console ?Ah, ce que je voulais dire ici par le fait que cela fonctionne, ce n’est pas que les logs sont enregistrés dans la console ou dans un fichier, mais plutôt s’il y a réellement un surcoût dû à l’exécution de la fonction.
Il peut facilement y avoir un malentendu.
Bien sûr, étant donné que le principal surcoût d’un logger vient des appels système, on ne peut pas vraiment dire qu’il n’y a aucun surcoût.
Mais peut-on dire que c’est là ce qui le différencie des autres loggers ? Pas vraiment, puisque les autres loggers fonctionnent eux aussi de la même manière.
Ah, je vois ce que vous vouliez dire. Pour commencer, quand on fait tourner des benchmarks avec une sortie nulle, on peut considérer que l’overhead est presque inexistant. Mais ce qui me semblait plus important encore que l’overhead en termes de performances, c’était de savoir si le comportement par défaut est un no-op ou non. Du point de vue de l’auteur d’une bibliothèque, même si la bibliothèque produit des logs en interne, ce serait problématique que des logs s’affichent arbitrairement dans la console ou dans un fichier au moment de l’exécution de l’application qui utilise cette bibliothèque.
Ah, c’était un SHOW GN.
Ces temps-ci, comme l’écosystème choisit souvent des loggers injectés depuis l’extérieur, je pense que c’est aussi pour ça que ça me parlait moins.
S’il n’est pas configuré, ça ne fonctionne évidemment pas.
Cela dit, c’est aussi une interface de logger qui n’existait pas jusque-là dans cet écosystème, et comme elle offre beaucoup de liberté, je trouve ça mieux.
Pour le benchmark que vous avez donné, comme il émet une sortie nulle en excluant les system calls,
je pense que sur ce point, cela peut clairement varier selon la forme du logger interne.
Sur ce point, il prend même jusqu’à trois fois d’écart avec Pino.
FYI : en plus, pour la forme de logger injecté depuis l’extérieur dont je parlais, il suffit de regarder l’OpenAI Node SDK : il reçoit le logger depuis l’extérieur puis effectue la sortie, donc vous pourrez facilement le vérifier.
https://github.com/dahlia/logtape/…