Pourquoi votre code Go plante, et comment y remédier sans maux de tête
\n\nVous avez écrit un code Go que vous pensiez parfait. La compilation s'est bien passée, les tests sont verts... mais en production, tout part en vrille. Ça vous dit quelque chose ? Bienvenue dans le monde réel du développement, où les bugs ne sont pas des erreurs de débutants, mais une partie de la croissance professionnelle. Aujourd'hui, nous allons aborder le débogage de code Go de manière à ce que vous ne vous contentiez pas de trouver les erreurs, mais que vous préveniez leur apparition dès la phase d'écriture.
\n\nGo est un langage qui offre au développeur des outils puissants pour le débogage directement « prêts à l'emploi ». Mais beaucoup n'utilisent que fmt.Println et sont sincèrement surpris de voir leurs goroutines se comporter comme des chats sur un toit brûlant. Corrigeons cela une fois pour toutes.
Technique n°1 : Print intelligent – quand log.Fatal n'est pas nécessaire
\n\nOui, je sais ce que vous allez dire : « Pourquoi aurais-je besoin de delve si je peux afficher une variable ? » Et vous aurez raison dans 80 % des cas. Mais le problème n'est pas fmt.Println, c'est comment vous l'utilisez.
Passons de Println à la journalisation structurée
\n\nAu lieu de coller des dizaines de prints, utilisez le package log ou slog (disponible depuis Go 1.21) :
package main\n\nimport (\n "log/slog"\n "os"\n)\n\nfunc main() {\n logger := slog.New(slog.NewTextHandler(os.Stdout, nil))\n \n // Au lieu de fmt.Println("erreur :", err)\n if err := doSomething(); err != nil {\n logger.Error("l'opération a échoué", \n "error", err,\n "module", "data_processor",\n "attempt", 3,\n )\n }\n}\n\n\nCette approche vous donne des journaux structurés, faciles à analyser, filtrer et interpréter. En production, cela vous évitera des appels nocturnes.
\n\nAstuce : compilation conditionnelle pour les logs de débogage
\n\nPour ne pas encombrer la production, utilisez les build tags :
\n\n//go:build debug\n\npackage main\n\nvar debug = true\n\n\n//go:build !debug\n\npackage main\n\nvar debug = false\n\n\nLancez avec le flag -tags debug – vous obtenez des logs détaillés. Sans le flag – une production propre.
Technique n°2 : Delve – votre détective personnel pour Go
\n\nSi le débogage par print est un vélo, Delve est une Tesla. Ce débogueur est spécialement conçu pour Go et comprend les goroutines, les interfaces et même les generics. L'installation est simple :
\n\ngo install github.com/go-delve/delve/cmd/dlv@latest\n\n\nDes points d'arrêt qui savent réfléchir
\n\nDelve permet de placer non seulement des breakpoints, mais aussi des points conditionnels :
\n\n(dlv) break main.go:25 if userID == 42\n(dlv) continue\n\n\nLe débogueur s'arrêtera uniquement lorsque userID sera égal à 42. Imaginez le temps gagné lors du débogage de boucles.
Exploration des goroutines
\n\nCe qu'il y a de plus effrayant dans Go, ce sont les deadlocks et les race conditions. Delve affiche toutes les goroutines actives :
\n\n(dlv) goroutines\n Goroutine 1 - User: ./main.go:10 main.main (0x109a5b6)\n Goroutine 2 - User: ./main.go:15 main.worker (0x109a6c8)\n* Goroutine 3 - User: ./main.go:22 main.worker (0x109a6c8)\n\n\nL'astérisque indique la goroutine actuelle. Basculez entre elles :
\n\n(dlv) goroutine 2\n(dlv) stack\n\n\nTravail avec la mémoire : voir ce qu'il y a à l'intérieur
\n\nLa commande locals affiche toutes les variables locales, et print affiche leurs valeurs avec les types :
(dlv) locals\ndata = []int len: 100000, cap: 100000\nresult = map[string]int ["error": 5, "success": 99995]\n(dlv) print data[0:5]\n[]int len: 5, cap: 100000, [42, 17, 89, 3, 256]\n\n\nTechnique n°3 : Profilage avec pprof – trouver les goulots d'étranglement
\n\nVotre code fonctionne, mais lentement ? pprof est le profileur intégré de Go. Il montre où le programme « ralentit » et où il consomme de la mémoire.
\n\n