Apprendre à journaliser dans Go
Les journaux jouent un rôle significatif dans un programme, car ils constituent une source d’informations que vous pouvez consulter en cas de problème. En règle générale, quand une erreur se produit, les utilisateurs finaux voient simplement un message indiquant un problème lié au programme. Du point de vue d’un développeur, nous avons besoin d’informations supplémentaires par rapport à un simple message d’erreur. Cela est principalement dû au fait que nous voulons reproduire le problème pour écrire un correctif approprié. Dans ce module, vous allez découvrir comment fonctionne la journalisation dans Go. Vous allez également apprendre quelques pratiques que vous devez toujours implémenter.
Package log
Pour ceux qui débutent, Go offre un package standard simple pour l’utilisation des journaux. Vous pouvez l’utiliser de la même manière que vous utilisez le package fmt
. Le package standard ne fournit pas de niveaux de journalisation et ne vous permet pas de configurer des journaliseurs distincts pour chaque package. Si vous devez écrire des configurations de journalisation plus complexes, vous pouvez le faire à l’aide d’un framework de journalisation. Nous traiterons les frameworks de journalisation ultérieurement.
Voici le moyen le plus simple d’utiliser les journaux :
import (
"log"
)
func main() {
log.Print("Hey, I'm a log!")
}
Quand vous exécutez le code précédent, vous obtenez cette sortie :
2020/12/19 13:39:17 Hey, I'm a log!
Par défaut, la fonction log.Print()
contient la date et l’heure en tant que préfixe du message de journal. Vous pouvez obtenir le même comportement à l’aide de fmt.Print()
, mais vous pouvez effectuer d’autres opérations avec le package log
, comme envoyer des journaux à un fichier. Nous allons examiner d’autres fonctionnalités du package log
ultérieurement.
Vous pouvez utiliser la fonction log.Fatal()
pour journaliser une erreur et mettre fin au programme comme si vous utilisiez os.Exit(1)
. Pour essayer, nous allons utiliser cet extrait de code :
package main
import (
"fmt"
"log"
)
func main() {
log.Fatal("Hey, I'm an error log!")
fmt.Print("Can you see me?")
}
Quand vous exécutez le code précédent, vous obtenez cette sortie :
2020/12/19 13:53:19 Hey, I'm an error log!
exit status 1
Notez comment la dernière ligne, fmt.Print("Can you see me?")
, ne s’exécute pas. Cela est dû au fait que l’appel de la fonction log.Fatal()
arrête le programme. Vous obtenez un comportement similaire lorsque vous utilisez la fonction log.Panic()
, qui appelle également la fonction panic()
, comme suit :
package main
import (
"fmt"
"log"
)
func main() {
log.Panic("Hey, I'm an error log!")
fmt.Print("Can you see me?")
}
Quand vous exécutez le code précédent, vous obtenez cette sortie :
2020/12/19 13:53:19 Hey, I'm an error log!
panic: Hey, I'm an error log!
goroutine 1 [running]:
log.Panic(0xc000060f58, 0x1, 0x1)
/usr/local/Cellar/go/1.15.5/libexec/src/log/log.go:351 +0xae
main.main()
/Users/christian/go/src/helloworld/logs.go:9 +0x65
exit status 2
Vous recevez toujours le message de journal, mais vous obtenez maintenant aussi la trace de la pile d’erreurs.
Une autre fonction essentielle est log.SetPrefix()
. Vous pouvez l’utiliser pour ajouter un préfixe aux messages de journal de votre programme. Par exemple, vous pouvez utiliser cet extrait de code :
package main
import (
"log"
)
func main() {
log.SetPrefix("main(): ")
log.Print("Hey, I'm a log!")
log.Fatal("Hey, I'm an error log!")
}
Quand vous exécutez le code précédent, vous obtenez cette sortie :
main(): 2021/01/05 13:59:58 Hey, I'm a log!
main(): 2021/01/05 13:59:58 Hey, I'm an error log!
exit status 1
Vous définissez le préfixe une seule fois et vos journaux incluent des informations telles que le nom de la fonction à partir de laquelle le journal a été créé.
Vous pouvez explorer d’autres fonctions sur le site web Go.
Journalisation dans un fichier
Outre l’impression des journaux dans la console, vous souhaiterez peut-être envoyer des journaux à un fichier afin de pouvoir les traiter ultérieurement ou en temps réel.
Pourquoi souhaiteriez-vous envoyer des journaux à un fichier ? Tout d’abord, vous souhaiterez peut-être cacher des informations spécifiques à vos utilisateurs finaux. Ils peuvent ne pas être intéressés, ou vous exposez peut-être des informations sensibles. Lorsque vous avez des journaux dans des fichiers, vous pouvez ensuite centraliser tous les journaux à un emplacement unique et les mettre en corrélation avec d’autres événements. Ce modèle est classique : pour que les applications distribuées puissent être éphémères, comme les conteneurs.
Nous allons utiliser le code suivant pour tester l’envoi des journaux à un fichier :
package main
import (
"log"
"os"
)
func main() {
file, err := os.OpenFile("info.log", os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
if err != nil {
log.Fatal(err)
}
defer file.Close()
log.SetOutput(file)
log.Print("Hey, I'm a log!")
}
Quand vous exécutez le code précédent, vous ne voyez rien dans la console. Dans votre répertoire, vous devez voir un nouveau fichier nommé info.log qui contient les journaux que vous avez envoyés avec la fonction log.Print()
. Notez que vous devez commencer par créer ou ouvrir un fichier, puis configurer le package log
pour envoyer l’ensemble de la sortie vers un fichier. Vous pouvez ensuite continuer à utiliser la fonction log.Print()
comme vous le feriez normalement.
Frameworks de journalisation
Enfin, il peut arriver que les fonctions du package log
ne suffisent pas. Il peut s’avérer utile d’utiliser un framework de journalisation au lieu d’écrire vos propres bibliothèques. Quelques frameworks de journalisation pour Go sont Logrus, zerolog, zap et Apex.
Découvrons ce que nous pouvons faire avec zerolog.
Dans un premier temps, vous devez installer le package. Si vous avez travaillé dans cette série, vous utilisez probablement déjà des modules Go et vous n’avez donc rien à faire. Au cas où, vous pouvez exécuter cette commande sur votre station de travail pour installer les bibliothèques zerolog :
go get -u github.com/rs/zerolog/log
À présent, utilisez cet extrait de code pour essayer :
package main
import (
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
)
func main() {
zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
log.Print("Hey! I'm a log message!")
}
Quand vous exécutez le code précédent, vous obtenez cette sortie :
{"level":"debug","time":1609855453,"message":"Hey! I'm a log message!"}
Notez la façon dont vous devez simplement inclure les noms d’importation corrects et vous pouvez ensuite continuer à utiliser la fonction log.Print()
comme vous le faites habituellement. Notez également que la sortie passe au format JSON. JSON est un format utile pour les journaux lorsque vous exécutez des recherches à un emplacement centralisé.
Une autre fonctionnalité utile est que vous pouvez inclure des données de contexte rapidement, comme suit :
package main
import (
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
)
func main() {
zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
log.Debug().
Int("EmployeeID", 1001).
Msg("Getting employee information")
log.Debug().
Str("Name", "John").
Send()
}
Quand vous exécutez le code précédent, vous obtenez cette sortie :
{"level":"debug","EmployeeID":1001,"time":1609855731,"message":"Getting employee information"}
{"level":"debug","Name":"John","time":1609855731}
Notez la façon dont nous avons ajouté l’ID de l’employé comme contexte. Il devient partie intégrante du journal comme une autre propriété. En outre, il est important de souligner que les champs que vous incluez sont fortement typés.
Vous pouvez implémenter d’autres fonctionnalités avec zerolog, comme l’utilisation de la journalisation nivelée, l’utilisation des traces de pile mises en forme et l’utilisation de plusieurs instances de journaliseur pour gérer différentes sorties. Pour plus d’informations, consultez le site GitHub.