Cet article est inspiré du sujet du
Go Forum lancé par Nate Finch. Ce post est axé sur Go, mais si vous dépassez cela, je pense que les idées présentées ici sont largement applicables.
Pourquoi n'y a-t-il pas d'amour?
Le package de journaux dans Go n'a pas de niveau pour les journaux; vous pouvez ajouter manuellement les préfixes DEBUG, INFO, WARN et ERROR. En outre, le type d'enregistreur dans Go n'a pas la possibilité d'activer ou de désactiver ces niveaux séparément pour les packages sélectionnés. À titre de comparaison, regardons quelques remplacements de développeurs tiers.

Google
glog a des niveaux:
- Info
- Avertissement
- Erreur
- Fatal (termine le programme)
Regardons une autre bibliothèque,
loggo , développée pour Juju, les niveaux y sont disponibles:
- Trace
- Déboguer
- Info
- Avertissement
- Erreur
- Critique
Loggo a également la possibilité de définir le niveau de détail du journal pour les packages souhaités individuellement.
Voici deux exemples, clairement créés sous l'influence d'autres bibliothèques pour se connecter dans d'autres langues.
En fait, leur origine peut être retracée à syslog (3), peut-être même plus tôt. Et je pense qu'ils ont tort.
Je veux prendre une position controversée. Je pense que toutes les bibliothèques de journaux sont mauvaises car elles offrent trop de fonctionnalités; un ensemble étonnant de fonctionnalités qui stupéfie le programmeur au moment même où il doit penser clairement à la façon de communiquer avec le lecteur du futur, avec qui verra ces magazines.
Je soutiens que les packages de journaux réussis nécessitent beaucoup moins de fonctionnalités et, bien sûr, moins de niveaux.
Parlons des avertissements (AVERTISSEMENT)
Commençons par le plus simple. Personne n'a besoin du niveau de journalisation AVERTISSEMENT (avertissement).
Personne ne lit les avertissements, car par définition, rien de mauvais ne s'est produit. Peut-être que quelque chose va mal à l'avenir, mais cela ressemble à quelqu'un d'autre, pas à mon problème.
De plus, si vous utilisez une sorte de journalisation à plusieurs niveaux, pourquoi devez-vous définir le niveau AVERTISSEMENT? Vous définiriez le niveau sur INFO ou ERREUR. La définition du niveau AVERTISSEMENT signifie que vous signalez probablement des erreurs au niveau AVERTISSEMENT.
Exclure le niveau d'avertissement - il s'agit soit d'un message d'information, soit d'une erreur.
Parlons du niveau d'erreur fatale
Le niveau FATAL enregistre en fait le message, puis appelle os.Exit (1). En principe, cela signifie:
- les expressions différées dans d'autres routines (goroutines) ne sont pas exécutées;
- les tampons ne sont pas vidés;
- les fichiers et répertoires temporaires ne sont pas supprimés.
En substance, log.Fatal est moins verbeux, mais sémantiquement équivalent à la panique.
Il est généralement admis que les bibliothèques ne doivent pas utiliser panic1, mais si l'appel à log.Fatal2 a le même effet, il doit également être désactivé.
L'hypothèse selon laquelle ce problème de nettoyage peut être résolu en enregistrant des gestionnaires d'arrêt dans l'enregistreur fournit une relation étroite entre l'enregistreur utilisé et chaque endroit où les opérations de nettoyage se produisent. Il viole également la séparation des intérêts.
N'enregistrez pas de messages avec un niveau FATAL, préférez renvoyer une erreur à l'appelant à la place. Si l'erreur atteint main.main, c'est le bon endroit pour effectuer un nettoyage avant de fermer le programme.
Parlons de l'erreur (niveau ERREUR)
La gestion des erreurs et la journalisation sont étroitement liées, par conséquent, à première vue, la journalisation du niveau d'erreur (ERREUR) devrait être facilement justifiée. Je ne suis pas d'accord.
Dans Go, si l'appel d'une fonction ou d'une méthode renvoie une valeur d'erreur, vous avez vraiment deux options:
- gérer l'erreur.
- retourner une erreur à son appelant. Vous pouvez magnifiquement emballer une erreur dans un emballage cadeau (emballage cadeau), mais ce n'est pas important pour cette discussion.
Si vous décidez de traiter l'erreur en l'écrivant dans le journal, alors par définition ce n'est plus une erreur - vous l'avez traitée. Le fait d'enregistrer l'erreur elle-même est le traitement de l'erreur; il n'est donc plus conseillé de l'écrire dans le journal comme une erreur.
Permettez-moi de vous convaincre avec ce morceau de code:
err := somethingHard() if err != nil { log.Error("oops, something was too hard", err) return err
Vous ne devez jamais enregistrer quoi que ce soit au niveau de l'erreur car vous devez soit gérer l'erreur, soit la transmettre à l'appelant.
Vous devez comprendre clairement, je ne dis pas que vous ne devriez pas écrire dans le journal qu'un changement d'état s'est produit:
if err := planA(); err != nil { log.Infof("could't open the foo file, continuing with plan b: %v", err) planB() }
Mais en réalité, log.Info et log.Error ont le même objectif.
Je ne dis pas "ne pas enregistrer d'erreurs"! Au lieu de cela, je pose la question, quelle est la plus petite API possible pour la journalisation? Et quand il s'agit d'erreurs, je crois que la grande majorité des choses enregistrées au niveau ERREUR sont simplement faites parce qu'elles sont liées à l'erreur. En fait, ils sont juste informatifs, nous pouvons donc supprimer la journalisation au niveau des erreurs (ERROR) de notre API.
Que reste-t-il?
Nous avons exclu AVERTISSEMENT, soutenu que rien ne devrait être enregistré au niveau d'erreur (ERREUR) et montré que seule l'application de niveau supérieur devrait avoir une sorte de comportement log.Fatal. Que reste-t-il?
Je pense qu'il n'y a que deux choses que vous devez vous connecter:
- les choses dont les développeurs se soucient lorsqu'ils développent ou déboguent un programme;
- les choses qui intéressent les utilisateurs lorsqu'ils utilisent votre programme.
De toute évidence, il s'agit des niveaux de débogage (DEBUG) et d'information (INFO), respectivement.
log.Info devrait simplement écrire cette ligne dans la sortie du journal. Il ne devrait pas être possible de le désactiver, car l'utilisateur ne devrait dire que ce qui lui est utile. Si une erreur se produit qui ne peut pas être traitée, elle devrait apparaître dans main.main où le programme se termine. L'inconvénient mineur d'avoir à insérer le préfixe FATAL avant le message de journal final ou à écrire directement dans os.Stderr à l'aide de fmt.Fprintf n'est pas une raison suffisante pour étendre le package avec la méthode log.Fatal.
log.Debug, c'est une question complètement différente. Un développeur ou un ingénieur de support en a besoin pour contrôler le programme. Pendant le développement, les expressions de débogage doivent être nombreuses sans recourir au niveau de trace ou debug2 (vous savez qui vous êtes). Le package de journalisation doit prendre en charge le contrôle granulaire pour activer ou désactiver les expressions de débogage pour les packages souhaités dans le package, ou peut-être même dans une portée plus étroite.
Conclusion
S'il s'agissait d'un sondage Twitter, je vous demanderais de choisir entre
- la journalisation est importante
- la journalisation est difficile
Mais le fait est que la journalisation est à la fois. La solution à ce problème DOIT être de détruire et de réduire sans pitié les distractions inutiles.
Qu'en penses-tu? Est-ce assez extravagant pour fonctionner, ou est-ce simplement extravagant?
Remarques
Certaines bibliothèques peuvent utiliser la panique / récupération comme mécanisme de flux de contrôle interne, mais le mantra principal est qu'elles ne devraient pas permettre à ces opérations de flux de contrôle de sortir de la limite du package.
Ironiquement, bien qu'il n'ait pas de niveau de sortie DEBUG, le package de journalisation Go standard a des fonctionnalités Fatal et Panic. Dans ce package, le nombre de fonctions qui conduisent à l'arrêt brutal du programme dépasse le nombre de celles qui ne le font pas.
À propos de l'auteur
L'auteur de cet article,
Dave Cheney , est l'auteur de nombreux packages populaires pour Go, par exemple
github.com/pkg/errors et
github.com/davecheney/httpstat . Vous pouvez évaluer vous-même l'autorité et l'expérience de l'auteur.
Du traducteur
De nombreux développeurs sont préoccupés par la journalisation, certains ont
discuté de l' ajout d'une interface Logger à la bibliothèque Go standard pour rationaliser la journalisation dans les bibliothèques et stimuler ainsi leurs développeurs. Les gars ont même rédigé leur proposition et mis le
document en discussion.
Plus la réflexion sur la présentation
Avons-nous besoin d'un nouvel enregistreur et quel devrait-il être? de Chris Hines.
Il existe plusieurs implémentations des idées de
go-log de Dave et un petit hors-sujet sur la question du niveau ERREUR et un package
logr plus élaboré.