全局恢复处理程序用于Golang的HTTP panic

huangapple go评论79阅读模式
英文:

global recover handler for 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("某个地方会发生错误,但我不知道具体是哪里")
}

如果我知道错误会发生在哪里,那么很容易实现全局错误处理:

if err != nil {
    sendMeMail(err)
}

但是如果我不知道错误会发生在哪里怎么办?所以我应该添加一个全局的recover处理程序。但是我不知道具体该如何实现。

更新

我在main函数的开头添加了defer recover,但在请求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)
            // 找出具体的错误并设置err
            var err error
            switch x := r.(type) {
            case string:
                err = errors.New(x)
            case error:
                err = x
            default:
                err = errors.New("未知的错误")
            }
            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("某个地方会发生错误,但我不知道具体是哪里")
}
英文:

I want to create global err handler to send it by email.

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")
}

How to make it global. It would be easy if I know where error will occur

if err != nil {
sendMeMail(err)
}

But what to do in cases when I don't know exactly where an error will occur? So I should add a global recoverish handler. But how to do it exactly I don't know.

Update

I added defer recover to beginning of main but it never executes on requesting http://localhost:3001. So panic is not emailed.

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")
}

答案1

得分: 36

你可以将处理程序包装在一个恢复中间件中。

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) {
    // 发送邮件
}

你可以查看codahale恢复处理程序negroni中间件以获取更多详细信息。

英文:

You can wrap your handlers in a recovery middleware

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
}

You can take a a look at codahale recovery handler or negroni middleware for more details.

答案2

得分: 4

我相信这就是gorilla recovery handler的用途。

英文:

I believe that is what the gorilla recovery handler is for

huangapple
  • 本文由 发表于 2015年2月26日 22:58:14
  • 转载请务必保留本文链接:https://go.coder-hub.com/28745648.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定