Der Standardfehler von go v1.x ist so einfach, dass ihm häufig die gewünschten Funktionen fehlen.
Es gibt pkg / error
usw., die die Verwendung des Standardfehlers vereinfachen, aber der Fehler selbst hat immer noch einen bestimmten Status (Statuscode, Fehlerstufe usw.). Wenn Sie es behalten möchten, müssen Sie einen benutzerdefinierten Fehler dafür erstellen.
Das ist an sich in Ordnung, aber wenn Sie Sentry über einen Fehler informieren möchten, wenn dieser auftritt
In sentry-go
CaptureException ()
wird davon ausgegangen, dass das folgende Paket zum Abrufen von Stacktrace verwendet wird. Es ist geworden.
Dieses Mal habe ich versucht, mit der Implementierung Stacktrace in Sentry mithilfe eines benutzerdefinierten Fehlers anzuzeigen.
sentry-go verfügt über die folgenden drei Erfassungsmethoden
Ich denke, Sie verwenden grundsätzlich "CaptureException" oder "CaptureMessage" Wie Sie beim Lesen des Quellcodes sehen können, ist in "CaptureException" "CaptureMessage" nur die Erstellung von Event der ursprüngliche Prozess und schließlich alles CaptureEvent wird aufgerufen.
Was ist diesmal wichtig, um Stacktrace zu erfassen?
Es ist ExtractStacktrace
, das den Stacktrace des Ereignisses in CaptureException
erhält.
Wie Sie sehen können, erhält Reflection Stacktrace gemäß Konvention aus der Stacktrace-Implementierung jedes Fehlerpakets. Kurz gesagt, wenn Sie dieselbe Schnittstelle wie die Stacktrace-Implementierung jedes Pakets mit einem benutzerdefinierten Fehler implementieren, sollten Sie in der Lage sein, den Stacktrace in Sentry abzurufen.
Der ursprünglich erstellte benutzerdefinierte Fehler wurde basierend auf pkg / error erweitert, also pkg / Errors. Wir werden die Stacktrace-Schnittstelle von pkg / error implementieren.
pkg / Errors
für benutzerdefinierte FehlerKlicken Sie hier, um die Methode zu implementieren, die für den benutzerdefinierten Fehler implementiert werden soll, den Sentry in Reflection aufruft
// 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 bezieht sich auf jede Frame-Information des Stack-Trace. StackTrace ist die Sammlung. Nur die Implementierung des oben genannten hat keine Informationen im benutzerdefinierten Fehlerstapel Wenn ein Fehler auftritt, muss aus den Laufzeitinformationen von golang ein Frame erstellt werden.
Ich wünschte, ich könnte die Funktion von "pkg / fehler" so wie sie ist verwenden, aber da "Aufrufer", die Stacktrace mit "pkg / fehler" erhalten, eine private Funktion ist, ist es notwendig, die gleiche Verarbeitung für benutzerdefinierte Fehler zu implementieren, wie sie ist. Die Implementierung zum Abrufen von Stacktrace beim Erstellen eines Fehlers lautet wie folgt.
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
}
Tiefe
gibt die Tiefe des zu erfassenden Stacktrace an, und der Parameter" 4 "von Runtime.Callers ()
gibt die Anzahl der Stapel an, die zu Stacktrace gesprungen werden sollen, damit die Informationen im Fehlerpaket nicht gestapelt werden.
Diese Anzahl von Sprüngen hängt von der Implementierung von Fehlerpaketen ab. Überprüfen Sie daher die Anzahl der Nester, bevor Sie callers () aufrufen.
Übrigens, wenn Sie Go 1.7 oder höher haben, können Sie auch die Funktion runtime.CallersFrames ()
verwenden, die Stacktrace (runtime.Frames) erhält.
https://golang.org/pkg/runtime/#example_Frames
Als Beispiel für die Implementierung von Stacktrace Das Beispiel mit dem fehlerhaften gprc.status lautet wie folgt.
error.go
type CustomError interface {
Error() string
Status() *status.Status
}
type customError struct {
status *status.Status
*stack //Hier geht es darum, die Stacktrace-Methode zu implementieren
}
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()}
}
Wenn Sie nur benutzerdefinierte Fehler in der App verwenden, ist die obige Implementierung in Ordnung. In einer echten App müssen Sie den Ursprungsfehler beibehalten, der in einem anderen Subsystem oder einer anderen Bibliothek aufgetreten ist. In diesem Fall muss der benutzerdefinierte Fehlerstapel den Ursprungsfehler Stacktrace erben.
In diesem Fall nehmen wir an, dass das im Subsystem verwendete Fehlerpaket pkg / error ist. Um den Stacktrace für pkg / Errors zu erhalten, lesen Sie den Quellcode für pkg / Errors. Es wird im Kommentar ausführlich beschrieben. 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.
Um den Stacktrace von pkg / Errors zu erhalten, der der Ursprungsfehler ist, und ihn in den Stack neu zu packen, implementieren Sie ihn wie folgt.
error.go
type CustomError interface {
Error() string
Status() *status.Status
Origin() error
}
type customError struct {
status *status.Status
origin error //Ursprungsfehler speichern
*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()}
}
Wenn der Ursprungsfehler "pkg / Errors" ist, rufen Sie die StackTrace-Implementierung von "pkg / Errors" auf, um den Frame abzurufen, konvertieren Sie den Wert dann einmal in den Wert des Programmzählers und speichern Sie ihn im Stapel. Wenn das Subsystem ein anderes Fehlerpaket als "pkg / error" verwendet, ist die Stacktrace-Implementierung natürlich für jedes Paket unterschiedlich, sodass zusätzliche Unterstützung erforderlich ist.
Das Erweitern einer bestimmten Bibliothek zum Implementieren eines benutzerdefinierten Fehlers ist ziemlich einfach. Wenn Sie einen benutzerdefinierten Fehler mit einer Bibliothek eines Drittanbieters wie Sentry verwenden, funktioniert dieser möglicherweise nur dann ordnungsgemäß, wenn er gemäß den Methoden vieler Fehlerbibliotheken erstellt wurde. Vergessen Sie nicht, Stacktrace ordnungsgemäß zu implementieren, insbesondere wenn Sie benutzerdefinierte Fehler implementieren.
Hier finden Sie eine etwas detailliertere Beschreibung des Codes. https://zenn.dev/tomtwinkle/articles/18447cca3232d07c9f12
Recommended Posts