Errorx - Bibliothek zum Arbeiten mit Fehlern in Go

Was ist Errorx und wie ist es nützlich?


Errorx ist eine Bibliothek zur Behandlung von Fehlern in Go. Es bietet Tools zum Lösen von Problemen im Zusammenhang mit dem Fehlermechanismus in großen Projekten sowie eine einzige Syntax für die Arbeit mit diesen.


Bild


Die meisten Joom-Serverkomponenten wurden seit Gründung des Unternehmens auf Go geschrieben. Diese Entscheidung hat sich in der Anfangsphase der Entwicklung und der Lebensdauer des Dienstes ausgezahlt. Angesichts der Ankündigungen zu den Aussichten für Go 2 sind wir sicher, dass wir dies in Zukunft nicht bereuen werden. Eine der Haupttugenden von Go ist die Einfachheit, und die Herangehensweise an Fehler zeigt dieses Prinzip wie nichts anderes. Nicht jedes Projekt erreicht einen ausreichenden Umfang, sodass die Funktionen der Standardbibliothek nicht ausreichen, sodass Sie nach eigenen Lösungen in diesem Bereich suchen müssen. Wir haben zufällig einige Entwicklungen in der Herangehensweise an die Arbeit mit Fehlern durchlaufen, und die errorx-Bibliothek spiegelt das Ergebnis dieser Entwicklung wider. Wir sind davon überzeugt, dass dies für viele Menschen nützlich sein kann, auch für diejenigen, die sich noch nicht sehr unwohl fühlen, wenn sie mit Fehlern an ihren Projekten arbeiten.


Fehler in Go


Bevor wir mit der Geschichte über errorx fortfahren, sollten einige Klarstellungen vorgenommen werden. Was ist am Ende mit den Bugs los?


type error interface { Error() string } 

Sehr einfach, oder? In der Praxis enthält eine Implementierung häufig nur eine Zeichenfolgenbeschreibung des Fehlers. Ein solcher Minimalismus hängt mit dem Ansatz zusammen, wonach ein Fehler nicht unbedingt etwas "Außergewöhnliches" bedeutet. Die am häufigsten verwendeten Fehler. New () aus der Standardbibliothek entspricht dieser Idee:


 func New(text string) error { return &errorString{text} } 

Wenn wir uns daran erinnern, dass Fehler in einer Sprache keinen besonderen Status haben und gewöhnliche Objekte sind, stellt sich die Frage: Was ist die Besonderheit der Arbeit mit ihnen?


Fehler sind keine Ausnahme . Es ist kein Geheimnis, dass viele, wenn sie Go kennenlernen, diesem Unterschied mit einigem Widerstand begegnen. Es gibt viele Veröffentlichungen, die den in Go gewählten Ansatz erklären, unterstützen und kritisieren. Auf die eine oder andere Weise dienen Fehler in Go vielen Zwecken, und mindestens einer davon entspricht genau den Ausnahmen in einigen anderen Sprachen: Fehlerbehebung. Infolgedessen ist es natürlich, von ihnen dieselbe Ausdruckskraft zu erwarten, auch wenn der Ansatz und die Syntax, die mit ihrer Verwendung verbunden sind, sehr unterschiedlich sind.


Was ist los?


Viele Projekte nutzen die Fehler in Go so wie sie sind und haben nicht die geringste Schwierigkeit. Mit zunehmender Komplexität des Systems treten jedoch eine Reihe von Problemen auf, die auch ohne hohe Erwartungen Aufmerksamkeit erregen. Eine gute Illustration ist eine ähnliche Zeile im Protokoll Ihres Dienstes:


Error: duplicate key


