Informationen zur Fehlerbehandlung in Go

Abgeschlossen

Während Sie Ihre Programme schreiben, müssen Sie die verschiedenen Möglichkeiten der Programmfehler in Erwägung ziehen, und Sie müssen Fehler verwalten. Die Benutzer sollten keine langen und verwirrenden Stapelüberwachungsfehler sehen. Es ist besser, wenn sie sinnvolle Informationen über den Fehler erhalten. Wie Sie gesehen haben, verfügt Go über integrierte Funktionen wie panic und recover, um in den Programmen Ausnahmen oder unerwartetes Verhalten zu verwalten. Fehler sind jedoch bekannte Fehler, die die Programme behandeln können sollten.

Bei der Vorgehensweise von Go zur Fehlerbehandlung handelt es sich einfach um einen Ablaufsteuerungsmechanismus, bei dem nur eine if- und return-Anweisung benötigt wird. Wenn Sie z. B. eine Funktion zum Abrufen von Informationen aus einem employee-Objekt aufrufen, möchten Sie möglicherweisen wissen, ob der Mitarbeiter vorhanden ist. Die vordefinierte Vorgehensweise von Go zur Behandlung eines solchen erwarteten Fehlers würde wie folgt aussehen:

employee, err := getInformation(1000)
if err != nil {
    // Something is wrong. Do something.
}

Beachten Sie, wie die getInformation-Funktion die employee-Struktur und auch einen Fehler als zweiten Wert zurückgibt. Der Fehler könnte nil lauten. Wenn der Fehler nil lautet, bedeutet dies einen Erfolg. Ein Fehler tritt auf, wenn es nicht nil lautet. Bei einem Nicht-nil-Fehler wird eine Fehlermeldung angezeigt, die Sie entweder ausdrucken oder vorzugsweise protokollieren können. Auf diese Weise behandeln Sie Fehler in Go. Im nächsten Abschnitt werden einige andere Strategien aufgezeigt.

Wahrscheinlich bemerken Sie, dass die Fehlerbehandlung in Go mehr Aufmerksamkeit beim Melden und Behandeln eines Fehlers erfordert. Genau darum geht es. Sehen wir uns einige andere Beispiele an, die Ihnen helfen, die Vorgehensweise von Go bei der Fehlerbehandlung besser zu verstehen.

Wir verwenden den Codeausschnitt, den wir für Strukturen verwendet haben, um die unterschiedlichen Fehlerbehandlungsstrategien zu üben:

package main

import (
    "fmt"
    "os"
)

type Employee struct {
    ID        int
    FirstName string
    LastName  string
    Address   string
}

func main() {
    employee, err := getInformation(1001)
    if err != nil {
        // Something is wrong. Do something.
    } else {
        fmt.Print(employee)
    }
}

func getInformation(id int) (*Employee, error) {
    employee, err := apiCallEmployee(1000)
    return employee, err
}

func apiCallEmployee(id int) (*Employee, error) {
    employee := Employee{LastName: "Doe", FirstName: "John"}
    return &employee, nil
}

Ab da konzentrieren wir uns auf das Bearbeiten der Funktionen getInformation, apiCallEmployee und main, um darzustellen, wie Fehler behandelt werden.

Fehlerbehandlungsstrategien

Wenn eine Funktion einen Fehler zurückgibt, ist es normalerweise der letzte Rückgabewert. Es liegt in der Verantwortung des Aufrufers, zu überprüfen, ob ein Fehler vorhanden ist, und ihn zu behandeln, wie im vorherigen Abschnitt gezeigt. Eine gängige Strategie besteht darin, dieses Muster weiterhin zu verwenden, um den Fehler in einer Unterroutine weiterzugeben. Beispielsweise kann eine Unterroutine (wie getInformation im vorherigen Beispiel) den Fehler an den Aufrufer zurückgeben, ohne etwas anderes zu tun:

func getInformation(id int) (*Employee, error) {
    employee, err := apiCallEmployee(1000)
    if err != nil {
        return nil, err // Simply return the error to the caller.
    }
    return employee, nil
}

