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
vendor
etglide.lock
puis lancermake 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.
Logger
Principes et utilisation du 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