[Go] Comment créer une erreur personnalisée pour Sentry

introduction

L'erorr standard de go v1.x est si simple qu'il manque souvent les fonctionnalités souhaitées. Il y a pkg / errors etc. qui rendent l'erreur standard plus facile à utiliser, mais l'erreur elle-même a toujours un statut spécifique (code d'état, niveau d'erreur, etc.). Si vous souhaitez le conserver, vous devrez créer une erreur personnalisée pour celui-ci.

C'est bien en soi, mais si vous souhaitez informer Sentry d'une erreur lorsqu'elle se produit Dans sentry-go CaptureException (), il est supposé que le package suivant est utilisé pour obtenir Stacktrace. Il est devenu.

Cette fois, j'ai essayé l'implémentation pour afficher Stacktrace dans Sentry en utilisant une erreur personnalisée.

Lisez la source de sentinelle

sentry-go propose les trois méthodes de capture suivantes

Je pense que vous utilisez essentiellement CaptureException ou CaptureMessage Comme vous pouvez le voir en lisant le code source, dans CaptureException`` CaptureMessage, seule la création de Event est le processus original et enfin tout CaptureEvent est appelé.

Ce qui est important comme processus pour capturer Stacktrace cette fois C'est [ʻExtractStacktrace](https://github.com/getsentry/sentry-go/blob/v0.7.0/stacktrace.go#L50) qui obtient le Stacktrace of Event dans CaptureException`.

Comme vous pouvez le voir, la réflexion obtient le Stacktrace par convention à partir de l'implémentation Stacktrace de chaque package d'erreur. En bref, si vous implémentez la même interface que l'implémentation Stacktrace de chaque package avec une erreur personnalisée, vous devriez pouvoir obtenir le Stacktrace dans Sentry.

Faire correspondre l'erreur personnalisée à Sentry

L'erreur personnalisée créée à l'origine a été étendue en fonction de pkg / errors, donc pkg / errors Nous allons implémenter l'interface Stacktrace de pkg / errors).

Implémentez la méthode Stacktrace pkg / errors pour l'erreur personnalisée

Cliquez ici pour la méthode à implémenter pour l'erreur personnalisée que Sentry appelle en réflexion

// Frame represents a program counter inside a stack frame.
// For historical reasons if Frame is interpreted as a uintptr
// its value represents the program counter + 1.
type Frame uintptr
// StackTrace is stack of Frames from innermost (newest) to outermost (oldest).
type StackTrace []Frame
// stack represents a stack of program counters.
type stack []uintptr

func (s *stack) StackTrace() StackTrace {
	f := make([]Frame, len(*s))
	for i := 0; i < len(f); i++ {
		f[i] = Frame((*s)[i])
	}
	return f
}

Frame fait référence à chaque information de trame de la trace de pile. StackTrace est la collection. La simple mise en œuvre de ce qui précède ne contient aucune information dans la pile d'erreurs personnalisée Il est nécessaire de créer un cadre à partir des informations d'exécution de golang lorsqu'une erreur se produit.

J'aurais aimé pouvoir utiliser la fonction de pkg / errors telle quelle, mais puisque callers qui obtient Stacktrace avec pkg / errors est une fonction privée, il est nécessaire d'implémenter le même traitement pour l'erreur personnalisée. La mise en œuvre pour obtenir Stacktrace lors de la création d'une erreur est la suivante.

func callers() *stack {
	const depth = 32
	const skip = 4
	var pcs [depth]uintptr
	n := runtime.Callers(skip, pcs[:])
	var st stack = pcs[0:n]
	return &st
}

