【GoLang】GoLang 错误处理 -- 使用 error is value 的思路处理,检查并处理error
2016-11-22 13:22
711 查看
吐血推荐:
https://dave.cheney.net/2016/04/27/dont-just-check-errors-handle-them-gracefully
参考资料:
https://blog.golang.org/errors-are-values https://dave.cheney.net/2016/06/12/stack-traces-and-the-errors-package https://godoc.org/github.com/pkg/errors#Cause
This post is an update to my previous blog post which reflects the changes in the
However, when I came to integrate the
If
I realised that, at least in my own code, it is likely that the name of the function contains sufficient information to frequently make the additional context passed to
I briefly considered making
I believe that for 90% of the use cases, this natural stack trace–that is the trace collected at the point
This lead to a large internal refactor of the package to collect and expose this natural stack trace.
The first attempts were a pair of functions;
So, Print and Fprint were the wrong API. They were too opinionated, without it being a useful opinion.
The errors package now leverages the powerful
Prints, as expected,
However if we change the formatting verb to
the same error value now results in
For those that need more control the
In your own code, use
If you receive an error from another function, it is often sufficient to simply return it.
If you interact with a package from another repository, consider using
Always return errors to their caller rather than logging them throughout your program.
At the top level of your program, or worker goroutine, use
If you want to exclude some classes of error from printing, use
The extended stack trace format,
Inspecting errors
Constant errors
Why is a Goroutine’s stack infinite ?
This entry was posted in Go, Programming and tagged error handling, errors, stacktrace on June 12, 2016.
https://dave.cheney.net/2016/04/27/dont-just-check-errors-handle-them-gracefully
参考资料:
https://blog.golang.org/errors-are-values https://dave.cheney.net/2016/06/12/stack-traces-and-the-errors-package https://godoc.org/github.com/pkg/errors#Cause
Stack traces and the errors package
A few months ago I gave a presentation on my philosophy for error handling. In the talk I introduced a smallerrorspackage designed to support the ideas presented in the talk.
This post is an update to my previous blog post which reflects the changes in the
errorspackage as I’ve put it into service in my own projects.
Wrapping and stack traces
In my April presentation I gave examples of using theWrapfunction to produce an annotated error that could be unwrapped for inspection, yet mirrored the recommendations from Kernighan and Donovan’s book.
package main import "fmt" import "github.com/pkg/errors" func main() { err := errors.New("error") err = errors.Wrap(err, "open failed") err = errors.Wrap(err, "read config failed") fmt.Println(err) // read config failed: open failed: error }
Wraping an error added context to the underlying error and recorded the file and line that the error occurred. This file and line information could be retrieved via a helper function,
Fprint, to give a trace of the execution path leading away from the error. More on that later.
However, when I came to integrate the
errorspackage into my own projects, I found that using
Wrapat each call site in the return path often felt redundant. For example:
func readconfig(file string) { if err := openfile(file); err != nil { return errors.Wrap(err, "read config failed") } // ... }
If
openfilefailed it would likely annotate the error it returned with open failed, and that error would also include the file and line of the
openfilefunction. Similarly,
readconfig‘s wrapped error would be annotated with read config failed as well as the file and line of the call to
errors.Wrapinside the
readconfigfunction.
I realised that, at least in my own code, it is likely that the name of the function contains sufficient information to frequently make the additional context passed to
Wrapredundant. But as
Wraprequires a message, even if I had nothing useful to add, I’d still have to pass something:
if err != nil { return errors.Wrap(err, "") // ewww }
I briefly considered making
Wrapvariadic–to make the second parameter optional–before realising that rather than forcing the user to manually annotate each stack frame in the return path, I can just record the entire stack trace at the point that an error is created by the
errorspackage.
I believe that for 90% of the use cases, this natural stack trace–that is the trace collected at the point
Newor
Errorfare called–is correct with respect to the information required to investigate the error’s cause. In the other cases,
Wrapand
Wrapfcan be used to add context when needed.
This lead to a large internal refactor of the package to collect and expose this natural stack trace.
Fprint and Print have been removed
As mentioned earlier, the mechanism for printing not just theerr.Error()text of an error, but also its stack trace, has also changed with feedback from early users.
The first attempts were a pair of functions;
Print(err error), which printed the detailed error to
os.Stderr, and
Fprint(w io.Writer, err error)which did the same but allowed the caller to control the destination. Neither were very popular.
Fprint(os.Stderr, err)and was hard to test, harder to write an example test for, and didn’t feel like its three lines paid their way. However, with
Fprintrequired you to pass an
io.Writer, usually a
bytes.Buffer, just to retrieve a
stringform of the error’s trace.
So, Print and Fprint were the wrong API. They were too opinionated, without it being a useful opinion.
Fprinthas been slowly gutted over the period of 0.5, 0.6 and now has been replaced with a much more powerful facility inspired by Chris Hines’ go-stack/stack package.
The errors package now leverages the powerful
fmt.Formatterinterface to allow it to customise its output when any error generated, or wrapped by this package, is passed to
fmt.Printf. This extended format is activated by the
%+vverb. For example,
func main() { err := parseArgs(os.Args[1:]) fmt.Printf("%v\n", err) }
Prints, as expected,
not enough arguments, expected at least 3, got 0
However if we change the formatting verb to
%+v,
func main() { err := parseArgs(os.Args[1:]) fmt.Printf("%+v\n", err) }
the same error value now results in
not enough arguments, expected at least 3, got 0 main.parseArgs /home/dfc/src/github.com/pkg/errors/_examples/wrap/main.go:12 main.main /home/dfc/src/github.com/pkg/errors/_examples/wrap/main.go:18 runtime.main /home/dfc/go/src/runtime/proc.go:183 runtime.goexit /home/dfc/go/src/runtime/asm_amd64.s:2059
For those that need more control the
Causeand
StackTracebehaviours return values who have their own
fmt.Formatterimplementations. The latter is alias for a slice of
Framevalues which represent each frame in a call stack. Again,
Frameimplements several
fmt.Formatterverbs that allow its output to be customised as required.
Putting it all together
With the changes to theerrorspackage, some guidelines on how to use the package are in order.
In your own code, use
errors.Newor
errors.Errorfat the point an error occurs.
func parseArgs(args []string) error { if len(args) < 3 { return errors.Errorf("not enough arguments, expected at least 3, got %d", len(args)) } // ... }
If you receive an error from another function, it is often sufficient to simply return it.
if err != nil { return err }
If you interact with a package from another repository, consider using
errors.Wrapor
errors.Wrapfto establish a stack trace at that point. This advice also applies when interacting with the standard library.
f, err := os.Open(path) if err != nil { return errors.Wrapf(err, "failed to open %q", path) }
Always return errors to their caller rather than logging them throughout your program.
At the top level of your program, or worker goroutine, use
%+vto print the error with sufficient detail.
func main() { err := app.Run() if err != nil { fmt.Printf("FATAL: %+v\n", err) os.Exit(1) } }
If you want to exclude some classes of error from printing, use
errors.Causeto unwraperrors before inspecting them.
Conclusion
Theerrorspackage, from the point of view of the four package level functions,
New,
Errorf,
Wrap, and
Wrapf,is done. Their API signatures are well tested, and now this package has been integrated into over 100 other packages, are unlikely to change at this point.
The extended stack trace format,
%+v, is still very new and I encourage you to try it and leave feedback via an issue.
Related Posts:
Don’t just check errors, handle them gracefullyInspecting errors
Constant errors
Why is a Goroutine’s stack infinite ?
This entry was posted in Go, Programming and tagged error handling, errors, stacktrace on June 12, 2016.
相关文章推荐
- 【GoLang】GoLang 错误处理 -- 使用异常的思路进行处理
- error: ignoring return value of 编译错误处理
- PHP 使用回调函数(set_error_handler)处理异常和错误
- 在使用jQuery2.1.0和bootstrap2.3.2出现“TypeError: $.browser is undefined”错误的解决方法
- 处理编译错误"0" is an invalid value for the "DebugInformation" parameter of the "DCC"
- error LNK2026: 模块对于 SAFESEH 映像是不安全的 分类: 错误处理 2013-09-23 09:26 6674人阅读 评论(6) 收藏 举报 目录(?)[+] 今天使用VS20
- Flex全局错误处理Global Error Handler在AIR 2.0和Flash Player 10.1中使用
- 使用On Error Resume Next语句,On Error Goto 0语句,Err对象来处理"运行时错误"
- ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in..的错误 [转]
- MySQL中ERROR 1071 (42000): Specified key was too long; max key length is 767 bytes错误的处理
- 关于captcha使用The _imagingft C module is not installed的错误处理
- 非禁用validateRequest=false使用Page_Error()错误处理
- JSF Validation Error: Value is not valid错误
- Android开发错误:Error:” ” is not translated in “en” (English) [MissingTranslation]处理方法?
- 使用Microsoft Update升级时遇到 Server Error 403 - Forbidden:Access is denied错误的解决办法
- configure: error: newly created file is older than distributed files! 错误处理
- 非禁用validateRequest=false使用Page_Error()错误处理[摘自网络]
- Python使用Tkinter错误,NameError: global name 'TOP' is not defined
- xmlbeans 使用 以及 java.io.IOException CreateProcess error=2错误处理(转)
- Python使用pip install psycopg2安装psycopg2包出现python setup.py egg_info failed with error code 1 in /tmp/pip-build-YtLeN3/psycopg2错误处理