1. Native error handling
Go language provides a very simple error handling mechanism through the built-in error interface.
error type is an interface type. This is its definition:
type error interface {
Error() string
}
We can generate error information by implementing the error interface type in coding.
Functions usually return error information in the last return value. Use errors.New to return an error message:
func Sqrt(f float64) (float64, error) { if f < 0 { return 0, errors.New("math: square root of negative number") } // realization }
In the following example, we pass a negative number when calling Sqrt, and then we get the non nil error object. Comparing this object with nil, the result is true, so fmt.Println(fmt package will call the error method when processing error) is called to output the error. See the example code called below:
result, err:= Sqrt(-1) if err != nil { fmt.Println(err) }
2. Open source error package
github.com/pkg/errors package adds the following common functions to the original error package:
- You can print the stack information of error: the print error needs% + v to be output in detail
- Use Wrap or Wrapf to initialize an error
- Using errors.WithMessage, you can wrap another layer based on the original error, including the original error information
- errors.Is, which is used to judge the error type. Different processing can be done according to different error types
- errors.As, used to resolve error s
See the global error handling section for specific use cases.
3. Error handling in Engineering
3.1 requirements sorting
Customize the error information and code it
- The controller layer can determine the user-defined error type, and finally determine whether to process by info or error
- You can print the initial location of the error (get the call stack of the error)
Confirm the current system location:
- User, get TagMessage
- Upstream service, error code mapping required
- Log monitoring, TagMessage monitoring
Next, in an engineering project, a complete set of error handling mechanism is implemented by using github.com/pkg/errors package
3.2 method 1: Map saves the mapping between error code and Message
3.2.1 definition error message
New error_handler.go
package error_handle import ( "github.com/pkg/errors" ) // 1. Customize the error structure and override the Error() method // Custom structure returned on error type CustomError struct { Code int `json:"code"` // Service code TagMessage string `json:"message"` // Description information } func (e *CustomError) Error() string { return e.TagMessage } // 2. Define errorCode const ( // Service level error code ServerError = 10101 TooManyRequests = 10102 ParamBindError = 10103 AuthorizationError = 10104 CallHTTPError = 10105 ResubmitError = 10106 ResubmitMsg = 10107 HashIdsDecodeError = 10108 SignatureError = 10109 // Business module level error code // User module IllegalUserName = 20101 UserCreateError = 20102 UserUpdateError = 20103 UserSearchError = 20104 // Authorized caller AuthorizedCreateError = 20201 AuthorizedListError = 20202 AuthorizedDeleteError = 20203 AuthorizedUpdateError = 20204 AuthorizedDetailError = 20205 AuthorizedCreateAPIError = 20206 AuthorizedListAPIError = 20207 AuthorizedDeleteAPIError = 20208 // administrators AdminCreateError = 20301 AdminListError = 20302 AdminDeleteError = 20303 AdminUpdateError = 20304 AdminResetPasswordError = 20305 AdminLoginError = 20306 AdminLogOutError = 20307 AdminModifyPasswordError = 20308 AdminModifyPersonalInfoError = 20309 // to configure ConfigEmailError = 20401 ConfigSaveError = 20402 ConfigRedisConnectError = 20403 ConfigMySQLConnectError = 20404 ConfigMySQLInstallError = 20405 ConfigGoVersionError = 20406 // Practical toolbox SearchRedisError = 20501 ClearRedisError = 20502 SearchRedisEmpty = 20503 SearchMySQLError = 20504 // menu bar MenuCreateError = 20601 MenuUpdateError = 20602 MenuListError = 20603 MenuDeleteError = 20604 MenuDetailError = 20605 // Borrow books BookNotFoundError = 20701 BookHasBeenBorrowedError = 20702 ) // 3. Define the text information corresponding to errorCode var codeTag = map[int]string{ ServerError: "Internal Server Error", TooManyRequests: "Too Many Requests", ParamBindError: "Incorrect parameter information", AuthorizationError: "Incorrect signature information", CallHTTPError: "Call third party HTTP Interface failure", ResubmitError: "Resubmit Error", ResubmitMsg: "Do not resubmit", HashIdsDecodeError: "ID Parameter error", SignatureError: "SignatureError", IllegalUserName: "Illegal user name", UserCreateError: "Failed to create user", UserUpdateError: "Failed to update user", UserSearchError: "Failed to query user", AuthorizedCreateError: "Failed to create caller", AuthorizedListError: "Failed to get caller list page", AuthorizedDeleteError: "Failed to delete caller", AuthorizedUpdateError: "Failed to update caller", AuthorizedDetailError: "Failed to get caller details", AuthorizedCreateAPIError: "Create caller API Address failure", AuthorizedListAPIError: "Get caller API Address list failed", AuthorizedDeleteAPIError: "Delete caller API Address failure", AdminCreateError: "Failed to create administrator", AdminListError: "Failed to get administrator list page", AdminDeleteError: "Failed to delete administrator", AdminUpdateError: "Failed to update administrator", AdminResetPasswordError: "Failed to reset password", AdminLoginError: "Login failed", AdminLogOutError: "Exit failed", AdminModifyPasswordError: "Failed to modify password", AdminModifyPersonalInfoError: "Failed to modify personal information", ConfigEmailError: "Failed to modify mailbox configuration", ConfigSaveError: "Failed to write configuration file", ConfigRedisConnectError: "Redis connection failed", ConfigMySQLConnectError: "MySQL connection failed", ConfigMySQLInstallError: "MySQL Failed to initialize data", ConfigGoVersionError: "GoVersion Not meeting requirements", SearchRedisError: "query RedisKey fail", ClearRedisError: "empty RedisKey fail", SearchRedisEmpty: "Inquired RedisKey non-existent", SearchMySQLError: "query mysql fail", MenuCreateError: "Failed to create menu", MenuUpdateError: "Failed to update menu", MenuDeleteError: "Failed to delete menu", MenuListError: "Failed to get menu list page", MenuDetailError: "Failed to get menu details", BookNotFoundError: "Book not found", BookHasBeenBorrowedError: "The book has been borrowed", } func Text(code int) string { return codeTag[code] } // 4. New custom error instantiation func NewCustomError(code int) error { // The first call must be instantiated with the Wrap method return errors.Wrap(&CustomError{ Code: code, TagMessage: codeTag[code], }, "") }
3.3 user defined Error usage
New test file: error_handler_test.go
package error_handle import ( "fmt" "github.com/pkg/errors" "testing" ) func TestText(t *testing.T) { books := []string{ "Book1", "Book222222", "Book3333333333", } for _, bookName := range books { err := searchBook(bookName) // Special business scenario: if the book is found to be borrowed, just come back next time. It does not need to be treated as an error if err != nil { // The error code at the bottom of the interface, error, is usually extracted before the API returns // As - get the specific implementation of the error var myError = new(CustomError) // As - parse error content if errors.As(err, &myError) { fmt.Printf("AS Information in: the current book is: %s ,error code is %d, message is %s\n", bookName, myError.Code, myError.TagMessage) } // For special scenarios, when an error (ErrorBookHasBeenBorrowed) is specified, it can be printed without returning an error // Is - determines whether the error is of the specified type if errors.Is(err, NewCustomError(BookHasBeenBorrowedError)) { fmt.Printf("IS Information in:%s It has been borrowed, Just press Info handle!\n", bookName) err = nil }else { // If there is stack information, call the WithMessage method newErr := errors.WithMessage(err, "WithMessage err") fmt.Printf("IS Information in:%s Not found, should press Error handle! ,newErr is %s\n", bookName , newErr) } } } } func searchBook(bookName string) error { // 1. It is found that the book does not exist in the Library - it is considered to be an error and detailed error information needs to be printed if len(bookName) > 10 { return NewCustomError(BookHasBeenBorrowedError) } else if len(bookName) > 6 { // 2. If you find that the book has been borrowed - just print the prompt of being taken away. It is not considered an error return NewCustomError(BookHasBeenBorrowedError) } // 3 find the book - no processing required return nil }
3.3 method 2: simplify the code with the help of generate (recommended)
The relationship between maintenance error codes and error information in mode 1 is complex. We can automatically generate codes with the help of go generate.
3.3.1 installing stringer
stringer is not a Go built-in tool and needs to be installed manually. Execute the following commands
go get golang.org/x/tools/cmd/stringer
3.3.1 definition error message
New error_handler.go. In error_ In handler, add a comment / / go:generate stringer -type ErrCode -linecomment. Execute go generate to generate a new file
package error_handle import ( "github.com/pkg/errors" ) // 1. Customize the error structure and override the Error() method // Custom structure returned on error type CustomError struct { Code ErrCode `json:"code"` // Service code Message string `json:"message"` // Service code } func (e *CustomError) Error() string { return e.Code.String() } type ErrCode int64 //Error code // 2. Define errorCode //go:generate stringer -type ErrCode -linecomment const ( // Service level error code ServerError ErrCode = 10101 // Internal Server Error TooManyRequests ErrCode = 10102 // Too Many Requests ParamBindError ErrCode = 10103 // Incorrect parameter information AuthorizationError ErrCode = 10104 // Incorrect signature information CallHTTPError ErrCode = 10105 // Failed to call the third-party HTTP interface ResubmitError ErrCode = 10106 // ResubmitError ResubmitMsg ErrCode = 10107 // Do not resubmit HashIdsDecodeError ErrCode = 10108 // Error in ID parameter SignatureError ErrCode = 10109 // SignatureError // Business module level error code // User module IllegalUserName ErrCode = 20101 // Illegal user name UserCreateError ErrCode = 20102 // Failed to create user UserUpdateError ErrCode = 20103 // Failed to update user UserSearchError ErrCode = 20104 // Failed to query user // to configure ConfigEmailError ErrCode = 20401 // Failed to modify mailbox configuration ConfigSaveError ErrCode = 20402 // Failed to write configuration file ConfigRedisConnectError ErrCode = 20403 // Redis connection failed ConfigMySQLConnectError ErrCode = 20404 // MySQL connection failed ConfigMySQLInstallError ErrCode = 20405 // MySQL failed to initialize data ConfigGoVersionError ErrCode = 20406 // GoVersion does not meet requirements // Practical toolbox SearchRedisError ErrCode = 20501 // Failed to query RedisKey ClearRedisError ErrCode = 20502 // Failed to clear RedisKey SearchRedisEmpty ErrCode = 20503 // RedisKey queried does not exist SearchMySQLError ErrCode = 20504 // Failed to query mysql // menu bar MenuCreateError ErrCode = 20601 // Failed to create menu MenuUpdateError ErrCode = 20602 // Failed to update menu MenuListError ErrCode = 20603 // Failed to delete menu MenuDeleteError ErrCode = 20604 // Failed to get menu list page MenuDetailError ErrCode = 20605 // Failed to get menu details // Borrow books BookNotFoundError ErrCode = 20701 // Book not found BookHasBeenBorrowedError ErrCode = 20702 // The book has been borrowed ) // 4. New custom error instantiation func NewCustomError(code ErrCode) error { // The first call must be instantiated with the Wrap method return errors.Wrap(&CustomError{ Code: code, Message: code.String(), }, "") }
3.3.2 user defined Error usage
New test file: error_handler_test.go
package error_handle import ( "fmt" "github.com/pkg/errors" "testing" ) func TestText(t *testing.T) { books := []string{ "Book1", "Book222222", "Book3333333333", } for _, bookName := range books { err := searchBook(bookName) // Special business scenario: if the book is found to be borrowed, just come back next time. It does not need to be treated as an error if err != nil { // The error code at the bottom of the interface, error, is usually extracted before the API returns // As - get the specific implementation of the error var customErr = new(CustomError) // As - parse error content if errors.As(err, &customErr) { //FMT. Printf ("information in as: current book is:% s, error code is% D, message is% s \ n", bookname, customerr. Code, customerr. Message) if customErr.Code == BookHasBeenBorrowedError { fmt.Printf("IS Medium info Information:%s It has been borrowed, Just press Info handle!\n", bookName) } else { // If there is stack information, call the WithMessage method newErr := errors.WithMessage(err, "WithMessage err1") // Use% + v to print the complete stack information fmt.Printf("IS Medium error Information:%s Not found, should press Error handle! ,newErr is: %+v\n", bookName, newErr) } } } } } func searchBook(bookName string) error { // 1. It is found that the book does not exist in the Library - it is considered to be an error and detailed error information needs to be printed if len(bookName) > 10 { return NewCustomError(BookNotFoundError) } else if len(bookName) > 6 { // 2. If you find that the book has been borrowed - just print the prompt of being taken away. It is not considered an error return NewCustomError(BookHasBeenBorrowedError) } // 3 find the book - no processing required return nil }
4 Summary
- As the bottom implementation of global error, CustomError saves specific error codes and error information;
- When CustomError returns an error upward, first initialize the stack with Wrap, and then increase the stack information with WithMessage;
- When parsing a specific error from error, use errors.As to extract CustomError, and the error code and error information can be passed into the specific API interface;
- To determine whether the error is the specified error, use the methods of errors.Is + Handler Error to process the logic in some specific cases;
Tips:
- Don't always use errors.Wrap to wrap errors repeatedly. The stack information will explode. You can test and understand the specific situation by yourself
- Using go generate can greatly simplify the repeated work of initializing Erro
- github.com/pkg/errors is fully compatible with the error of the standard library. You can replace and later transform the code left over by history
- It must be noted that the stack of printing error needs to use% + V, while the original% v is still an ordinary string method; At the same time, pay attention to whether the log collection tool supports multi line matching
I am Jian Fan, an inspirational migrant worker in the new era who aims to describe the most complex problems in the simplest language. If you have any doubts about this article, please leave a message in my WeChat official account. What I can do is to help you:
- Help build your own knowledge system
- Real high concurrency scenarios on the Internet
- Occasionally share classic scenarios and practices related to Golang and Java
My blog: https://besthpt.github.io/
The official account of WeChat: