Menu
Coddy logo textTech

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.

challenge icon

Aufgabe

Einfach

Erstellen 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, andernfalls nil
    • ProcessOrder(orderID, itemID string) error – Ruft ValidateItem auf. Wenn ein Fehler zurückgegeben wird, wrappe ihn mit dem Kontext: "processing order [orderID]: %w". Andernfalls gib nil zurück
    • SubmitOrder(customerName, orderID, itemID string) error – Ruft ProcessOrder auf. Wenn ein Fehler zurückgegeben wird, wrappe ihn mit dem Kontext: "submitting order for [customerName]: %w". Andernfalls gib nil zurück

    Jede Ebene sollte fmt.Errorf mit dem Verb %w verwenden, um den Fehler der darunterliegenden Ebene zu wrappen und so eine Kontextkette aufzubauen.

  • main.go: Lies die Auftragsdetails aus der Eingabe, rufe SubmitOrder auf 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 found

Beachte, 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 Bob

Probier 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
}
quiz iconTeste dich selbst

Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.

Alle Lektionen in Objektorientierte Programmierung

Übe selbstständig: Online-Go-Compiler