As this question is frequently asked, here is a dedicated section for you.

Introduction

Most third-party WebServer libraries for Golang do not have default mechanisms to capture exceptions arising during the HTTP request handling process. Minor errors may not be recorded in logs, leading to difficulty in troubleshooting, while severe exceptions can cause the process to crash, rendering the service unavailable.

If you choose goframe, you are in luck. As an enterprise-level foundational development framework, panic incidents occurring during execution are automatically captured by the Server by default. When a panic occurs, the current execution flow is immediately halted, but it will never cause a direct process crash.

Retrieving Exception Errors

When a panic occurs during the HTTP execution process, it is logged to the Server log file by default. Developers can also manually capture it by registering middleware and customize related error handling. This operation is also explained in the examples of the middleware section, and we will elaborate on it here.

Relevant Methods

Exceptions are captured using the GetError method in the Request object.

Exception Handling - 图1tip

Developers cannot capture exceptions using the recover method because the Server in the goframe framework has already done so. To ensure exceptions do not crash the process by default, they are not re-thrown upwards.

  1. // GetError returns the error that occurs during the request procedure.
  2. // It returns nil if there's no error.
  3. func (r *Request) GetError() error

This method is often used in flow control components, such as post-middleware or HOOK hook methods.

Usage Example

Here, we use a global post-middleware to capture exceptions. When an exception occurs, it is captured and written to a designated log file, and a fixed friendly message is returned to avoid exposing sensitive error information to the client.

Exception Handling - 图2tip

Please note:

  • Even if developers capture and log exceptions themselves, the Server will still print them to its own error log file. Logs outputted by API methods are business logs (related to the business), while self-managed logs by the Server are service logs (similar to nginx‘s error.log).
  • Since most low-level errors in the goframe framework include stack information of the error, if you are interested in specific stack information (call chain, error file path, source code line number, etc.), you can use gerror to retrieve it. For details, please refer to the section Error Handling - Stack. If the exception includes stack information, it is printed by default to the Server‘s error log file.
  1. package main
  2. import (
  3. "github.com/gogf/gf/v2/frame/g"
  4. "github.com/gogf/gf/v2/net/ghttp"
  5. )
  6. func MiddlewareErrorHandler(r *ghttp.Request) {
  7. r.Middleware.Next()
  8. if err := r.GetError(); err != nil {
  9. // Log to a custom error log file
  10. g.Log("exception").Error(err)
  11. // Return a fixed friendly message
  12. r.Response.ClearBuffer()
  13. r.Response.Writeln("The server is having a hiccup. Please try again later!")
  14. }
  15. }
  16. func main() {
  17. s := g.Server()
  18. s.Use(MiddlewareErrorHandler)
  19. s.Group("/api.v2", func(group *ghttp.RouterGroup) {
  20. group.ALL("/user/list", func(r *ghttp.Request) {
  21. panic("db error: sql is xxxxxxx")
  22. })
  23. })
  24. s.SetPort(8199)
  25. s.Run()
  26. }

