]> Cypherpunks repositories - gostls13.git/commitdiff
doc: add Error Handling article
authorAndrew Gerrand <adg@golang.org>
Mon, 12 Dec 2011 22:44:06 +0000 (09:44 +1100)
committerAndrew Gerrand <adg@golang.org>
Mon, 12 Dec 2011 22:44:06 +0000 (09:44 +1100)
Originally published on The Go Programming Language Blog, July 12, 2011.

http://blog.golang.org/2011/07/error-handling-and-go.html

Update #2547

R=golang-dev, r
CC=golang-dev
https://golang.org/cl/5475060

doc/Makefile
doc/articles/error_handling.html [new file with mode: 0644]
doc/articles/error_handling.tmpl [new file with mode: 0644]
doc/progs/error.go [new file with mode: 0644]
doc/progs/error2.go [new file with mode: 0644]
doc/progs/error3.go [new file with mode: 0644]
doc/progs/error4.go [new file with mode: 0644]
doc/progs/run

index f65e538d974be849e6945e4b55daa4a5114c6f01..daa0a5ea2b65cbdabb93f43945a0b08aa45cc832 100644 (file)
@@ -8,7 +8,14 @@ TARG=tmpltohtml
 GOFILES=\
        tmpltohtml.go\
 
-all: tmpltohtml go_tutorial.html effective_go.html go1.html articles/defer_panic_recover.html
+HTML=\
+       articles/defer_panic_recover.html\
+       articles/error_handling.html\
+       effective_go.html\
+       go1.html\
+       go_tutorial.html\
+
+all: tmpltohtml $(HTML)
 
 %.html: %.tmpl tmpltohtml
        ./makehtml $*.tmpl
