глобальный обработчик восстановления для golang http panic

Я хочу создать глобальный обработчик ошибок, чтобы отправить его по электронной почте.

package main

import (
    "github.com/gorilla/mux"
    "log"
    "net/http"
)

func main() {
    rtr := mux.NewRouter()
    rtr.HandleFunc("/", withPanic).Methods("GET")

    http.Handle("/", rtr)
    log.Println("Listening...")

    http.ListenAndServe(":3001", http.DefaultServeMux)
}

func withPanic(w http.ResponseWriter, r *http.Request) {
    panic("somewhere here will be panic, but I don't know where exactly")
}

Как сделать его глобальным. Было бы легко, если бы я знал, где произойдет ошибка

if err != nil {
sendMeMail(err)
}

Но что делать в случаях, когда я не знаю, где именно произойдет ошибка? Поэтому я должен добавить глобальный обработчик recoverish. Но как это сделать точно не знаю.

Обновлять

Я добавил defer recover в начало main, но он никогда не выполняется при запросе http://localhost:3001. Так паника не по электронной почте.

package main

import (
    "errors"
    "fmt"
    "github.com/gorilla/mux"
    "log"
    "net/http"
)

func main() {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("Recovered in f", r)
            // find out exactly what the error was and set err
            var err error
            switch x := r.(type) {
            case string:
                err = errors.New(x)
            case error:
                err = x
            default:
                err = errors.New("Unknown panic")
            }
            if err != nil {
                // sendMeMail(err)
                fmt.Println("sendMeMail")
            }
        }
    }()
    rtr := mux.NewRouter()
    rtr.HandleFunc("/", withPanic).Methods("GET")

    http.Handle("/", rtr)
    log.Println("Listening...")

    http.ListenAndServe(":3001", http.DefaultServeMux)
}

func withPanic(w http.ResponseWriter, r *http.Request) {
    panic("somewhere here will be panic, but I don't know where exactly")
}

person Maxim Yefremov    schedule 26.02.2015    source источник


Ответы (2)


Вы можете обернуть свои обработчики в промежуточное программное обеспечение восстановления.

package main

import (
    "errors"
    "github.com/gorilla/mux"
    "log"
    "net/http"
)

func main() {
    m := mux.NewRouter()
    m.Handle("/", RecoverWrap(http.HandlerFunc(handler))).Methods("GET")

    http.Handle("/", m)
    log.Println("Listening...")

    http.ListenAndServe(":3001", nil)

}

func handler(w http.ResponseWriter, r *http.Request) {
    panic(errors.New("panicing from error"))
}

func RecoverWrap(h http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        defer func() {
            r := recover()
            if r != nil {
                var err error
                switch t := r.(type) {
                case string:
                    err = errors.New(t)
                case error:
                    err = t
                default:
                    err = errors.New("Unknown error")
                }
                sendMeMail(err)
                http.Error(w, err.Error(), http.StatusInternalServerError)
            }
        }()
        h.ServeHTTP(w, r)
    })
}

func sendMeMail(err error) {
    // send mail
}

Вы можете взглянуть на обработчик восстановления codahale или промежуточное ПО negroni для получения дополнительной информации.

person Salah Eddine Taouririt    schedule 26.02.2015
comment
e.Error() должно быть err.Error()? - person eyeApps LLC; 22.09.2016
comment
исправлено @eyeAppsLLC. - person Salah Eddine Taouririt; 22.09.2016
comment
Очень аккуратное решение - person Vahid Amiri; 28.11.2018
comment
r := recover() скрывает r *http.Request от внешней функции. Если вы хотите получить к нему доступ в блоке восстановления - лучше изменить имя, например. e := recover() - person Mih V An; 25.04.2020

Я считаю, что для этого предназначен обработчик восстановления Gorilla.

person mozey    schedule 08.08.2018