[Go] So erstellen Sie einen benutzerdefinierten Fehler für Sentry

Einführung

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.

Lesen Sie die Quelle von Sentry-Go

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.

Stellen Sie sicher, dass der benutzerdefinierte Fehler Sentry entspricht

Der ursprünglich erstellte benutzerdefinierte Fehler wurde basierend auf pkg / error erweitert, also pkg / Errors. Wir werden die Stacktrace-Schnittstelle von pkg / error implementieren.

Implementieren Sie die Stacktrace-Methode pkg / Errors für benutzerdefinierte Fehler

Klicken 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()}
}

Holen Sie sich einen anderen Stacktrace of Origin-Fehler als einen benutzerdefinierten Fehler

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.

abschließend

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.

Bonus

Hier finden Sie eine etwas detailliertere Beschreibung des Codes. https://zenn.dev/tomtwinkle/articles/18447cca3232d07c9f12

Recommended Posts

[Go] So erstellen Sie einen benutzerdefinierten Fehler für Sentry
So erstellen Sie ein lokales Repository für Linux
So erstellen Sie ein Conda-Paket
So erstellen Sie eine virtuelle Brücke
Wie erstelle ich eine Docker-Datei?
So erstellen Sie eine Konfigurationsdatei
So erstellen Sie einen Klon aus Github
So erstellen Sie einen Git-Klonordner
So erstellen Sie eine * .spec-Datei für pyinstaller.
So erstellen Sie eine Bezeichnung (Maske) für die Segmentierung mit labelme (semantische Segmentierungsmaske)
So erstellen Sie ein Repository aus Medien
So erstellen Sie einen benutzerdefinierten Backtrader-Indikator
So erstellen Sie ein Funktionsobjekt aus einer Zeichenfolge
So erstellen Sie eine JSON-Datei in Python
So schreiben Sie einen ShellScript Bash für Anweisung
[Hinweis] So erstellen Sie eine Ruby-Entwicklungsumgebung
So erstellen Sie ein 1-zeiliges Kivy-Eingabefeld
Verfahren zur Erstellung plattformübergreifender Apps mit kivy
So erstellen Sie eine Rest-API in Django
[Go] So schreiben oder rufen Sie eine Funktion auf
[Hinweis] So erstellen Sie eine Mac-Entwicklungsumgebung
Was ich durch die Implementierung des Erstellens einer Standardbox für SSD gelernt habe
Lesen Sie die Python-Markdown-Quelle: So erstellen Sie einen Parser
Erstellen Sie einen Datensatz mit Bildern, die für das Training verwendet werden sollen
So erstellen Sie ein Untermenü mit dem Plug-In [Blender]
So stellen Sie eine Go-Anwendung auf einer ECS-Instanz bereit
So erstellen Sie eine Entwicklungsumgebung für TensorFlow (1.0.0) (Mac)
So erstellen Sie ein einfaches TCP-Server / Client-Skript
[Python] So erstellen Sie mit Matplotlib ein zweidimensionales Histogramm
So rufen Sie eine Funktion auf
Wie man ein Terminal hackt
So definieren Sie Go-Variablen
[Go] Verwendung von "... (3 Perioden)"
[Go language] Versuchen Sie, einen nutzlos zählbaren Zähler mit mehreren Threads zu erstellen
So verwalten Sie eine README-Datei für Github und PyPI
So erstellen Sie mit snappyHexMesh ein Flussnetz um einen Zylinder
So definieren Sie mehrere Variablen in einer Python for-Anweisung
Wie erstelle ich ein Python-Paket (geschrieben für Praktikanten)
[Python Kivy] So erstellen Sie ein einfaches Popup-Fenster
So ersetzen Sie eine Teilübereinstimmung durch einen numerischen Wert (Anmerkung 1)
So schreiben Sie einen Test für die Verarbeitung mit BigQuery
Ich habe versucht, einen Bot für die Ankündigung eines Wiire-Ereignisses zu erstellen
Ich möchte vorerst eine Docker-Datei erstellen.
So richten Sie WSL2 unter Windows 10 ein und erstellen eine Lernumgebung für Linux-Befehle
Wie erstelle ich eine japanisch-englische Übersetzung?
Übersicht über das Erstellen eines Server-Sockets und das Einrichten eines Client-Sockets
So setzen Sie einen symbolischen Link
Schritte zum Erstellen eines Django-Projekts
Wie man einen lockeren Bot macht
Wie erstelle ich einen Crawler?
So passen Sie U-Boot mit einer benutzerdefinierten Karte für OSD335X an (Hinweis)
So erstellen Sie eine rekursive Funktion
[Los] Erstellen Sie einen CLI-Befehl, um die Erweiterung des Bildes zu ändern