Versions Compared
Key
- This line was added.
- This line was removed.
- Formatting was changed.
由于这个问题问得比较多,因此专门为大家撰写了这一章节。
基本介绍
目前大多数Golang
的第三方WebServer
库均没有默认对HTTP
请求处理过程中产生的异常进行捕获,轻者错误产生后无法记录到日志造成排查错困难,重则异常造成进程直接崩溃,服务不可用。
吐槽:由于作者公司产品也有在用
gin
,某几次生产事故便因为开发者不严谨的代码panic
造成进程崩溃退出。解决方案是所有相关项目必须手动创建recover
中间件进行捕获。
当你选择goframe
,你很幸运。为保证严谨性,默认情况下,执行过程中产生的panic
是有被Server
自动捕获的,产生panic
时当前执行流程会立即中止,但是绝对不会影响进程直接崩溃。
获取异常错误
HTTP
执行流程中产生panic
异常时,默认处理是记录到Server
的日志文件中。当然,开发者也可以通过注册中间件方式手动捕获,然后自定义相关的错误处理。这一操作其实在中间件章节的示例中也有介绍,我们这里来再仔细说明下。
相关方法
异常的捕获我们通过Request
对象中的GetError
方法获取。
Tip |
---|
开发者不能通过 |
Code Block | ||
---|---|---|
| ||
// GetError returns the error occurs in the procedure of the request. // It returns nil if there's no error. func (r *Request) GetError() error |
该方法往往使用在流程控制组件中,如后置中间件或者HOOK
钩子方法中。
使用示例
我们这里使用一个全局的后置中间件来捕获异常,当异常产生后,捕获并写入到指定的日志文件中,返回固定友好的提示信息,避免敏感的报错信息暴露给调用端。
Tip |
---|
需要注意的是:
|
Code Block | ||
---|---|---|
| ||
package main import ( "github.com/gogf/gf/frame/g" "github.com/gogf/gf/net/ghttp" ) func MiddlewareErrorHandler(r *ghttp.Request) { r.Middleware.Next() if err := r.GetError(); err != nil { // 记录到自定义错误日志文件 g.Log("exception").Error(err) //返回固定的友好信息 r.Response.ClearBuffer() r.Response.Writeln("服务器居然开小差了,请稍后再试吧!") } } func main() { s := g.Server() s.Use(MiddlewareErrorHandler) s.Group("/api.v2", func(group *ghttp.RouterGroup) { group.ALL("/user/list", func(r *ghttp.Request) { panic("db error: sql is xxxxxxx") }) }) s.SetPort(8199) s.Run() } |
执行后,我们通过curl
工具来试试吧:
Code Block | ||
---|---|---|
| ||
$ curl -v "http://127.0.0.1:8199/api.v2/user/list" > GET /api.v2/user/list HTTP/1.1 > Host: 127.0.0.1:8199 > User-Agent: curl/7.61.1 > Accept: */* > < HTTP/1.1 500 Internal Server Error < Server: GF HTTP Server < Date: Sun, 19 Jul 2020 07:44:30 GMT < Content-Length: 52 < Content-Type: text/plain; charset=utf-8 < 服务器居然开小差了,请稍后再试吧! |
获取异常堆栈
异常堆栈信息
WebServer
本身在捕获异常时,如果抛出的异常信息并不包含堆栈内容,那么WebServer
会自动获取抛出异常位点(即panic
的位置)的堆栈并创建一个新的包含该堆栈信息的错误对象。我们来看一个示例。WebServer
本身在捕获异常时,异常信息本身包含所有的堆栈信息,该堆栈信息至少会包含抛出异常位点的堆栈。如果抛出的异常时一个通过gerror
组件的错误对象,或者实现堆栈打印接口的错误对象,那么该异常信息也包含所有该错误对象的堆栈信息。我们来看一个示例。
Code Block | ||
---|---|---|
| ||
package main import ( "github.com/gogf/gf/frame/g" "github.com/gogf/gf/net/ghttp" ) func MiddlewareErrorHandler(r *ghttp.Request) { r.Middleware.Next() if err := r.GetError(); err != nil { r.Response.ClearBuffer() r.Response.Writef("%+v", err) } } func main() { s := g.Server() s.Use(MiddlewareErrorHandler) s.Group("/api.v2", func(group *ghttp.RouterGroup) { group.ALL("/user/list", func(r *ghttp.Request) { panic("db error: sql is xxxxxxx") }) }) s.SetPort(8199) s.Run() } |
可以看到,我们通过%+v
的格式化打印来获取异常错误中的堆栈信息,具体原理请参考章节:错误处理-堆栈特性。执行后,我们通过curl
工具来测试下:
Code Block | ||
---|---|---|
| ||
$ curl "http://127.0.0.1:8199/api.v2/user/list"
db error: sql is xxxxxxx
1. db error: sql is xxxxxxx
1). main.main.func1.1
/Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/.example/other/test.go:25
2). github.com/gogf/gf/net/ghttp.(*middleware).Next.func1.8
/Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/net/ghttp/ghttp_request_middleware.go:111
3). github.com/gogf/gf/net/ghttp.niceCallFunc
/Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/net/ghttp/ghttp_func.go:46
4). github.com/gogf/gf/net/ghttp.(*middleware).Next.func1
/Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/net/ghttp/ghttp_request_middleware.go:110
5). github.com/gogf/gf/util/gutil.TryCatch
/Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/util/gutil/gutil.go:46
6). github.com/gogf/gf/net/ghttp.(*middleware).Next
/Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/net/ghttp/ghttp_request_middleware.go:47
7). main.MiddlewareErrorHandler
/Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/.example/other/test.go:10
8). github.com/gogf/gf/net/ghttp.(*middleware).Next.func1.9
/Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/net/ghttp/ghttp_request_middleware.go:117
9). github.com/gogf/gf/net/ghttp.niceCallFunc
/Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/net/ghttp/ghttp_func.go:46
10). github.com/gogf/gf/net/ghttp.(*middleware).Next.func1
/Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/net/ghttp/ghttp_request_middleware.go:116
11). github.com/gogf/gf/util/gutil.TryCatch
/Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/util/gutil/gutil.go:46
12). github.com/gogf/gf/net/ghttp.(*middleware).Next
/Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/net/ghttp/ghttp_request_middleware.go:47
13). github.com/gogf/gf/net/ghttp.(*Server).ServeHTTP
/Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/net/ghttp/ghttp_server_handler.go:122 |
错误堆栈信息
如果抛出的异常是一个通过gerror
组件的错误对象,或者实现堆栈打印接口的错误对象,由于该异常的错误对象已经包含了详细的堆栈信息,那么WebServer
将会直接返回该错误对象,不会自动创建错误对象。我们来看一个示例。
Code Block | ||
---|---|---|
| ||
package main
import (
"github.com/gogf/gf/errors/gerror"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/net/ghttp"
)
func MiddlewareErrorHandler(r *ghttp.Request) {
r.Middleware.Next()
if err := r.GetError(); err != nil {
r.Response.ClearBuffer()
r.Response.Writef("%+v", err)
}
}
func DbOperation() error {
// ...
return gerror.New("DbOperation error: sql is xxxxxxx")
}
func UpdateData() {
err := DbOperation()
if err != nil {
panic(gerror.Wrap(err, "UpdateData error"))
}
}
func main() {
s := g.Server()
s.Use(MiddlewareErrorHandler)
s.Group("/api.v2", func(group *ghttp.RouterGroup) {
group.ALL("/user/list", func(r *ghttp.Request) {
UpdateData()
})
})
s.SetPort(8199)
s.Run()
} |
执行后,我们通过curl
工具来测试下:
Code Block | ||
---|---|---|
| ||
$ curl "http://127.0.0.1:8199/api.v2/user/list" UpdateData error: DbOperation error: sql is xxxxxxx 1. UpdateData error 1). main.UpdateData /Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/.example/other/test.go:25 2). main.main.func1.1 /Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/.example/other/test.go:34 3). github.com/gogf/gf/net/ghttp.(*middleware).Next.func1.8 /Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/net/ghttp/ghttp_request_middleware.go:111 4). github.com/gogf/gf/net/ghttp.niceCallFunc /Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/net/ghttp/ghttp_func.go:46 5). github.com/gogf/gf/net/ghttp.(*middleware).Next.func1 /Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/net/ghttp/ghttp_request_middleware.go:110 6). github.com/gogf/gf/util/gutil.TryCatch /Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/util/gutil/gutil.go:46 7). github.com/gogf/gf/net/ghttp.(*middleware).Next /Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/net/ghttp/ghttp_request_middleware.go:47 8). main.MiddlewareErrorHandler /Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/.example/other/test.go:10 9). github.com/gogf/gf/net/ghttp.(*middleware).Next.func1.9 /Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/net/ghttp/ghttp_request_middleware.go:117 10). github.com/gogf/gf/net/ghttp.niceCallFunc /Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/net/ghttp/ghttp_func.go:46 11). github.com/gogf/gf/net/ghttp.(*middleware).Next.func1 /Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/net/ghttp/ghttp_request_middleware.go:116 12). github.com/gogf/gf/util/gutil.TryCatch /Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/util/gutil/gutil.go:46 13). github.com/gogf/gf/net/ghttp.(*middleware).Next /Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/net/ghttp/ghttp_request_middleware.go:47 14). github.com/gogf/gf/net/ghttp.(*Server).ServeHTTP /Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/net/ghttp/ghttp_server_handler.go:122 2. DbOperation error: sql is xxxxxxx 1). main.DbOperation /Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/.example/other/test.go:19 2). main.UpdateData /Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/.example/other/test.go:23 3). main.main.func1.1 /Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/.example/other/test.go:34 4). github.com/gogf/gf/net/ghttp.(*middleware).Next.func1.8 /Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/net/ghttp/ghttp_request_middleware.go:111 5). github.com/gogf/gf/net/ghttp.niceCallFunc /Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/net/ghttp/ghttp_func.go:46 6). github.com/gogf/gf/net/ghttp.(*middleware).Next.func1 /Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/net/ghttp/ghttp_request_middleware.go:110 7). github.com/gogf/gf/util/gutil.TryCatch /Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/util/gutil/gutil.go:46 8). github.com/gogf/gf/net/ghttp.(*middleware).Next /Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/net/ghttp/ghttp_request_middleware.go:47 9). main.MiddlewareErrorHandler /Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/.example/other/test.go:10 10). github.com/gogf/gf/net/ghttp.(*middleware).Next.func1.9 /Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/net/ghttp/ghttp_request_middleware.go:117 11). github.com/gogf/gf/net/ghttp.niceCallFunc /Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/net/ghttp/ghttp_func.go:46 12). github.com/gogf/gf/net/ghttp.(*middleware).Next.func1 /Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/net/ghttp/ghttp_request_middleware.go:116 13). github.com/gogf/gf/util/gutil.TryCatch /Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/util/gutil/gutil.go:46 14). github.com/gogf/gf/net/ghttp.(*middleware).Next /Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/net/ghttp/ghttp_request_middleware.go:47 15). github.com/gogf/gf/net/ghttp.(*Server).ServeHTTP /Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/net/ghttp/ghttp_server_handler.go:122 |
Panel | ||
---|---|---|
| ||
|