diff --git a/doc/articles/error_handling.html b/doc/articles/error_handling.html
new file mode 100644 (file)
index 0000000..1a69324
--- /dev/null
@@ -0,0 +1,447 @@
+<!-- Error Handling and Go -->
+
+<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>
+
+<pre><!--{{code "progs/error.go" `/func Open/`}}
+-->func Open(name string) (file *File, err error)
+</pre>
+
+<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>
+
+<pre><!--{{code "progs/error.go" `/func openFile/` `/STOP/`}}
+-->    f, err := os.Open(&#34;filename.ext&#34;)
+    if err != nil {
+        log.Fatal(err)
+    }
+    // do something with the open *File f
+</pre>
+
+<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>
+
+<pre><!--{{code "progs/error.go" `/errorString/` `/STOP/`}}
+-->// errorString is a trivial implementation of error.
+type errorString struct {
+    s string
+}
+
+func (e *errorString) Error() string {
+    return e.s
+}
+</pre>
+
+<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>
+
+<pre><!--{{code "progs/error.go" `/New/` `/STOP/`}}
+-->// New returns an error that formats as the given text.
+func New(text string) error {
+    return &amp;errorString{text}
+}
+</pre>
+
+<p>
+Here's how you might use <code>errors.New</code>:
+</p>
+
+<pre><!--{{code "progs/error.go" `/func Sqrt/` `/STOP/`}}
+-->func Sqrt(f float64) (float64, error) {
+    if f &lt; 0 {
+        return 0, errors.New(&#34;math: square root of negative number&#34;)
+    }
+    // implementation
+}
+</pre>
+
+<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>
+
+<pre><!--{{code "progs/error.go" `/func printErr/` `/STOP/`}}
+-->    f, err := Sqrt(-1)
+    if err != nil {
+        fmt.Println(err)
+    }
+</pre>
+
+<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>
+
+<pre><!--{{code "progs/error.go" `/fmtError/` `/STOP/`}}
+-->    if f &lt; 0 {
+        return 0, fmt.Errorf(&#34;math: square root of negative number %g&#34;, f)
+    }
+</pre>
+
+<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>
+
+<pre><!--{{code "progs/error.go" `/type NegativeSqrtError/` `/STOP/`}}
+-->type NegativeSqrtError float64
+
+func (f NegativeSqrtError) Error() string {
+    return fmt.Sprintf(&#34;math: square root of negative number %g&#34;, float64(f))
+}
+</pre>
+
+<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>
+
+<pre><!--{{code "progs/error.go" `/type SyntaxError/` `/STOP/`}}
+-->type SyntaxError struct {
+    msg    string // description of error
+    Offset int64  // error occurred after reading Offset bytes
+}
+
+func (e *SyntaxError) Error() string { return e.msg }
+</pre>
+
+<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>
+
+<pre><!--{{code "progs/error.go" `/func decodeError/` `/STOP/`}}
+-->    if err := dec.Decode(&amp;val); err != nil {
+        if serr, ok := err.(*json.SyntaxError); ok {
+            line, col := findLine(f, serr.Offset)
+            return fmt.Errorf(&#34;%s:%d:%d: %v&#34;, f.Name(), line, col, err)
+        }
+        return err
+    }
+</pre>
+
+<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>
+
+<pre><!--{{code "progs/error.go" `/func netError/` `/STOP/`}}
+-->        if nerr, ok := err.(net.Error); ok &amp;&amp; nerr.Temporary() {
+            time.Sleep(1e9)
+            continue
+        }
+        if err != nil {
+            log.Fatal(err)
+        }
+</pre>
+
+<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>
+
+<pre><!--{{code "progs/error2.go" `/func init/` `/STOP/`}}
+-->func init() {
+    http.HandleFunc(&#34;/view&#34;, viewRecord)
+}
+
+func viewRecord(w http.ResponseWriter, r *http.Request) {
+    c := appengine.NewContext(r)
+    key := datastore.NewKey(c, &#34;Record&#34;, r.FormValue(&#34;id&#34;), 0, nil)
+    record := new(Record)
+    if err := datastore.Get(c, key, record); err != nil {
+        http.Error(w, err.Error(), 500)
+        return
+    }
+    if err := viewTemplate.Execute(w, record); err != nil {
+        http.Error(w, err.Error(), 500)
+    }
+}
+</pre>
+
+<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>
+
+<pre><!--{{code "progs/error3.go" `/type appHandler/`}}
+-->type appHandler func(http.ResponseWriter, *http.Request) error
+</pre>
+
+<p>
+Then we can change our <code>viewRecord</code> function to return errors:
+</p>
+
+<pre><!--{{code "progs/error3.go" `/func viewRecord/` `/STOP/`}}
+-->func viewRecord(w http.ResponseWriter, r *http.Request) error {
+    c := appengine.NewContext(r)
+    key := datastore.NewKey(c, &#34;Record&#34;, r.FormValue(&#34;id&#34;), 0, nil)
+    record := new(Record)
+    if err := datastore.Get(c, key, record); err != nil {
+        return err
+    }
+    return viewTemplate.Execute(w, record)
+}
+</pre>
+
+<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>
+
+<pre><!--{{code "progs/error3.go" `/ServeHTTP/` `/STOP/`}}
+-->func (fn appHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+    if err := fn(w, r); err != nil {
+        http.Error(w, err.Error(), 500)
+    }
+}
+</pre>
+
+<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>
+
+<pre><!--{{code "progs/error3.go" `/func init/` `/STOP/`}}
+-->func init() {
+    http.Handle(&#34;/view&#34;, appHandler(viewRecord))
+}
+</pre>
+
+<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>
+
+<pre><!--{{code "progs/error4.go" `/type appError/` `/STOP/`}}
+-->type appError struct {
+    Error   error
+    Message string
+    Code    int
+}
+</pre>
+
+<p>
+Next we modify the appHandler type to return <code>*appError</code> values:
+</p>
+
+<pre><!--{{code "progs/error4.go" `/type appHandler/`}}
+-->type appHandler func(http.ResponseWriter, *http.Request) *appError
+</pre>
+
+<p>
+(It's usually a mistake to pass back the concrete type of an error rather than
+<code>error</code>, for reasons to be discussed in another article, 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>
+
+<pre><!--{{code "progs/error4.go" `/ServeHTTP/` `/STOP/`}}
+-->func (fn appHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+    if e := fn(w, r); e != nil { // e is *appError, not os.Error.
+        c := appengine.NewContext(r)
+        c.Errorf(&#34;%v&#34;, e.Error)
+        http.Error(w, e.Message, e.Code)
+    }
+}
+</pre>
+
+<p>
+Finally, we update <code>viewRecord</code> to the new function signature and
+have it return more context when it encounters an error: 
+</p>
+
+<pre><!--{{code "progs/error4.go" `/func viewRecord/` `/STOP/`}}
+-->func viewRecord(w http.ResponseWriter, r *http.Request) *appError {
+    c := appengine.NewContext(r)
+    key := datastore.NewKey(c, &#34;Record&#34;, r.FormValue(&#34;id&#34;), 0, nil)
+    record := new(Record)
+    if err := datastore.Get(c, key, record); err != nil {
+        return &amp;appError{err, &#34;Record not found&#34;, 404}
+    }
+    if err := viewTemplate.Execute(w, record); err != nil {
+        return &amp;appError{err, &#34;Can&#39;t display record&#34;, 500}
+    }
+    return nil
+}
+</pre>
+
+<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 simply 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>
diff --git a/doc/articles/error_handling.tmpl b/doc/articles/error_handling.tmpl
new file mode 100644 (file)
index 0000000..75800ae
--- /dev/null
@@ -0,0 +1,312 @@
+<!-- Error Handling and Go -->
+
+<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 "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 "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 "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 "progs/error.go" `/New/` `/STOP/`}}
+
+<p>
+Here's how you might use <code>errors.New</code>:
+</p>
+
+{{code "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 "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 "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 "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 "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 "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 "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 "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 "progs/error3.go" `/type appHandler/`}}
+
+<p>
+Then we can change our <code>viewRecord</code> function to return errors:
+</p>
+
+{{code "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 "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 "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 "progs/error4.go" `/type appError/` `/STOP/`}}
+
+<p>
+Next we modify the appHandler type to return <code>*appError</code> values:
+</p>
+
+{{code "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 to be discussed in another article, 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 "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 "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 simply 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>
diff --git a/doc/progs/error.go b/doc/progs/error.go
new file mode 100644 (file)
index 0000000..3f98709
--- /dev/null
@@ -0,0 +1,115 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file contains the code snippets included in "Error Handling and Go."
+
+package main
+
+import (
+       "encoding/json"
+       "errors"
+       "fmt"
+       "log"
+       "net"
+       "os"
+       "time"
+)
+
+type File struct{}
+
+func Open(name string) (file *File, err error)
+
+func openFile() { // OMIT
+       f, err := os.Open("filename.ext")
+       if err != nil {
+               log.Fatal(err)
+       }
+       // do something with the open *File f
+       // STOP OMIT
+       _ = f
+}
+
+// errorString is a trivial implementation of error.
+type errorString struct {
+       s string
+}
+
+func (e *errorString) Error() string {
+       return e.s
+}
+// STOP OMIT
+
+// New returns an error that formats as the given text.
+func New(text string) error {
+       return &errorString{text}
+}
+// STOP OMIT
+
+func Sqrt(f float64) (float64, error) {
+       if f < 0 {
+               return 0, errors.New("math: square root of negative number")
+       }
+       // implementation
+       return 0, nil // OMIT
+}
+// STOP OMIT
+
+func printErr() (int, error) { // OMIT
+       f, err := Sqrt(-1)
+       if err != nil {
+               fmt.Println(err)
+       }
+       // STOP OMIT
+       // fmtError OMIT
+       if f < 0 {
+               return 0, fmt.Errorf("math: square root of negative number %g", f)
+       }
+       // STOP OMIT
+       return 0, nil
+}
+
+type NegativeSqrtError float64
+
+func (f NegativeSqrtError) Error() string {
+       return fmt.Sprintf("math: square root of negative number %g", float64(f))
+}
+// STOP OMIT
+
+type SyntaxError struct {
+       msg    string // description of error
+       Offset int64  // error occurred after reading Offset bytes
+}
+
+func (e *SyntaxError) Error() string { return e.msg }
+// STOP OMIT
+
+func decodeError(dec *json.Decoder, val struct{}) error { // OMIT
+       var f os.FileInfo // OMIT
+       if err := dec.Decode(&val); err != nil {
+               if serr, ok := err.(*json.SyntaxError); ok {
+                       line, col := findLine(f, serr.Offset)
+                       return fmt.Errorf("%s:%d:%d: %v", f.Name(), line, col, err)
+               }
+               return err
+       }
+       // STOP OMIT
+       return nil
+}
+
+func findLine(os.FileInfo, int64) (int, int)
+
+func netError(err error) { // OMIT
+       for { // OMIT
+               if nerr, ok := err.(net.Error); ok && nerr.Temporary() {
+                       time.Sleep(1e9)
+                       continue
+               }
+               if err != nil {
+                       log.Fatal(err)
+               }
+               // STOP OMIT
+       }
+}
+
+func main() {}
diff --git a/doc/progs/error2.go b/doc/progs/error2.go
new file mode 100644 (file)
index 0000000..fe72350
--- /dev/null
@@ -0,0 +1,53 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file contains the code snippets included in "Error Handling and Go."
+
+package main
+
+import (
+       "net/http"
+       "text/template"
+)
+
+func init() {
+       http.HandleFunc("/view", viewRecord)
+}
+
+func viewRecord(w http.ResponseWriter, r *http.Request) {
+       c := appengine.NewContext(r)
+       key := datastore.NewKey(c, "Record", r.FormValue("id"), 0, nil)
+       record := new(Record)
+       if err := datastore.Get(c, key, record); err != nil {
+               http.Error(w, err.Error(), 500)
+               return
+       }
+       if err := viewTemplate.Execute(w, record); err != nil {
+               http.Error(w, err.Error(), 500)
+       }
+}
+// STOP OMIT
+
+type ap struct{}
+
+func (ap) NewContext(*http.Request) *ctx { return nil }
+
+type ctx struct{}
+
+func (*ctx) Errorf(string, ...interface{}) {}
+
+var appengine ap
+
+type ds struct{}
+
+func (ds) NewKey(*ctx, string, string, int, *int) string { return "" }
+func (ds) Get(*ctx, string, *Record) error               { return nil }
+
+var datastore ds
+
+type Record struct{}
+
+var viewTemplate *template.Template
+
+func main() {}
diff --git a/doc/progs/error3.go b/doc/progs/error3.go
new file mode 100644 (file)
index 0000000..8305edc
--- /dev/null
@@ -0,0 +1,60 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file contains the code snippets included in "Error Handling and Go."
+
+package main
+
+import (
+       "net/http"
+       "text/template"
+)
+
+func init() {
+       http.Handle("/view", appHandler(viewRecord))
+}
+// STOP OMIT
+
+func viewRecord(w http.ResponseWriter, r *http.Request) error {
+       c := appengine.NewContext(r)
+       key := datastore.NewKey(c, "Record", r.FormValue("id"), 0, nil)
+       record := new(Record)
+       if err := datastore.Get(c, key, record); err != nil {
+               return err
+       }
+       return viewTemplate.Execute(w, record)
+}
+// STOP OMIT
+
+type appHandler func(http.ResponseWriter, *http.Request) error
+
+func (fn appHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+       if err := fn(w, r); err != nil {
+               http.Error(w, err.Error(), 500)
+       }
+}
+// STOP OMIT
+
+type ap struct{}
+
+func (ap) NewContext(*http.Request) *ctx { return nil }
+
+type ctx struct{}
+
+func (*ctx) Errorf(string, ...interface{}) {}
+
+var appengine ap
+
+type ds struct{}
+
+func (ds) NewKey(*ctx, string, string, int, *int) string { return "" }
+func (ds) Get(*ctx, string, *Record) error               { return nil }
+
+var datastore ds
+
+type Record struct{}
+
+var viewTemplate *template.Template
+
+func main() {}
diff --git a/doc/progs/error4.go b/doc/progs/error4.go
new file mode 100644 (file)
index 0000000..661dcdc
--- /dev/null
@@ -0,0 +1,71 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file contains the code snippets included in "Error Handling and Go."
+
+package main
+
+import (
+       "net/http"
+       "text/template"
+)
+
+type appError struct {
+       Error   error
+       Message string
+       Code    int
+}
+// STOP OMIT
+
+type appHandler func(http.ResponseWriter, *http.Request) *appError
+
+func (fn appHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+       if e := fn(w, r); e != nil { // e is *appError, not os.Error.
+               c := appengine.NewContext(r)
+               c.Errorf("%v", e.Error)
+               http.Error(w, e.Message, e.Code)
+       }
+}
+// STOP OMIT
+
+func viewRecord(w http.ResponseWriter, r *http.Request) *appError {
+       c := appengine.NewContext(r)
+       key := datastore.NewKey(c, "Record", r.FormValue("id"), 0, nil)
+       record := new(Record)
+       if err := datastore.Get(c, key, record); err != nil {
+               return &appError{err, "Record not found", 404}
+       }
+       if err := viewTemplate.Execute(w, record); err != nil {
+               return &appError{err, "Can't display record", 500}
+       }
+       return nil
+}
+// STOP OMIT
+
+func init() {
+       http.Handle("/view", appHandler(viewRecord))
+}
+
+type ap struct{}
+
+func (ap) NewContext(*http.Request) *ctx { return nil }
+
+type ctx struct{}
+
+func (*ctx) Errorf(string, ...interface{}) {}
+
+var appengine ap
+
+type ds struct{}
+
+func (ds) NewKey(*ctx, string, string, int, *int) string { return "" }
+func (ds) Get(*ctx, string, *Record) error               { return nil }
+
+var datastore ds
+
+type Record struct{}
+
+var viewTemplate *template.Template
+
+func main() {}
index d8efe96c250c60589ccbbd0ac5de27526450b6b0..b4c63f8b3e49ffd1314a22e75aa4bb528fd3bca4 100755 (executable)
@@ -31,6 +31,13 @@ effective_go="
        eff_sequence.go
 "
 
+error_handling="
+       error.go
+       error2.go
+       error3.go
+       error4.go
+"
+
 go_tutorial="
        cat.go 
        cat_rot13.go 
@@ -52,6 +59,7 @@ go_tutorial="
 for i in \
        $defer_panic_recover \
        $effective_go \
+       $error_handling \
        $go_tutorial \
        go1.go \
 ; do