depth indique la profondeur de Stacktrace à acquérir, et le paramètre" 4 "of` runtime.Callers () ʻindique le nombre de piles à passer à Stacktrace afin que les informations du package d'erreur ne soient pas empilées. Ce nombre de sauts dépend de l'implémentation des packages d'erreurs, vérifiez donc le nombre de nids avant d'appeler callers ().

En passant, si vous avez Go 1.7 ou supérieur, vous pouvez également utiliser la fonction runtime.CallersFrames () qui obtient Stacktrace (runtime.Frames). https://golang.org/pkg/runtime/#example_Frames

Comme exemple d'implémentation de Stacktrace L'exemple avec gprc.status en erreur est le suivant.

error.go


type CustomError interface {
	Error() string
	Status() *status.Status
}

type customError struct {
	status  *status.Status
	*stack //Le point ici est d'implémenter la méthode Stacktrace
}

func NewCustomError(code codes.Code, message string, args ...interface{}) error {
	return newCustomError(nil, code, message, args...))
}

func newCustomError(code codes.Code, message string, args ...interface{}) error {
	s := status.Newf(code, message, args...)
	return &customError{s, callers()}
}

Obtenir Stacktrace d'erreur d'origine autre qu'une erreur personnalisée

Si vous n'utilisez qu'une erreur personnalisée dans l'application, l'implémentation ci-dessus convient. Dans une vraie application, vous devrez conserver l'erreur d'origine qui s'est produite dans un autre sous-système ou bibliothèque. Dans ce cas, la pile d'erreurs personnalisée doit hériter de l'erreur d'origine Stacktrace.

Dans ce cas, supposons que le package d'erreur utilisé dans le sous-système est pkg / errors. Pour obtenir le Stacktrace pour pkg / errors, regardez le code source de pkg / errors. Il est décrit en détail dans le commentaire. https://github.com/pkg/errors/blob/v0.9.1/errors.go#L66

// Retrieving the stack trace of an error or wrapper
//
// New, Errorf, Wrap, and Wrapf record a stack trace at the point they are
// invoked. This information can be retrieved with the following interface:
//
//     type stackTracer interface {
//             StackTrace() errors.StackTrace
//     }
//
// The returned errors.StackTrace type is defined as
//
//     type StackTrace []Frame
//
// The Frame type represents a call site in the stack trace. Frame supports
// the fmt.Formatter interface that can be used for printing information about
// the stack trace of this error. For example:
//
//     if err, ok := err.(stackTracer); ok {
//             for _, f := range err.StackTrace() {
//                     fmt.Printf("%+s:%d\n", f, f)
//             }
//     }
//
// Although the stackTracer interface is not exported by this package, it is
// considered a part of its stable public interface.

En vous référant à ce qui précède, pour obtenir le Stacktrace de pkg / errors qui est l'erreur d'origine et le reconditionner dans la pile, implémentez comme suit.

error.go


type CustomError interface {
	Error() string
	Status() *status.Status
	Origin() error
}

type customError struct {
	status  *status.Status
	origin  error //Erreur d'origine du magasin
	*stack
}

func NewCustomErrorFrom(origin error, code codes.Code, message string, args ...interface{}) error {
	return newCustomError(origin, code, message, args...))
}

func newCustomError(origin error, code codes.Code, message string, args ...interface{}) error {
	s := status.Newf(code, message, args...)
	if origin != nil {
		// https://github.com/pkg/errors
		type stackTracer interface {
			StackTrace() errors.StackTrace
		}
		if e, ok := origin.(stackTracer); ok {
			originStack := make([]uintptr, len(e.StackTrace()))
			for _, f := range e.StackTrace() {
				originStack = append(originStack, uintptr(f))
			}
			var stack stack = originStack
			return &applicationError{s, origin, &stack}
		}
	}
	return &CustomError{s, origin, callers()}
}

Si l'erreur d'origine est pkg / errors, appelez l'implémentation StackTrace de pkg / errors pour obtenir la Frame, puis convertissez cette valeur en la valeur du compteur de programme une fois et stockez-la dans la pile. Bien sûr, si le sous-système utilise un package d'erreur autre que pkg / errors, l'implémentation de Stacktrace sera différente pour chaque package, donc des mesures séparées sont nécessaires.

en conclusion

Étendre une bibliothèque spécifique pour implémenter une erreur personnalisée est assez simple, Lorsque vous utilisez une erreur personnalisée à l'aide d'une bibliothèque tierce telle que Sentry, elle peut ne pas fonctionner correctement à moins qu'elle ne soit créée selon les manières de nombreuses bibliothèques d'erreurs. Veillez à ne pas oublier d'implémenter correctement Stacktrace, en particulier lors de l'implémentation d'une erreur personnalisée.

prime

Voici une description un peu plus détaillée du code. https://zenn.dev/tomtwinkle/articles/18447cca3232d07c9f12

Recommended Posts

[Go] Comment créer une erreur personnalisée pour Sentry
Comment créer un référentiel local pour le système d'exploitation Linux
Comment créer un package Conda
Comment créer un pont virtuel
Comment créer un Dockerfile (basique)
Comment créer un fichier de configuration
Comment créer un clone depuis Github
Comment créer un dossier git clone
Comment créer un fichier * .spec pour pyinstaller.
Comment créer une étiquette (masque) pour la segmentation avec labelme (masque de segmentation sémantique)
Comment créer un référentiel à partir d'un média
Comment créer un indicateur personnalisé Backtrader
Comment créer un objet fonction à partir d'une chaîne
Comment créer un fichier JSON en Python
Comment écrire une instruction ShellScript Bash for
[Note] Comment créer un environnement de développement Ruby
Comment créer une boîte de saisie Kivy 1 ligne
Procédure de création d'application multi-plateforme avec kivy
Comment créer une API Rest dans Django
[Go] Comment écrire ou appeler une fonction
[Note] Comment créer un environnement de développement Mac
Ce que j'ai appris en mettant en œuvre comment créer une boîte par défaut pour SSD
Lire la source Python-Markdown: Comment créer un analyseur
Créer un ensemble de données d'images à utiliser pour la formation
Comment créer un sous-menu avec le plug-in [Blender]
Comment déployer une application Go sur une instance ECS
Comment créer un environnement de développement pour TensorFlow (1.0.0) (Mac)
Comment créer un simple script serveur / client TCP
[Python] Comment créer un histogramme bidimensionnel avec Matplotlib
Comment appeler une fonction
Comment pirater un terminal
Comment définir les variables Go
[Aller] Comment utiliser "... (3 périodes)"
[Go language] Essayez de créer un compteur de lignes inutilement multithread
Comment gérer un README pour github et PyPI
Comment créer un maillage de flux autour d'un cylindre avec SnappyHexMesh
Comment définir plusieurs variables dans une instruction Python for
Comment créer un package Python (écrit pour un stagiaire)
[Python Kivy] Comment créer une simple fenêtre pop-up
Comment remplacer une valeur numérique pour une correspondance partielle (Remarque 1)
Comment rédiger un test de traitement utilisant BigQuery
J'ai essayé de créer un bot pour annoncer un événement Wiire
Je veux créer un Dockerfile pour le moment.
Comment configurer WSL2 sur Windows 10 et créer un environnement d'étude pour les commandes Linux
Comment faire une traduction japonais-anglais
Présentation de la création d'un socket serveur et de la création d'un socket client
Comment mettre un lien symbolique
Étapes pour créer un projet Django
Comment créer un bot slack
Comment créer un robot - Avancé
Comment personnaliser U-Boot avec une carte personnalisée pour OSD335X (Remarque)
Comment créer une fonction récursive
[Go] Créez une commande CLI pour changer l'extension de l'image