Error-Wrapping (fmt.Errorf)
Teil des Abschnitts Objektorientierte Programmierung der GO-Journey von Coddy. Lektion 54 von 107.
Wenn Fehler mehrere Ebenen deiner Anwendung durchlaufen, wird es entscheidend zu wissen, wo ein Fehler entstanden ist. Die Go-Funktion fmt.Errorf mit dem Verb %w ermöglicht es dir, Fehler zu verpacken, wobei der ursprüngliche Fehler erhalten bleibt und gleichzeitig Kontext hinzugefügt wird.
Das Wrapping von Fehlern erstellt eine Fehlerkette. Jede Ebene kann Informationen darüber hinzufügen, was sie beim Auftreten des Fehlers zu tun versucht hat:
func ReadConfig(filename string) error {
data, err := os.ReadFile(filename)
if err != nil {
return fmt.Errorf("reading config file: %w", err)
}
// Daten verarbeiten...
return nil
}Das Verb %w ist besonders. Es schließt den ursprünglichen Fehler in den neuen ein. Das unterscheidet sich von %v, das den Fehler lediglich in eine Zeichenfolge umwandelt und die Identität des ursprünglichen Fehlers verliert.
func LoadSettings() error {
err := ReadConfig("settings.json")
if err != nil {
return fmt.Errorf("loading settings: %w", err)
}
return nil
}Wenn LoadSettings fehlschlägt, zeigt die Fehlermeldung die vollständige Kette: "loading settings: reading config file: open settings.json: no such file or directory". Jede Ebene fügt Kontext hinzu, wodurch das Debuggen wesentlich einfacher wird.
Der verpackte Fehler enthält weiterhin den ursprünglichen Fehler. In der nächsten Lektion lernst du, wie du diese Fehler auspackst und mithilfe von errors.Is() und errors.As() überprüfst, was sich darin befindet.
Aufgabe
EinfachErstellen wir ein Auftragsverarbeitungssystem, das das Wrapping von Fehlern über mehrere Ebenen demonstriert. Du erstellst eine Kette von Funktionen, wobei jede Ebene Fehler mit zusätzlichem Kontext versieht, sodass sich genau nachvollziehen lässt, wo Probleme auftreten.
Du organisierst deinen Code über zwei Dateien:
orders.go: Erstelle die Logik zur Auftragsverarbeitung mit mehreren Ebenen, die Fehler beim Weiterreichen nach oben wrappen.Implementiere drei Funktionen, die eine Verarbeitungskette bilden:
ValidateItem(itemID string) error– Gibt einen Fehler mit der Meldung"item not found"zurück, wenn die itemID"INVALID"ist, andernfallsnilProcessOrder(orderID, itemID string) error– RuftValidateItemauf. Wenn ein Fehler zurückgegeben wird, wrappe ihn mit dem Kontext:"processing order [orderID]: %w". Andernfalls gibnilzurückSubmitOrder(customerName, orderID, itemID string) error– RuftProcessOrderauf. Wenn ein Fehler zurückgegeben wird, wrappe ihn mit dem Kontext:"submitting order for [customerName]: %w". Andernfalls gibnilzurück
Jede Ebene sollte
fmt.Errorfmit dem Verb%wverwenden, um den Fehler der darunterliegenden Ebene zu wrappen und so eine Kontextkette aufzubauen.main.go: Lies die Auftragsdetails aus der Eingabe, rufeSubmitOrderauf und zeige das Ergebnis an. Wenn ein Fehler auftritt, gib die vollständige Fehlerkette aus. Bei Erfolg gib eine Bestätigungsnachricht aus.
Die folgenden Eingaben werden bereitgestellt:
- Zeile 1: Kundenname
- Zeile 2: Auftrags-ID
- Zeile 3: Artikel-ID
Gib das Ergebnis aus:
- Wenn ein Fehler auftritt:
Error: [full error chain] - Bei Erfolg:
Order [orderID] submitted successfully for [customerName]
Wenn beispielsweise Alice, ORD-123 und INVALID angegeben werden, sollte deine Ausgabe wie folgt aussehen:
Error: submitting order for Alice: processing order ORD-123: item not foundBeachte, wie die Fehlermeldung die vollständige Kette zeigt. Du kannst das Problem vom Versuch der Übermittlung auf der obersten Ebene über die Auftragsverarbeitung bis hin zum eigentlichen Validierungsfehler zurückverfolgen. Jede Ebene hat mithilfe von %w ihren eigenen Kontext hinzugefügt.
Wenn hingegen Bob, ORD-456 und ITEM-001 angegeben werden, sollte deine Ausgabe wie folgt aussehen:
Order ORD-456 submitted successfully for BobProbier es selbst
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
scanner := bufio.NewScanner(os.Stdin)
// Kundenname lesen
scanner.Scan()
customerName := scanner.Text()
// Bestell-ID lesen
scanner.Scan()
orderID := scanner.Text()
// Artikel-ID lesen
scanner.Scan()
itemID := scanner.Text()
// TODO: SubmitOrder mit den Eingabewerten aufrufen
// TODO: Wenn ein Fehler auftritt, ausgeben: Error: [full error chain]
// TODO: If successful, print: Order [orderID] submitted successfully for [customerName]
_ = customerName
_ = orderID
_ = itemID
}
Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Objektorientierte Programmierung
1Grundlagen der OOP in Go
Externe DateienGo-Workspace & ModulePackages & ImportsExportierte vs. nicht exportierte NamenEinführung in OOP mit GoStructs als KlassenMethoden für Structs definierenPointer- vs. Value-ReceiverStruct-InitialisierungKonstruktorfunktionenRückblick – Einfacher Taschenrechner4Schnittstellen
Einführung in SchnittstellenImplizite ImplementierungSchnittstelle als VertragLeere Schnittstelle (any)TypzusicherungTypwechselZusammensetzung von SchnittstellenStringer- und Error-SchnittstellenRückblick – Formenrechner7Kapselung
Exportierte vs. nicht exportierte FelderKapselung auf PaketebeneGetter- und Setter-MethodenInformationsverbergung in GoRückblick – Studierendendatensätze10Generics (Go 1.18+)
Einführung in GenericsTypparameterTypbeschränkungenGenerische StrukturenWorkaround für generische MethodenZusammenfassung – Generische Sammlung2Typen & Structs im Detail
Grundlegende & zusammengesetzte TypenBenutzerdefinierte TypdefinitionenStruct-TagsAnonyme StructsVerschachtelte StructsNullwerte & StandardwerteRückblick – Kontaktbuch5Komposition statt Vererbung
Warum Go keine Vererbung hatGrundlagen der Struct-EinbettungMethoden-PromotionMehrere Structs einbettenEinbettung vs. AggregationVerbergen eingebetteter MethodenRückblick – Mitarbeiterhierarchie8Fehlerbehandlung & OOP
Das Error-InterfaceBenutzerdefinierte FehlertypenError-Wrapping (fmt.Errorf)Sentinel-Fehlererrors.Is() und errors.As()Panic, Defer und RecoverRückblick – Dateiparser3Zeiger & Speicher
Grundlagen von Zeigern in GoZeiger auf StructsÜbergabe per Wert vs. ReferenzDie Funktion new()Garbage Collection in GoRückblick – Verkettete Liste erstellen6Polymorphismus in Go
Polymorphismus über InterfacesDuck-Typing in GoRegeln zur Interface-ErfüllungPolymorphe SammlungenDependency InjectionZusammenfassung – Zahlungsprozessor9Konkurrenz & OOP
Grundlagen der GoroutinesChannels & KommunikationGepufferte vs. ungepufferte ChannelsSelect-Anweisungsync.Mutex & sync.RWMutexsync.WaitGroupThread-sicheres Struct-DesignRückblick – Worker PoolÜbe selbstständig: Online-Go-Compiler