After execution, let’s give it a try with the curl tool:

  1. $ curl -v "http://127.0.0.1:8199/api.v2/user/list"
  2. > GET /api.v2/user/list HTTP/1.1
  3. > Host: 127.0.0.1:8199
  4. > User-Agent: curl/7.61.1
  5. > Accept: */*
  6. >
  7. < HTTP/1.1 500 Internal Server Error
  8. < Server: GoFrame HTTP Server
  9. < Date: Sun, 19 Jul 2020 07:44:30 GMT
  10. < Content-Length: 52
  11. < Content-Type: text/plain; charset=utf-8
  12. <
  13. The server is having a hiccup. Please try again later!

Retrieving Exception Stacks

Exception Stack Information

When the WebServer itself captures an exception, if the thrown exception information does not include stack content, the WebServer will automatically capture the stack of the point where the exception occurred (the panic location) and create a new error object containing that stack information. Let’s look at an example.

  1. package main
  2. import (
  3. "github.com/gogf/gf/v2/frame/g"
  4. "github.com/gogf/gf/v2/net/ghttp"
  5. )
  6. func MiddlewareErrorHandler(r *ghttp.Request) {
  7. r.Middleware.Next()
  8. if err := r.GetError(); err != nil {
  9. r.Response.ClearBuffer()
  10. r.Response.Writef("%+v", err)
  11. }
  12. }
  13. func main() {
  14. s := g.Server()
  15. s.Use(MiddlewareErrorHandler)
  16. s.Group("/api.v2", func(group *ghttp.RouterGroup) {
  17. group.ALL("/user/list", func(r *ghttp.Request) {
  18. panic("db error: sql is xxxxxxx")
  19. })
  20. })
  21. s.SetPort(8199)
  22. s.Run()
  23. }

You can see that we use the %+v formatted print to retrieve the stack information from the exception error; for the specific principle, please refer to the chapter: Error Handling - Stack. After execution, we will test it with the curl tool:

  1. $ curl "http://127.0.0.1:8199/api.v2/user/list"
  2. db error: sql is xxxxxxx
  3. 1. db error: sql is xxxxxxx
  4. 1). main.main.func1.1
  5. /Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/.example/other/test.go:25
  6. 2). github.com/gogf/gf/v2/net/ghttp.(*middleware).Next.func1.8
  7. /Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/v2/net/ghttp/ghttp_request_middleware.go:111
  8. 3). github.com/gogf/gf/v2/net/ghttp.niceCallFunc
  9. /Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/v2/net/ghttp/ghttp_func.go:46
  10. 4). github.com/gogf/gf/v2/net/ghttp.(*middleware).Next.func1
  11. /Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/v2/net/ghttp/ghttp_request_middleware.go:110
  12. 5). github.com/gogf/gf/v2/util/gutil.TryCatch
  13. /Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/v2/util/gutil/gutil.go:46
  14. 6). github.com/gogf/gf/v2/net/ghttp.(*middleware).Next
  15. /Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/v2/net/ghttp/ghttp_request_middleware.go:47
  16. 7). main.MiddlewareErrorHandler
  17. /Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/.example/other/test.go:10
  18. 8). github.com/gogf/gf/v2/net/ghttp.(*middleware).Next.func1.9
  19. /Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/v2/net/ghttp/ghttp_request_middleware.go:117
  20. 9). github.com/gogf/gf/v2/net/ghttp.niceCallFunc
  21. /Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/v2/net/ghttp/ghttp_func.go:46
  22. 10). github.com/gogf/gf/v2/net/ghttp.(*middleware).Next.func1
  23. /Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/v2/net/ghttp/ghttp_request_middleware.go:116
  24. 11). github.com/gogf/gf/v2/util/gutil.TryCatch
  25. /Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/v2/util/gutil/gutil.go:46
  26. 12). github.com/gogf/gf/v2/net/ghttp.(*middleware).Next
  27. /Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/v2/net/ghttp/ghttp_request_middleware.go:47
  28. 13). github.com/gogf/gf/v2/net/ghttp.(*Server).ServeHTTP
  29. /Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/v2/net/ghttp/ghttp_server_handler.go:122

Error Stack Information

If the thrown exception is an error object created via the gerror component, or an error object implementing the stack print API, since the error object already contains complete stack information, the WebServer will directly return that error object and not automatically create a new error object. Let’s look at an example.

  1. package main
  2. import (
  3. "github.com/gogf/gf/v2/errors/gerror"
  4. "github.com/gogf/gf/v2/frame/g"
  5. "github.com/gogf/gf/v2/net/ghttp"
  6. )
  7. func MiddlewareErrorHandler(r *ghttp.Request) {
  8. r.Middleware.Next()
  9. if err := r.GetError(); err != nil {
  10. r.Response.ClearBuffer()
  11. r.Response.Writef("%+v", err)
  12. }
  13. }
  14. func DbOperation() error {
  15. // ...
  16. return gerror.New("DbOperation error: sql is xxxxxxx")
  17. }
  18. func UpdateData() {
  19. err := DbOperation()
  20. if err != nil {
  21. panic(gerror.Wrap(err, "UpdateData error"))
  22. }
  23. }
  24. func main() {
  25. s := g.Server()
  26. s.Use(MiddlewareErrorHandler)
  27. s.Group("/api.v2", func(group *ghttp.RouterGroup) {
  28. group.ALL("/user/list", func(r *ghttp.Request) {
  29. UpdateData()
  30. })
  31. })
  32. s.SetPort(8199)
  33. s.Run()
  34. }

After execution, we will test it with the curl tool:

  1. $ curl "http://127.0.0.1:8199/api.v2/user/list"
  2. UpdateData error: DbOperation error: sql is xxxxxxx
  3. 1. UpdateData error
  4. 1). main.UpdateData
  5. /Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/.example/other/test.go:25
  6. 2). main.main.func1.1
  7. /Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/.example/other/test.go:34
  8. 3). github.com/gogf/gf/v2/net/ghttp.(*middleware).Next.func1.8
  9. /Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/v2/net/ghttp/ghttp_request_middleware.go:111
  10. 4). github.com/gogf/gf/v2/net/ghttp.niceCallFunc
  11. /Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/v2/net/ghttp/ghttp_func.go:46
  12. 5). github.com/gogf/gf/v2/net/ghttp.(*middleware).Next.func1
  13. /Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/v2/net/ghttp/ghttp_request_middleware.go:110
  14. 6). github.com/gogf/gf/v2/util/gutil.TryCatch
  15. /Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/v2/util/gutil/gutil.go:46
  16. 7). github.com/gogf/gf/v2/net/ghttp.(*middleware).Next
  17. /Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/v2/net/ghttp/ghttp_request_middleware.go:47
  18. 8). main.MiddlewareErrorHandler
  19. /Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/.example/other/test.go:10
  20. 9). github.com/gogf/gf/v2/net/ghttp.(*middleware).Next.func1.9
  21. /Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/v2/net/ghttp/ghttp_request_middleware.go:117
  22. 10). github.com/gogf/gf/v2/net/ghttp.niceCallFunc
  23. /Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/v2/net/ghttp/ghttp_func.go:46
  24. 11). github.com/gogf/gf/v2/net/ghttp.(*middleware).Next.func1
  25. /Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/v2/net/ghttp/ghttp_request_middleware.go:116
  26. 12). github.com/gogf/gf/v2/util/gutil.TryCatch
  27. /Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/v2/util/gutil/gutil.go:46
  28. 13). github.com/gogf/gf/v2/net/ghttp.(*middleware).Next
  29. /Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/v2/net/ghttp/ghttp_request_middleware.go:47
  30. 14). github.com/gogf/gf/v2/net/ghttp.(*Server).ServeHTTP
  31. /Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/v2/net/ghttp/ghttp_server_handler.go:122
  32. 2. DbOperation error: sql is xxxxxxx
  33. 1). main.DbOperation
  34. /Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/.example/other/test.go:19
  35. 2). main.UpdateData
  36. /Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/.example/other/test.go:23
  37. 3). main.main.func1.1
  38. /Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/.example/other/test.go:34
  39. 4). github.com/gogf/gf/v2/net/ghttp.(*middleware).Next.func1.8
  40. /Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/v2/net/ghttp/ghttp_request_middleware.go:111
  41. 5). github.com/gogf/gf/v2/net/ghttp.niceCallFunc
  42. /Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/v2/net/ghttp/ghttp_func.go:46
  43. 6). github.com/gogf/gf/v2/net/ghttp.(*middleware).Next.func1
  44. /Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/v2/net/ghttp/ghttp_request_middleware.go:110
  45. 7). github.com/gogf/gf/v2/util/gutil.TryCatch
  46. /Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/v2/util/gutil/gutil.go:46
  47. 8). github.com/gogf/gf/v2/net/ghttp.(*middleware).Next
  48. /Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/v2/net/ghttp/ghttp_request_middleware.go:47
  49. 9). main.MiddlewareErrorHandler
  50. /Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/.example/other/test.go:10
  51. 10). github.com/gogf/gf/v2/net/ghttp.(*middleware).Next.func1.9
  52. /Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/v2/net/ghttp/ghttp_request_middleware.go:117
  53. 11). github.com/gogf/gf/v2/net/ghttp.niceCallFunc
  54. /Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/v2/net/ghttp/ghttp_func.go:46
  55. 12). github.com/gogf/gf/v2/net/ghttp.(*middleware).Next.func1
  56. /Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/v2/net/ghttp/ghttp_request_middleware.go:116
  57. 13). github.com/gogf/gf/v2/util/gutil.TryCatch
  58. /Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/v2/util/gutil/gutil.go:46
  59. 14). github.com/gogf/gf/v2/net/ghttp.(*middleware).Next
  60. /Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/v2/net/ghttp/ghttp_request_middleware.go:47
  61. 15). github.com/gogf/gf/v2/net/ghttp.(*Server).ServeHTTP
  62. /Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/v2/net/ghttp/ghttp_server_handler.go:122