Menu
Coddy logo textTech

Opakowywanie błędów (fmt.Errorf)

Część sekcji Programowanie obiektowe ścieżki GO w Coddy. Lekcja 54 z 107.

Gdy błędy przechodzą przez wiele warstw aplikacji, kluczowe staje się ustalenie, gdzie powstał błąd. Funkcja fmt.Errorf języka Go z czasownikiem %w pozwala opakowywać błędy, zachowując oryginalny błąd i dodając kontekst.

Opakowywanie błędów tworzy łańcuch błędów. Każda warstwa może dodać informacje o tym, co próbowała zrobić, gdy wystąpił błąd:

func ReadConfig(filename string) error {
    data, err := os.ReadFile(filename)
    if err != nil {
        return fmt.Errorf("reading config file: %w", err)
    }
    // przetwórz dane...
    return nil
}

Znacznik formatujący %w jest wyjątkowy. Opakowuje pierwotny błąd w nowy. Różni się od %v, który po prostu zamienia błąd na ciąg znaków i powoduje utratę jego pierwotnej tożsamości.

func LoadSettings() error {
    err := ReadConfig("settings.json")
    if err != nil {
        return fmt.Errorf("loading settings: %w", err)
    }
    return nil
}

Gdy LoadSettings kończy się niepowodzeniem, komunikat o błędzie pokazuje pełny łańcuch: "loading settings: reading config file: open settings.json: no such file or directory". Każda warstwa dodaje kontekst, co znacznie ułatwia debugowanie.

Opakowany błąd nadal zawiera w sobie pierwotny błąd. W następnej lekcji dowiesz się, jak rozpakować te błędy i sprawdzić, co zawierają, za pomocą errors.Is() i errors.As().

challenge icon

Wyzwanie

Łatwy

Zbudujmy system przetwarzania zamówień, który pokazuje opakowywanie błędów na wielu poziomach. Utworzysz łańcuch funkcji, w którym każda warstwa dodaje kontekst do błędów, dzięki czemu łatwo prześledzić, gdzie dokładnie występują problemy.

Rozmieścisz kod w dwóch plikach:

  • orders.go: Utwórz logikę przetwarzania zamówień z wieloma warstwami, które opakowują błędy w miarę ich propagowania w górę.

    Zaimplementuj trzy funkcje tworzące łańcuch przetwarzania:

    • ValidateItem(itemID string) error — Zwraca błąd z komunikatem "item not found", jeśli itemID ma wartość "INVALID", w przeciwnym razie zwraca nil
    • ProcessOrder(orderID, itemID string) error — Wywołuje ValidateItem. Jeśli funkcja zwróci błąd, opakuj go kontekstem: "processing order [orderID]: %w". W przeciwnym razie zwróć nil
    • SubmitOrder(customerName, orderID, itemID string) error — Wywołuje ProcessOrder. Jeśli funkcja zwróci błąd, opakuj go kontekstem: "submitting order for [customerName]: %w". W przeciwnym razie zwróć nil

    Każda warstwa powinna używać fmt.Errorf z czasownikiem %w, aby opakować błąd z niższej warstwy, tworząc łańcuch kontekstu.

  • main.go: Odczytaj szczegóły zamówienia z danych wejściowych, wywołaj SubmitOrder i wyświetl wynik. Jeśli wystąpi błąd, wypisz pełny łańcuch błędów. Jeśli operacja się powiedzie, wypisz komunikat potwierdzający.

Zostaną podane następujące dane wejściowe:

  • Wiersz 1: Imię i nazwisko klienta
  • Wiersz 2: ID zamówienia
  • Wiersz 3: ID produktu

Wypisz wynik:

  • Jeśli wystąpi błąd: Error: [full error chain]
  • Jeśli operacja się powiedzie: Order [orderID] submitted successfully for [customerName]

Na przykład dla wartości Alice, ORD-123 i INVALID wynik powinien wyglądać następująco:

Error: submitting order for Alice: processing order ORD-123: item not found

Zwróć uwagę, że komunikat o błędzie pokazuje cały łańcuch. Możesz prześledzić problem od próby złożenia zamówienia na najwyższym poziomie, przez przetwarzanie zamówienia, aż po właściwy błąd walidacji. Każda warstwa dodała własny kontekst za pomocą %w.

A dla wartości Bob, ORD-456 i ITEM-001 wynik powinien wyglądać następująco:

Order ORD-456 submitted successfully for Bob

Spróbuj swoich sił

package main

import (
	"bufio"
	"fmt"
	"os"
)

func main() {
	scanner := bufio.NewScanner(os.Stdin)
	
	// Wczytaj nazwę klienta
	scanner.Scan()
	customerName := scanner.Text()
	
	// Wczytaj identyfikator zamówienia
	scanner.Scan()
	orderID := scanner.Text()
	
	// Wczytaj identyfikator produktu
	scanner.Scan()
	itemID := scanner.Text()
	
	// TODO: Wywołaj SubmitOrder z wczytanymi wartościami
	// TODO: Jeśli wystąpi błąd, wypisz: Error: [pełny łańcuch błędów]
	// TODO: Jeśli operacja się powiedzie, wypisz: Order [orderID] submitted successfully for [customerName]
	
	_ = customerName
	_ = orderID
	_ = itemID
}
quiz iconSprawdź się

Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.

Wszystkie lekcje w sekcji Programowanie obiektowe

Poćwicz samodzielnie: Kompilator Go online