diff options
Diffstat (limited to 'doc/articles/error_handling.html')
-rw-r--r-- | doc/articles/error_handling.html | 316 |
1 files changed, 316 insertions, 0 deletions
diff --git a/doc/articles/error_handling.html b/doc/articles/error_handling.html new file mode 100644 index 000000000..8f4fffb48 --- /dev/null +++ b/doc/articles/error_handling.html @@ -0,0 +1,316 @@ +<!--{ + "Title": "Error Handling and Go", + "Template": true +}--> + +<p> +If you have written any Go code you have probably encountered the built-in +<code>error</code> type. Go code uses <code>error</code> values to +indicate an abnormal state. For example, the <code>os.Open</code> function +returns a non-nil <code>error</code> value when it fails to open a file. +</p> + +{{code "/doc/progs/error.go" `/func Open/`}} + +<p> +The following code uses <code>os.Open</code> to open a file. If an error +occurs it calls <code>log.Fatal</code> to print the error message and stop. +</p> + +{{code "/doc/progs/error.go" `/func openFile/` `/STOP/`}} + +<p> +You can get a lot done in Go knowing just this about the <code>error</code> +type, but in this article we'll take a closer look at <code>error</code> and +discuss some good practices for error handling in Go. +</p> + +<p> +<b>The error type</b> +</p> + +<p> +The <code>error</code> type is an interface type. An <code>error</code> +variable represents any value that can describe itself as a string. Here is the +interface's declaration: +</p> + +<pre>type error interface { + Error() string +}</pre> + +<p> +The <code>error</code> type, as with all built in types, is +<a href="/doc/go_spec.html#Predeclared_identifiers">predeclared</a> in the +<a href="/doc/go_spec.html#Blocks">universe block</a>. +</p> + +<p> +The most commonly-used <code>error</code> implementation is the +<a href="/pkg/errors/">errors</a> package's unexported <code>errorString</code> type. +</p> + +{{code "/doc/progs/error.go" `/errorString/` `/STOP/`}} + +<p> +You can construct one of these values with the <code>errors.New</code> +function. It takes a string that it converts to an <code>errors.errorString</code> +and returns as an <code>error</code> value. +</p> + +{{code "/doc/progs/error.go" `/New/` `/STOP/`}} + +<p> +Here's how you might use <code>errors.New</code>: +</p> + +{{code "/doc/progs/error.go" `/func Sqrt/` `/STOP/`}} + +<p> +A caller passing a negative argument to <code>Sqrt</code> receives a non-nil +<code>error</code> value (whose concrete representation is an +<code>errors.errorString</code> value). The caller can access the error string +("math: square root of...") by calling the <code>error</code>'s +<code>Error</code> method, or by just printing it: +</p> + +{{code "/doc/progs/error.go" `/func printErr/` `/STOP/`}} + +<p> +The <a href="/pkg/fmt/">fmt</a> package formats an <code>error</code> value +by calling its <code>Error() string</code> method. +</p> + +<p> +It is the error implementation's responsibility to summarize the context. +The error returned by <code>os.Open</code> formats as "open /etc/passwd: +permission denied," not just "permission denied." The error returned by our +<code>Sqrt</code> is missing information about the invalid argument. +</p> + +<p> +To add that information, a useful function is the <code>fmt</code> package's +<code>Errorf</code>. It formats a string according to <code>Printf</code>'s +rules and returns it as an <code>error</code> created by +<code>errors.New</code>. +</p> + +{{code "/doc/progs/error.go" `/fmtError/` `/STOP/`}} + +<p> +In many cases <code>fmt.Errorf</code> is good enough, but since +<code>error</code> is an interface, you can use arbitrary data structures as +error values, to allow callers to inspect the details of the error. +</p> + +<p> +For instance, our hypothetical callers might want to recover the invalid +argument passed to <code>Sqrt</code>. We can enable that by defining a new +error implementation instead of using <code>errors.errorString</code>: +</p> + +{{code "/doc/progs/error.go" `/type NegativeSqrtError/` `/STOP/`}} + +<p> +A sophisticated caller can then use a +<a href="/doc/go_spec.html#Type_assertions">type assertion</a> to check for a +<code>NegativeSqrtError</code> and handle it specially, while callers that just +pass the error to <code>fmt.Println</code> or <code>log.Fatal</code> will see +no change in behavior. +</p> + +<p> +As another example, the <a href="/pkg/encoding/json/">json</a> package specifies a +<code>SyntaxError</code> type that the <code>json.Decode</code> function +returns when it encounters a syntax error parsing a JSON blob. +</p> + +{{code "/doc/progs/error.go" `/type SyntaxError/` `/STOP/`}} + +<p> +The <code>Offset</code> field isn't even shown in the default formatting of the +error, but callers can use it to add file and line information to their error +messages: +</p> + +{{code "/doc/progs/error.go" `/func decodeError/` `/STOP/`}} + +<p> +(This is a slightly simplified version of some +<a href="http://camlistore.org/code/?p=camlistore.git;a=blob;f=lib/go/camli/jsonconfig/eval.go#l68">actual code</a> +from the <a href="http://camlistore.org">Camlistore</a> project.) +</p> + +<p> +The <code>error</code> interface requires only a <code>Error</code> method; +specific error implementations might have additional methods. For instance, the +<a href="/pkg/net/">net</a> package returns errors of type +<code>error</code>, following the usual convention, but some of the error +implementations have additional methods defined by the <code>net.Error</code> +interface: +</p> + +<pre>package net + +type Error interface { + error + Timeout() bool // Is the error a timeout? + Temporary() bool // Is the error temporary? +}</pre> + +<p> +Client code can test for a <code>net.Error</code> with a type assertion and +then distinguish transient network errors from permanent ones. For instance, a +web crawler might sleep and retry when it encounters a temporary error and give +up otherwise. +</p> + +{{code "/doc/progs/error.go" `/func netError/` `/STOP/`}} + +<p> +<b>Simplifying repetitive error handling</b> +</p> + +<p> +In Go, error handling is important. The language's design and conventions +encourage you to explicitly check for errors where they occur (as distinct from +the convention in other languages of throwing exceptions and sometimes catching +them). In some cases this makes Go code verbose, but fortunately there are some +techniques you can use to minimize repetitive error handling. +</p> + +<p> +Consider an <a href="http://code.google.com/appengine/docs/go/">App Engine</a> +application with an HTTP handler that retrieves a record from the datastore and +formats it with a template. +</p> + +{{code "/doc/progs/error2.go" `/func init/` `/STOP/`}} + +<p> +This function handles errors returned by the <code>datastore.Get</code> +function and <code>viewTemplate</code>'s <code>Execute</code> method. In both +cases, it presents a simple error message to the user with the HTTP status code +500 ("Internal Server Error"). This looks like a manageable amount of code, but +add some more HTTP handlers and you quickly end up with many copies of +identical error handling code. +</p> + +<p> +To reduce the repetition we can define our own HTTP <code>appHandler</code> +type that includes an <code>error</code> return value: +</p> + +{{code "/doc/progs/error3.go" `/type appHandler/`}} + +<p> +Then we can change our <code>viewRecord</code> function to return errors: +</p> + +{{code "/doc/progs/error3.go" `/func viewRecord/` `/STOP/`}} + +<p> +This is simpler than the original version, but the <a +href="/pkg/net/http/">http</a> package doesn't understand functions that return +<code>error</code>. +To fix this we can implement the <code>http.Handler</code> interface's +<code>ServeHTTP</code> method on <code>appHandler</code>: +</p> + +{{code "/doc/progs/error3.go" `/ServeHTTP/` `/STOP/`}} + +<p> +The <code>ServeHTTP</code> method calls the <code>appHandler</code> function +and displays the returned error (if any) to the user. Notice that the method's +receiver, <code>fn</code>, is a function. (Go can do that!) The method invokes +the function by calling the receiver in the expression <code>fn(w, r)</code>. +</p> + +<p> +Now when registering <code>viewRecord</code> with the http package we use the +<code>Handle</code> function (instead of <code>HandleFunc</code>) as +<code>appHandler</code> is an <code>http.Handler</code> (not an +<code>http.HandlerFunc</code>). +</p> + +{{code "/doc/progs/error3.go" `/func init/` `/STOP/`}} + +<p> +With this basic error handling infrastructure in place, we can make it more +user friendly. Rather than just displaying the error string, it would be better +to give the user a simple error message with an appropriate HTTP status code, +while logging the full error to the App Engine developer console for debugging +purposes. +</p> + +<p> +To do this we create an <code>appError</code> struct containing an +<code>error</code> and some other fields: +</p> + +{{code "/doc/progs/error4.go" `/type appError/` `/STOP/`}} + +<p> +Next we modify the appHandler type to return <code>*appError</code> values: +</p> + +{{code "/doc/progs/error4.go" `/type appHandler/`}} + +<p> +(It's usually a mistake to pass back the concrete type of an error rather than +<code>error</code>, +for reasons discussed in <a href="/doc/go_faq.html#nil_error">the Go FAQ</a>, +but it's the right thing to do here because <code>ServeHTTP</code> is the only +place that sees the value and uses its contents.) +</p> + +<p> +And make <code>appHandler</code>'s <code>ServeHTTP</code> method display the +<code>appError</code>'s <code>Message</code> to the user with the correct HTTP +status <code>Code</code> and log the full <code>Error</code> to the developer +console: +</p> + +{{code "/doc/progs/error4.go" `/ServeHTTP/` `/STOP/`}} + +<p> +Finally, we update <code>viewRecord</code> to the new function signature and +have it return more context when it encounters an error: +</p> + +{{code "/doc/progs/error4.go" `/func viewRecord/` `/STOP/`}} + +<p> +This version of <code>viewRecord</code> is the same length as the original, but +now each of those lines has specific meaning and we are providing a friendlier +user experience. +</p> + +<p> +It doesn't end there; we can further improve the error handling in our +application. Some ideas: +</p> + +<ul> +<li>give the error handler a pretty HTML template, +<li>make debugging easier by writing the stack trace to the HTTP response when +the user is an administrator, +<li>write a constructor function for <code>appError</code> that stores the +stack trace for easier debugging, +<li>recover from panics inside the <code>appHandler</code>, logging the error +to the console as "Critical," while telling the user "a serious error +has occurred." This is a nice touch to avoid exposing the user to inscrutable +error messages caused by programming errors. +See the <a href="defer_panic_recover.html">Defer, Panic, and Recover</a> +article for more details. +</ul> + +<p> +<b>Conclusion</b> +</p> + +<p> +Proper error handling is an essential requirement of good software. By +employing the techniques described in this post you should be able to write +more reliable and succinct Go code. +</p> |