Hier wird das erste Problem sofort offensichtlich: Wenn Sie sich nicht absichtlich darum kümmern, ist es in einem irgendwie großen System fast unmöglich zu verstehen, was schief gelaufen ist, nur anhand der ersten Meldung. In diesem Beitrag fehlen Details und ein breiterer Kontext des Problems. Dies ist ein Fehler eines Programmierers, aber es kommt zu oft vor, um ihn zu vernachlässigen. Der Code, der den "positiven" Zweigen des Kontrollgraphen gewidmet ist, verdient in der Praxis immer mehr Aufmerksamkeit und wird durch Tests besser abgedeckt als der "negative" Code, der mit einer Unterbrechung der Ausführung oder externen Problemen verbunden ist. Wie oft das if err != nil {return err} Mantra if err != nil {return err} in Go-Programmen wiederholt wird, macht dieses Versehen noch wahrscheinlicher.


Betrachten Sie als kleinen Exkurs dieses Beispiel:


 func (m *Manager) ApplyToUsers(action func(User) (*Data, error), ids []UserID) error { users, err := m.LoadUsers(ids) if err != nil { return err } var actionData []*Data for _, user := range users { data, err := action(user) if err != nil { return err } ok, err := m.validateData(data) if err != nil { return nil } if !ok { log.Error("Validation failed for %v", data) continue } actionData = append(actionData, data) } return m.Apply(actionData) } 

Wie schnell haben Sie den Fehler in diesem Code gesehen? Aber es wurde mindestens einmal gemacht, wahrscheinlich von jedem Go-Programmierer. Hinweis: Fehler im Ausdruck, if err != nil { return nil } .


Wenn wir mit einer verschwommenen Nachricht im Protokoll zum Problem zurückkehren, ist in dieser Situation natürlich auch jeder passiert. Es ist sehr unangenehm, den Fehlerbehandlungscode bereits zu dem Zeitpunkt zu beheben, zu dem das Problem auftritt. Darüber hinaus ist gemäß den anfänglichen Daten aus dem Protokoll völlig unklar, auf welcher Seite mit der Suche nach dem Teil des Codes begonnen werden soll, der tatsächlich verbessert werden muss. Dies scheint eine weit hergeholte Komplexität für Projekte zu sein, deren Code und Anzahl der externen Abhängigkeiten gering sind. Bei Großprojekten ist dies jedoch ein völlig reales und schmerzhaftes Problem.


