Comment logger en go

Quelles sont les différentes stratégies de log en golang? Le langage go en natif, des frameworks tles que logrus, zerolog ou bien le slog le dernier né. Exemples de logs en Go...

Comment logger en go

Petit rappel évident mais il faut bien garder en tête à quoi servent les logs. Généralement, il s'agit de traces techniques qui vous serviront à cerner plus rapidement un problème (fonctionnel, technique, de performance...) de votre service ou application. Il ne faut pas logger d'informations fonctionnelles ni personnelles.

En natif en go

Par défaut le langages offre au moins 2 possibilités pour ajouter des logs :

  1. utiliser le package "fmt" et printX comme par exemple fmt.Printf("erreur de chargement de %s", maVariable)
    Il est évident que cela n'est pas tenable sur le long terme au fur et à mesure que la base de code augmente, mais qui ne l'a jamais fait... ;-)
  2. utiliser le package "log" et printX. Le package log natif est déjà très riche comme beaucoup d'autres packages natifs et évitent de se charger en dépendances externes.
package main

import (
	"fmt"
	"log"
)

func main() {
	fmt.Printf("Exemple pour logger :\n")
	log.Printf("mon premier log")
}

Le package log vous permet également de spécifier la sortie avec la méthode SetOutput(w io.Writer) et vous pouvez définir aussi votre propre logger avec func New(out io.Writer, prefix string, flag int) *Logger.

et le fichier logs.txt :

2022/06/20 12:06:51 mon premier log dans le fichier
INFO : 2022/06/20 12:06:51.716196 main.go:21: un log personnalisé

Vous pouvez aussi ajouter l'initialisation de votre système de log dans un répertoire utils  et une fonction init() par exemple :

package logging

import (
	"log"
	"os"
)

var (
	Info     *log.Logger
	Warning  *log.Logger
	Error    *log.Logger
	Critical *log.Logger
)

func init() {

	Info = log.New(os.Stdout, "INFO: ", log.Ldate|log.Ltime|log.Lshortfile)
	Warning = log.New(os.Stdout, "WARNING: ", log.Ldate|log.Ltime|log.Lshortfile)
	Error = log.New(os.Stderr, "ERROR: ", log.Ldate|log.Ltime|log.Lshortfile)
	Critical = log.New(os.Stderr, "CRITICAL: ", log.Ldate|log.Ltime|log.Lshortfile)
}

Cela reste intéressant pour des petits services ou utilitaires mais très vite il va manquer une rotation des fichiers, de la distribution pour un usage multi services...

Avec des librairies externes

Une des plus connues est certainement logrus. Seulement elle est passée en mode maintenance. Cela n'empêche pas de l'utiliser ni qu'elle soit utilisée dans de gros projets tel que Docker :

moby/health.go at 7b9275c0da707b030e62c96b679a976f31f929d3 · moby/moby
Moby Project - a collaborative project for the container ecosystem to assemble container-based systems - moby/health.go at 7b9275c0da707b030e62c96b679a976f31f929d3 · moby/moby

ou encore si vous utilisez datadog, ils privilégient logrus.

glog n'est également plus vraiment maintenue. C'est pourquoi les équipes de Kubernetees ont fait un fork pour leur projet :

kubernetes/vendor/k8s.io/klog/v2 at 609db7ed0b1f2839e414c17d29fe4d76edc994bd · kubernetes/kubernetes
Production-Grade Container Scheduling and Management - kubernetes/vendor/k8s.io/klog/v2 at 609db7ed0b1f2839e414c17d29fe4d76edc994bd · kubernetes/kubernetes

Dans un projet interne, j'ai implémenté zerolog. Cela permet de bien structurer ses logs mais on arrive très vite à beaucoup de code et de verbosité. IMHO, il devient intéressant à utiliser dans des projets de monitoring et donc couplé à des outils de visualisation de logs (Grafana, Kibana...)

Il y a beaucoup d'autres librairies mais le point important est de bien déterminer ce que l'on veut de ses logs...

Note du 28/12/2022 :

Un nouveau package est en cours et fera peut être son entrée dans une version future de go:

https://pkg.go.dev/golang.org/x/exp/slog

Avec ce package, vous pouvez directement écrire un :

slog.Info("une info en stdout")

# ou bien en declarant un text handler ou json handler :

textHandler := slog.NewTextHandler(os.Stdout)
logger := slog.New(textHandler)
logger.Info("une info supplémentaire", slog.Int("compteur", 10))

et il semble que les perfs soient bonnes (mais non vérifié).