L’adoption d’une architecture serverless présente de nombreux intérêts, tels que la scalabilité et une meilleure résilience en cas de panne. En revanche, du fait de l’aspect distribué de ce type d’architecture, il peut-être difficile de consolider les logs se trouvant aux quatre coins du cloud. Dans cet article je vais parler d’une astuce facilitant le débogage et le parcours des logs des fonctions Lambda.

Logs, Lambda et CloudWatch

Lors de l’exécution d’une Lambda, les logs sont automatiquement transférés vers le service CloudWatch Logs. Ce service permet de centraliser et de stocker les logs des différents services et applications déployés sur AWS.

Pour une fonction lambda donnée, par exemple hello-gophers, les logs trouveront dans un groupe de log /aws/lambda/hello-gophers. Il est ainsi possible de consulter ces logs depuis la console AWS, la CLI ou encore en interagissant avec les APIs CloudWatch. Des fonctions plus avancées sont aussi disponibles, telles que le tri ou le filtrage par mots clefs.

Lorsque l’on a peu de trafic il est possible de se baser sur le timestamp afin d’identifier les messages concernant une requête en particulier. En revanche, si votre trafic augmente, vous allez avoir des exécutions concurrentes. Impossible donc de se baser sur le timestamp.

AWS Request ID

Fort heureusement AWS génère un request ID pour chaque exécution de votre lambda.
Il est d’ailleurs affiché dans les logs en début et en fin d’exécution :

Cloudwatch Logs avec le request ID

Cloudwatch Logs avec le request ID

Une bonne pratique consiste alors à inclure systématiquement ce request ID lorsque l’on log un message dans notre fonction.
Il est possible de le récupérer en utilisant le SDK du langage de son choix puis de configurer le framework de logging utilisé en conséquence.

Dans le cas du langage Go, cette information se trouve dans le lambdacontext.
https://github.com/aws/aws-lambda-go/blob/main/lambdacontext/context.go

Configuration avec zap

Le framework de logging que je préfère utiliser est zap. Il est rapide, facilement configurable et permet de logger au format json. C’est d’ailleurs celui que j’ai utilisé sur mon side project budgetcategorizer.

Pour configurer le request ID il faut dans un premier le récupérer, l’ajouter aux initial fields puis d’enregistrer cette configuration au niveau des global loggers.

Voici une implémentation possible :

func initLogger(ctx context.Context) {
	// Retrieve AWS Request ID
	lc, _ := lambdacontext.FromContext(ctx)
	requestID := lc.AwsRequestID
	cfg := zap.Config{
		Encoding:         "json",
		Level:            zap.NewAtomicLevelAt(zapcore.DebugLevel),
		OutputPaths:      []string{"stdout"},
		ErrorOutputPaths: []string{"stderr"},
		InitialFields:    map[string]interface{}{"request-id": requestID},
		EncoderConfig: zapcore.EncoderConfig{
			MessageKey: "message",

			LevelKey:    "level",
			EncodeLevel: zapcore.CapitalLevelEncoder,

			TimeKey:    "time",
			EncodeTime: zapcore.ISO8601TimeEncoder,

			CallerKey:    "caller",
			EncodeCaller: zapcore.ShortCallerEncoder,
		},
	}
	logger, _ := cfg.Build()
	defer zap.S().Sync()
	zap.ReplaceGlobals(logger)
}

Configuration avec logrus

Un des autres framework de logging populaire pour Go est logrus. Pour inclure le request ID systématiquement il faut d’abord instancier un logrus.Entry avec la méthode withFields() :

lc, _ := lambdacontext.FromContext(ctx)
requestID := lc.AwsRequestID
requestLogger := log.WithFields(log.Fields{"request-id": requestID})

Il est ensuite possible de le réutiliser :

requestLogger.Warn("chef on a un problème") # cette commande va logger request-id

Je trouve cela moins pratique et moins élégant de devoir réutiliser le même objet que de se baser sur un logger global comme le fait zap.

Filtrage par request ID

Une fois cette configuration mise en place, il est possible de retourner dans la console AWS et de filter les logs Cloudwatch par request ID.

Filtrage par request ID

Filtrage par request ID

De plus si vous utilisez un outil de gestion et d’analyse de logs tel que SumoLogic ou DataDog, vous allez pouvoir utiliser cette ID afin de rechercher les logs pertinents.


Si tu es arrivé jusqu’ici, merci beaucoup d’avoir lu cet article !
Pense à t’abonner à la mailing list pour ne rater aucun article, le formulaire se trouve en bas de page.
Photo de couverture par Christina @ wocintechchat.com.