Angenommen, ein Programmierer mit bitterer Erfahrung möchte dem zurückgegebenen Fehler im Voraus einen Kontext hinzufügen. Der naive Weg, dies zu tun, ist ungefähr so:


 func InsertUser(u *User) error { err := usersTable.Insert(u) if err != nil { return errors.New(fmt.Sprintf("failed to insert user %s: %v", u.Name, err) } return nil } 

Es wurde besser. Der weitere Kontext ist noch unklar, aber jetzt ist es viel einfacher, zumindest in welchem ​​Code der Fehler aufgetreten ist. Nachdem wir jedoch ein Problem gelöst haben, haben wir versehentlich ein anderes erstellt. Durch den hier erstellten Fehler blieb die Diagnosemeldung original, aber alles andere, einschließlich des Typs und des zusätzlichen Inhalts, ging verloren.


Betrachten Sie einen ähnlichen Code im Datenbanktreiber, um festzustellen, warum dies gefährlich ist:


 var ErrDuplicateKey = errors.New("duplicate key") func (t *Table) Insert(entity interface{}) error { // returns ErrDuplicateKey if a unique constraint is violated by insert } func IsDuplicateKeyError(err error) bool { return err == ErrDuplicateKey } 

Jetzt wird die IsDuplicateKeyError() zerstört, obwohl wir zum Zeitpunkt des Hinzufügens unseres Textes zum Fehler nicht die Absicht hatten, seine Semantik zu ändern. Dies wiederum wird den Code beschädigen, der auf dieser Prüfung beruht:


 func RegisterUser(u *User) error { err := InsertUser(u) if db.IsDuplicateKeyError(err) { // find existing user, handle conflict } else { return err } } 

Wenn wir intelligenter vorgehen und unseren eigenen Fehlertyp hinzufügen möchten, der den ursprünglichen Fehler speichert und ihn beispielsweise über die Cause() error kann, lösen wir das Problem auch nur teilweise.


  1. Anstelle der Fehlerverarbeitung müssen Sie nun wissen, dass der wahre Grund in Cause()
  2. Es gibt keine Möglichkeit, externen Bibliotheken dieses Wissen beizubringen, und die darin geschriebenen Hilfsfunktionen bleiben unbrauchbar
  3. Unsere Implementierung kann erwarten, dass Cause() die unmittelbare Fehlerursache zurückgibt (oder null, wenn dies nicht der Fall ist), während die Implementierung in einer anderen Bibliothek erwartet, dass die Methode die nicht null-Grundursache zurückgibt. Das Fehlen von Standardwerkzeugen oder eines allgemein akzeptierten Vertrags birgt sehr unangenehme Überraschungen

Diese Teillösung wird jedoch in vielen Fehlerbibliotheken verwendet, einschließlich in gewissem Umfang auch in unseren. In Go 2 ist geplant, diesen Ansatz bekannt zu machen. In diesem Fall ist es einfacher, die oben beschriebenen Probleme zu lösen.


Fehlerx


Im Folgenden werden wir über die Angebote von errorx sprechen, aber versuchen Sie zunächst, die Überlegungen zu formulieren, die der Bibliothek zugrunde liegen.


  • Diagnose ist wichtiger als Ressourceneinsparung. Die Leistung beim Erstellen und Anzeigen von Fehlern ist wichtig. Trotzdem stellen sie eher einen negativen als einen positiven Pfad dar und dienen in den meisten Fällen als Signal für ein Problem. Daher ist das Vorhandensein von Diagnoseinformationen in einem Fehler noch wichtiger.
  • Standardmäßig Stapelverfolgung. Damit der Fehler mit der Fülle der Diagnose verschwindet, sollten keine Anstrengungen erforderlich sein. Im Gegenteil, gerade um einige der Informationen (aus Gründen der Kürze oder aus Leistungsgründen) auszuschließen, können zusätzliche Maßnahmen erforderlich sein.
  • Semantik von Fehlern. Es sollte eine einfache und zuverlässige Möglichkeit geben, die Bedeutung des Fehlers zu überprüfen: Art, Vielfalt, Eigenschaften.
  • Einfache Zugabe. Das Hinzufügen von Diagnoseinformationen zu einem übergebenen Fehler sollte einfach sein und die Überprüfung seiner Semantik nicht beeinträchtigen.
  • Einfachheit. Code, der Fehlern gewidmet ist, wird häufig und routinemäßig geschrieben, daher sollte die Syntax grundlegender Manipulationen mit ihnen einfach und präzise sein. Dies reduziert die Anzahl der Fehler und erleichtert das Lesen.
  • Weniger ist mehr. Die Verständlichkeit und Einheitlichkeit des Codes ist wichtiger als die optionalen Funktionen und Erweiterungsoptionen (die wahrscheinlich niemand verwenden wird).
  • Die Fehlersemantik ist Teil der API. Fehler, die eine separate Verarbeitung im aufrufenden Code erfordern, sind de facto Teil des öffentlichen API-Pakets. Sie müssen nicht versuchen, es auszublenden oder weniger explizit zu machen, aber Sie können die Verarbeitung bequemer und externe Abhängigkeiten weniger anfällig machen.
  • Die meisten Fehler sind undurchsichtig. Je mehr Arten von Fehlern für einen externen Benutzer nicht voneinander zu unterscheiden sind, desto besser. Das Laden von API-Fehlertypen, die eine spezielle Behandlung erfordern, sowie das Laden der Fehler selbst mit den zur Verarbeitung erforderlichen Daten ist ein Konstruktionsfehler, der vermieden werden sollte.

Die schwierigste Frage für uns war die Erweiterbarkeit: Sollte errorx Grundelemente für die Einführung benutzerdefinierter Fehlertypen bereitstellen, deren Verhalten beliebig unterschiedlich ist, oder gibt es eine Implementierung, mit der Sie alles, was Sie benötigen, aus der Box holen können? Wir haben die zweite Option gewählt. Erstens löst errorx ein sehr praktisches Problem - und unsere Erfahrung mit der Verwendung zeigt, dass es für diesen Zweck besser ist, eine Lösung zu haben, als Ersatzteile für die Erstellung. Zweitens ist die Überlegung zur Einfachheit sehr wichtig: Da Fehler weniger berücksichtigt werden, sollte der Code so gestaltet sein, dass es schwierig ist, mit ihnen zu arbeiten. Die Praxis hat gezeigt, dass es dafür wichtig ist, dass alle diese Codes gleich aussehen und funktionieren.


TL; DR nach Hauptbibliotheksfunktionen:


  • Stack-Trace-Erstellungsorte in allen Fehlern standardmäßig
  • Typprüfungen auf Fehler, verschiedene Sorten
  • Die Möglichkeit, einem vorhandenen Fehler Informationen hinzuzufügen, ohne etwas zu beschädigen
  • Geben Sie die Sichtbarkeitssteuerung ein, wenn Sie den ursprünglichen Grund vor dem Anrufer verbergen möchten
  • Fehlerbehandlungs-Code-Generalisierungsmechanismus (Typhierarchie, Merkmale)
  • Fehleranpassung durch dynamische Eigenschaften
  • Standardfehlertypen
  • Syntax-Dienstprogramme zur Verbesserung der Lesbarkeit von Fehlerbehandlungscode

Einführung


Wenn wir das oben analysierte Beispiel mit errorx überarbeiten, erhalten wir Folgendes:


 var ( DBErrors = errorx.NewNamespace("db") ErrDuplicateKey = DBErrors.NewType("duplicate_key") ) func (t *Table) Insert(entity interface{}) error { // ... return ErrDuplicateKey.New("violated constraint %s", details) } func IsDuplicateKeyError(err error) bool { return errorx.IsOfType(err, ErrDuplicateKey) } 

 func InsertUser(u *User) error { err := usersTable.Insert(u) if err != nil { return errorx.Decorate(err, "failed to insert user %s", u.Name) } return nil } 

Der IsDuplicateKeyError() mit IsDuplicateKeyError() ändert sich nicht.


Was hat sich in diesem Beispiel geändert?


  • ErrDuplicateKey wurde zu einem Typ, nicht zu einer Fehlerinstanz. Die Überprüfung ist resistent gegen Kopierfehler, es besteht keine fragile Abhängigkeit von der exakten Gleichheit
  • Es gibt einen Namespace für Datenbankfehler. Es wird höchstwahrscheinlich andere Fehler geben, und eine solche Gruppierung ist für die Lesbarkeit nützlich und kann in einigen Fällen im Code verwendet werden
  • Einfügen gibt für jeden Aufruf einen neuen Fehler zurück:
    • Der Fehler enthält weitere Details. Dies ist natürlich ohne errorx möglich, aber es ist unmöglich, wenn jedes Mal dieselbe Fehlerinstanz zurückgegeben wird, die zuvor für IsDuplicateKeyError() erforderlich war.
    • Diese Fehler können eine andere Stapelverfolgung enthalten, was nützlich ist, weil Nicht für alle Aufrufe der Einfügefunktion ist diese Situation akzeptabel
  • InsertUser() ergänzt den Fehlertext, wendet jedoch den ursprünglichen Fehler an, der für nachfolgende Vorgänge vollständig beibehalten wird
  • IsDuplicateKeyError() funktioniert jetzt: Es kann weder durch Kopieren des Fehlers noch durch beliebig viele Ebenen mit Decorate () verwöhnt werden.

Es ist nicht notwendig, immer nur einem solchen Schema zu folgen:


  • Die Art des Fehlers ist bei weitem nicht immer eindeutig: Die gleichen Typen können an vielen Stellen verwendet werden
  • Falls gewünscht, kann die Stack-Trace-Sammlung deaktiviert werden, und Sie können nicht jedes Mal einen neuen Fehler erstellen, sondern denselben wie im ursprünglichen Beispiel zurückgeben. Hierbei handelt es sich um sogenannte Sentinel-Fehler, deren Verwendung wir nicht empfehlen. Dies kann jedoch hilfreich sein, wenn der Fehler nur als Markierung im Code verwendet wird und Sie beim Erstellen von Objekten sparen möchten
  • Es gibt eine Möglichkeit, die errorx.IsOfType(err, ErrDuplicateKey) beenden, wenn Sie die Semantik der Grundursache vor neugierigen Blicken verbergen möchten
  • Neben dem Vergleich mit dem genauen Typ gibt es noch andere Möglichkeiten, die Typprüfung selbst durchzuführen

Godoc enthält detaillierte Informationen zu all dem. Im Folgenden werden die Hauptmerkmale, die für die tägliche Arbeit ausreichen, etwas näher erläutert.


Typen


Jeder errorx-Fehler gehört zu einem Typ. Typ ist wichtig, weil geerbte Fehlereigenschaften können durchlaufen werden; Durch ihn oder seine Eigenschaften werden bei Bedarf Semantikprüfungen durchgeführt. Darüber hinaus ergänzt der aussagekräftige Name des Typs die Fehlermeldung und kann sie in einigen Fällen ersetzen.


 AuthErrors = errorx.NewNamespace("auth") ErrInvalidToken = AuthErrors.NewType("invalid_token") 

 return ErrInvalidToken.NewWithNoMessage() 

Die Fehlermeldung enthält auth.invalid_token . Die Fehlerdeklaration könnte anders aussehen:


 ErrInvalidToken = AuthErrors.NewType("invalid_token").ApplyModifiers(errorx.TypeModifierOmitStackTrace) 

In dieser Ausführungsform ist unter Verwendung des Typmodifikators die Stapelverfolgungssammlung deaktiviert. Der Fehler hat eine Markersemantik: Sein Typ wird an den externen Benutzer des Dienstes übergeben, und ein Aufrufstapel in den Protokollen wäre nicht sinnvoll, da Dies ist kein zu reparierendes Problem.


Hier können wir den Vorbehalt machen, dass Fehler in mehreren Aspekten dualer Natur sind. Der Inhalt des Fehlers wird sowohl für die Diagnose als auch manchmal als Information für einen externen Benutzer verwendet: API-Client, Bibliotheksbenutzer usw. Fehler werden im Code sowohl als Mittel zur Vermittlung der Semantik des Geschehens als auch als Mechanismus zur Übertragung der Kontrolle verwendet. Bei der Verwendung von Fehlertypen sollte dies berücksichtigt werden.


Fehlererstellung


 return MyType.New("fail") 

Das Abrufen eines eigenen Typs für jeden Fehler ist völlig optional. Jedes Projekt kann ein eigenes Paket mit allgemeinen Fehlern haben, und einige Sätze werden zusammen mit errorx als Teil des allgemeinen Namespace bereitgestellt. Es enthält Fehler, die in den meisten Fällen keine Verarbeitung im Code beinhalten und für „Ausnahmesituationen“ geeignet sind, in denen ein Fehler aufgetreten ist.


 return errorx.IllegalArgument.New("negative value %d", value) 

In einem typischen Fall wird eine Aufrufkette so konzipiert, dass am Ende der Kette ein Fehler erstellt und am Anfang verarbeitet wird. In Go wird es nicht ohne Grund als schlechte Form angesehen, einen Fehler zweimal zu verarbeiten, d. H. Beispielsweise einen Fehler in das Protokoll zu schreiben und ihn höher im Stapel zurückzugeben. Sie können dem Fehler jedoch selbst Informationen hinzufügen, bevor Sie ihn weitergeben:


 return errorx.Decorate(err, "failed to upload '%s' to '%s'", filename, location) 

Der dem Fehler hinzugefügte Text wird im Protokoll angezeigt, es schadet jedoch nicht, den Typ des ursprünglichen Fehlers zu überprüfen.


Manchmal entsteht das gegenteilige Bedürfnis: Unabhängig von der Art des Fehlers sollte der externe Benutzer des Pakets ihn nicht kennen. Wenn er eine solche Gelegenheit bekommen würde, könnte er eine fragile Abhängigkeit von einem Teil der Implementierung schaffen.


 return service.ErrBadRequest.Wrap(err, "failed to load user data") 

Ein wichtiger Unterschied, der Wrap zur bevorzugten Alternative zu New macht, besteht darin, dass der ursprüngliche Fehler vollständig in den Protokollen wiedergegeben wird. Und insbesondere wird es einen nützlichen ersten Aufrufstapel mit sich bringen.


Ein weiterer nützlicher Trick, mit dem Sie alle möglichen Informationen zum Aufrufstapel speichern können, sieht folgendermaßen aus:


 return errorx.EnhanceStackTrace(err, "operation fail") 

Wenn der ursprüngliche Fehler von einer anderen Goroutine stammt, enthält das Ergebnis eines solchen Aufrufs eine Stapelverfolgung beider Goroutinen, was die Nützlichkeit ungewöhnlich erhöht. Die Notwendigkeit, einen solchen Anruf zu tätigen, ist eindeutig auf Leistungsprobleme zurückzuführen: Dieser Fall ist relativ selten, und eine Ergonomie, die ihn selbst erkennt, würde den üblichen Wrap verlangsamen, wenn er überhaupt nicht erforderlich ist.


Godoc enthält weitere Informationen und beschreibt zusätzliche Funktionen wie DecorateMany.


Fehlerbehandlung


Am besten, wenn die Fehlerbehandlung auf Folgendes zurückzuführen ist:


 log.Error("Error: %+v", err) 

Je weniger Fehler Sie machen müssen, außer um sie in das Protokoll auf der Systemschicht des Projekts zu drucken, desto besser. In Wirklichkeit reicht dies manchmal nicht aus, und Sie müssen dies tun:


 if errorx.IsOfType(err, MyType) { /* handle */ } 

Diese Prüfung ist sowohl bei einem Fehler vom Typ MyType als auch bei seinen MyType Typen erfolgreich und ist gegen errorx.Decorate() resistent. Hier besteht jedoch eine direkte Abhängigkeit von der Art des Fehlers, was innerhalb des Pakets ganz normal ist, aber bei Verwendung außerhalb des Pakets unangenehm sein kann. In einigen Fällen ist der Typ eines solchen Fehlers Teil einer stabilen externen API, und manchmal möchten wir diese Prüfung durch eine Eigenschaftsprüfung ersetzen und nicht durch die genaue Art des Fehlers.


Bei klassischen Go-Fehlern würde dies über eine Schnittstelle erfolgen, deren Typ als Indikator für die Art des Fehlers dient. Errorx-Typen unterstützen diese Erweiterung nicht, Sie können jedoch stattdessen den Trait Mechanismus verwenden. Zum Beispiel:


 func IsTemporary(err error) bool { return HasTrait(err, Temporary()) } 

Diese in errorx integrierte Funktion prüft, ob der Fehler die Standardeigenschaft Temporary , d. H. ob es vorübergehend ist. Das Markieren von Fehlertypen mit Merkmalen liegt in der Verantwortung der Fehlerquelle und kann durch diese ein nützliches Signal übertragen, ohne dass bestimmte interne Typen Teil der externen API sind.


 return errorx.IgnoreWithTrait(err, errorx.NotFound()) 

Diese Syntax ist nützlich, wenn eine bestimmte Art von Fehler erforderlich ist, um den Steuerungsfluss zu unterbrechen, sie sollte jedoch nicht an die aufrufende Funktion übergeben werden.


Trotz der Fülle an Verarbeitungswerkzeugen, von denen hier nicht alle aufgeführt sind, ist es wichtig zu bedenken, dass das Arbeiten mit Fehlern so einfach wie möglich bleiben sollte. Ein Beispiel für die Regeln, die wir einhalten wollen:


  • Code, der einen Fehler empfängt, sollte ihn immer vollständig protokollieren. Wenn ein Teil der Informationen überflüssig ist, lassen Sie den Code, der den Fehler erzeugt, dies erledigen
  • Sie sollten niemals den Fehlertext oder das Ergebnis der Funktion Error() verwenden, um ihn im Code zu verarbeiten. Hierfür sind nur Typ- / Merkmalsprüfungen geeignet oder bei Nicht-Errorx-Fehlern eine Typzusicherung
  • Der Benutzercode sollte nicht beschädigt werden, da ein Fehler nicht auf besondere Weise verarbeitet wird, auch wenn eine solche Verarbeitung möglich ist und zusätzliche Funktionen bietet
  • Fehler, die von Eigenschaften überprüft werden, sind besser als sogenannte Sentinel-Fehler, weil solche Kontrollen sind weniger fragil

Außerhalb errorx


Hier haben wir beschrieben, was dem Bibliotheksbenutzer sofort zur Verfügung steht, aber in Joom ist die Penetration von fehlerbezogenem Code sehr groß. Das Protokollierungsmodul akzeptiert explizit Fehler in seiner Signatur und druckt sich selbst aus, um die Möglichkeit einer falschen Formatierung auszuschließen und optional verfügbare Kontextinformationen aus der Fehlerkette zu extrahieren. Das Modul, das für die paniksichere Arbeit mit Goroutins verantwortlich ist, entpackt den Fehler, wenn er mit einer Panik auftritt, und weiß auch, wie man Panik mithilfe der Fehlersyntax darstellt, ohne den ursprünglichen Stack-Trace zu verlieren. Einige davon werden wir vielleicht auch veröffentlichen.


Kompatibilitätsprobleme


Trotz der Tatsache, dass wir sehr zufrieden sind, wie errorx es uns ermöglicht, mit Fehlern zu arbeiten, ist die Situation mit dem diesem Thema gewidmeten Bibliothekscode alles andere als ideal. Wir bei Joom lösen ganz bestimmte praktische Probleme mit errorx, aber aus Sicht des Go-Ökosystems wäre es vorzuziehen, diesen gesamten Satz von Werkzeugen in der Standardbibliothek zu haben. Der Fehler, dessen Quelle tatsächlich oder möglicherweise zu einem anderen Paradigma gehört, muss als fremd angesehen werden, d. H. möglicherweise keine Informationen in der Form enthalten, die im Projekt akzeptiert wird.


Es wurden jedoch einige Maßnahmen ergriffen, um nicht mit anderen vorhandenen Lösungen in Konflikt zu geraten.


Das Format '%+v' verwendet, um einen Fehler zusammen mit der Stapelverfolgung zu drucken, falls vorhanden. Dies ist der De-facto-Standard im Go-Ökosystem und sogar im Entwurfsentwurf für Go 2 enthalten.


Cause() error errorx , , , Causer, errorx Wrap().



, Go 2, . .


, errorx Go 1. , Go 2, . , , errorx.


Check-handle , errorx , a Unwrap() error Wrap() errorx (.. , , Wrap ), . , , .


design draft Go 2, errorx.Is() errorx.As() , errors .


Fazit


, , , - , . , API : , , . 1.0 , Joom. , - .


: https://github.com/joomcode/errorx


, !


Bild

Source: https://habr.com/ru/post/de429690/


All Articles