Gestion des logs
Résumé
Mise en place d'une première version des logs pour les moteurs Go à l'aide de la bibliothèque zerolog.
Issue: #53 (moved)
Related merge requests : https://git.canopsis.net/canopsis/go-revolution/merge_requests/197 et https://git.canopsis.net/cat/go-engines/merge_requests/18
Processus de tests
-
S'assurer que les dépendances sont à jour (supprimer le répertoire
vendoretglide.lockpuis lancermake initpeut aider). -
Lancer les moteurs Go avec les options que vous utilisez habituellement plus ces deux options :
-
-dpour le niveau de log. Si-dest présent, alors les logs à partir du niveau debug seront affichés, sinon ce sera qu'à partir du niveau info.
-
-
Vérifier que les moteurs fonctionnent bien. Les logs ne doivent pas impacter le workflow des moteurs (la question d'un benchmark sur les performances se posera sans doute un peu plus tard).
-
Vérifier l'affichage des logs. Ils devraient s'afficher dans la console soit directement, soit avec
docker(-compose) logssi le moteur Go fonctionne dans un container Docker.
Zerolog
Pour une première approche de la gestion des logs (cf l'issue #53 (moved)), je me suis basé sur la bibliothèque zerolog. Voici le contenu de cette MR.
cmd/engine-XXX/main.go
Le point d'entrée sont les fichier mmain.go. Au démarrage de chacun des moteurs, un logger du type zerolog.Logger est créé. Ce logger est créé avec différents paramètres, seul le niveau de logs affichés est paramétrable. Il se base sur l'option -d déjà présente dans les moteurs.
if opts.ModeDebug {
zerolog.SetGlobalLevel(zerolog.DebugLevel)
} else {
zerolog.SetGlobalLevel(zerolog.InfoLevel)
}
logger := zerolog.New(zerolog.ConsoleWriter{Out: os.Stdout, TimeFormat: time.UnixDate}).With().Timestamp().Caller().Logger()
Les paramètres sont donc :
- Niveau de log, qui est soit à
INFOsoit àDEBUG - Sortie des logs qui est la sortie standard
os.Stdout - Format de la date en
Mon Jan _2 15:04:05 MST 2006 - Ajout du fichier et de la ligne d'où le message est loggé (par exemple
"/go/src/your_project/some_file:21")
Logger dans les objets Canopsis
Les logger créés dans les fichiers main.go vont ensuite être données en paramètres de plusieurs objets Canopsis. Les DefaultEngine et les différents Service (comme AlarmService, EntityService ou le déprécié TicketService) vont recevoir ce logger avec les mêmes paramètres.
Principes et utilisation du Logger
La logique de zerolog est de créer un évènement (zerolog.Event) puis de l'envoyer avec un message. Un événèment possède un niveau et les différentes fonctions renvoient un Event. logger.Debug() renvoie un Event avec un niveau debug, logger.Info() renvoie un Event avec un niveau info, etc.
Puis la création du message se fait avec Msg("this is a string") ou Msgf("who run the world? %+v", girls) à l'instar du Print et Printf en Go.
Logging simple
Pour envoyer des logs, il faut donc créer un Event qui sera envoyé avec un message.
zl := zerolog.New(zerolog.ConsoleWriter{Out: os.Stdout, TimeFormat: time.UnixDate}).With().Timestamp().Caller().Logger()
var sum int = 19
zl.Debug().Msgf("9 + 10 = %+v", sum)
zl.Info().Msg("Hello, world!")
zl.Warn().Msg("Something weird happened but it's still fine")
err := fmt.Errorf("unable to do something")
zl.Error().Msgf("Oops, something wrong happened : %+v", err)
zl.Fatal().Msg("Will stop everything!!!")
zl.Panic().Msg("Will end the goroutine")
Note :
zl.Fatal().Msg()va appeleros.Exit(1), ce qui va arrêter le moteur directement.zl.Panic().Msg()va appelerpanic(), ce qui va arrêter la goroutine.
Logging avec contexte
Si le message ne suffit pas, zerolog permet d'ajouter du contexte aux logs envoyés, sous le format clé-valeur. La clé est toujours un string, le type de valeur peut varier.
certDir := os.Getenv("SSL_CERT_DIR")
certFile := os.Getenv("SSL_CERT_FILE")
httpProxy := os.Getenv("HTTPS_PROXY")
httpsProxy := os.Getenv("HTTP_PROXY")
zl.Info().Str("SSL_CERT_DIR", certDir).Str("SSL_CERT_FILE", certFile).Str("HTTPS_PROXY", httpProxy).Str("HTTP_PROXY", httpsProxy).Msg("Environment variables will be used by the HTTP client")
{"level":"info","SSL_CERT_DIR":"","SSL_CERT_FILE":"","HTTPS_PROXY":"","HTTP_PROXY":"","time":"2019-04-18T16:51:49+02:00","caller":"/home/capensis/go/src/git.canopsis.net/cat/go-engines/plugins/axepostprocessor/webhook/main.go:82","message":"Environment variables will be used by the HTTP client"}
Ici, Str() a été utilisé pour ajouter un string dans le contexte. De même, Int() ajoute un entier au contexte, Err() une error ou Dur() pour une time.Duration.
zl.Warn().Str("webhook", webhook.ID).Err(err).Msg("Failed to generate url template for webhook")
// 10 May 19 16:41 CEST WRN home/capensis/go/src/git.canopsis.net/cat/go-engines/plugins/axepostprocessor/webhook/main.go:301 > Failed to generate url template for webhook error="template: webhook-url-459963:1: unexpected \"{\" in command" webhook id=master-webhook
zl.Info().Int("alarms", len(cancelResolved)).Msg("Resolved cancels")
// 10 May 19 16:41 CEST INF home/capensis/go/src/git.canopsis.net/canopsis/go-revolution/cmd/engine-axe/engine.go:109 > Resolved cancels alarms=0

