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.
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.
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).
pkg / errors
pour l'erreur personnaliséeCliquez 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()}
}
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.
É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.
Voici une description un peu plus détaillée du code. https://zenn.dev/tomtwinkle/articles/18447cca3232d07c9f12
Recommended Posts