Möglicherweise möchten Sie auch weitere Informationen hinzufügen, bevor Sie den Fehler weitergeben. Zu diesem Zweck können Sie die fmt.Errorf()-Funktion verwenden, die der vorhergehenden Funktion ähnelt, aber es wird ein Fehler zurückgegeben. Beispielsweise können Sie dem Fehler mehr Kontext hinzufügen und den ursprünglichen Fehler wie folgt zurückgeben:

func getInformation(id int) (*Employee, error) {
    employee, err := apiCallEmployee(1000)
    if err != nil {
        return nil, fmt.Errorf("Got an error when getting the employee information: %v", err)
    }
    return employee, nil
}

Eine andere Strategie ist das Ausführen der Wiederholungslogik, wenn die Fehler vorübergehend sind. Beispielsweise können Sie eine Wiederholungsrichtlinie verwenden, um eine Funktion dreimal aufzurufen und zwei Sekunden lang zu warten:

func getInformation(id int) (*Employee, error) {
    for tries := 0; tries < 3; tries++ {
        employee, err := apiCallEmployee(1000)
        if err == nil {
            return employee, nil
        }

        fmt.Println("Server is not responding, retrying ...")
        time.Sleep(time.Second * 2)
    }

    return nil, fmt.Errorf("server has failed to respond to get the employee information")
}

Anstatt Fehler in der Konsole zu drucken, können Sie Fehler protokollieren und alle Implementierungsdetails von Endbenutzern ausblenden. Thema des nächsten Moduls ist die Protokollierung. Sehen wir uns jetzt an, wie Sie benutzerdefinierte Fehler erstellen und verwenden können.

Erstellen wiederverwendbarer Fehler

Manchmal nimmt die Anzahl der Fehlermeldungen zu, und Sie möchten die Reihenfolge beibehalten. Möglicherweise möchten Sie auch eine Bibliothek für häufige Fehlermeldungen erstellen, die Sie wieder verwenden möchten. In Go können Sie die errors.New()-Funktion verwenden, um Fehler zu erstellen und Sie in mehreren Teilen wie folgt wiederzuverwenden:

var ErrNotFound = errors.New("Employee not found!")

func getInformation(id int) (*Employee, error) {
    if id != 1001 {
        return nil, ErrNotFound
    }

    employee := Employee{LastName: "Doe", FirstName: "John"}
    return &employee, nil
}

Der Code für die getInformation-Funktion sieht besser aus, und wenn Sie die Fehlermeldung ändern müssen, führen Sie das nur an einem Ort aus. Beachten Sie außerdem, dass die Konvention bedeutet, dass das Err-Präfix für Fehlervariablen enthalten sein soll.

Wenn Sie zum Schluss eine Fehlervariable haben, können Sie spezifischer sein, wenn Sie einen Fehler in einer Aufruferfunktion behandeln. Mit der errors.Is()-Funktion können Sie den Fehlertyp, den Sie erhalten, wie folgt vergleichen:

employee, err := getInformation(1000)
if errors.Is(err, ErrNotFound) {
    fmt.Printf("NOT FOUND: %v\n", err)
} else {
    fmt.Print(employee)
}

Beachten Sie folgende empfohlenen Vorgehensweisen, wenn Sie Fehler in Go behandeln:

  • Überprüfen Sie immer auf Fehler, auch wenn Sie diese nicht erwarten. Behandeln Sie diese dann ordnungsgemäß, um zu vermeiden, dass Endbenutzern unnötige Informationen angezeigt werden.
  • Fügen Sie ein Präfix an die Fehlermeldung an, damit Sie den Ursprung des Fehlers kennen. Beispielsweise können Sie den Namen des Pakets und der Funktion hinzufügen.
  • Erstellen Sie so viele wiederverwendbare Fehlervariablen wie möglich.
  • Lernen Sie den Unterschied zwischen der Verwendung von zurückgegebenen Fehlern und der Panic-Funktion kennen. Nutzen Sie die Panic-Funktion, wenn Sie keine andere Möglichkeit haben. Wenn eine Abhängigkeit z. B. nicht bereit ist, ist es nicht sinnvoll, das Programm laufen zu lassen (es sei denn, Sie möchten ein Standardverhalten ausführen).
  • Protokollieren Sie Fehler mit so vielen Details wie möglich (im nächsten Abschnitt wird beschrieben, wie Sie das machen), und drucken Sie Fehler aus, die ein Endbenutzer verstehen kann.