Skip to content

Gestion des logs

Alexandre Moevi requested to merge zerolog into develop

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 vendor et glide.lock puis lancer make init peut aider).

  • Lancer les moteurs Go avec les options que vous utilisez habituellement plus ces deux options :

    • -d pour le niveau de log. Si -d est 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) logs si 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 à INFO soit à 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")

logs-1

Note : zl.Fatal().Msg() va appeler os.Exit(1), ce qui va arrêter le moteur directement. zl.Panic().Msg() va appeler panic(), 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")

logs-2

{"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
Edited by Alexandre Moevi

Merge request reports