[Translate] error handling and go

Source: Internet
Author: User
Tags square root stack trace
This is a creation in Article, where the information may have evolved or changed.

Golang Error-handling Panic–recover model is really not the same, the Go team's blog has written a relatively complete introduction to this model using the article "Error Handling and Go". I think it's very good, so I translate it here. This translation should have been completed long ago, but due to corporate restructuring, and so on, has been left to the end of the failure to deal with the complete. So dragged to today, really should not ah!

———————— Translation Split Line ———————— –

Error handling and go

If you have already written the Go code, you may have encountered an OS. The Error type. The Go code uses the OS. error value to indicate the exception status. For example, when the OS. The open function returns a non-nil OS when the file fails. The Error value.

Func Open (name string) (file *file, err Error)

The following function uses the OS. Open opens a file. If an error occurs, it calls log. Fatal prints error messages and interrupts the operation.

Func Main () {    F, err: = OS. Open ("filename.ext")    if err! = Nil {        log. Fatal (Err)    }    //Do something to open *file F}

In Go, just know the OS. Error can do a lot of things, but in this article, we will learn more about the OS. Error and discuss some of the best ways to handle errors in Go.

Type of error

Os. The Error type is an interface type. Os. The error variable can be any value that can be portrayed as a string. Here is the definition of the interface:

Package OSType Error Interface {    string () string}

For OS. Error is nothing special. It's just a widely used convention.

The most general OS. The Error implementation is an errorstring type of OS package that is not exported.

Type errorstring Stringfunc (S errorstring) string () string {return string (s)}

Available via OS. The NewError function constructs a value like this. It takes a string and then converts it to os.errorstring and returns an OS. The Error value.

Func NewError (s string) Error {return errorstring (s)}

This demonstrates the use of the OS. One possibility of NewError:

Func Sqrt (f float64) (float64, OS. Error) {    If f < 0 {        return 0, OS. NewError ("Math:square root of negative number")    }    //implementation}

The call direction Sqrt passed the wrong argument and gets a non-nil OS. The Error value (actually a os.errorstring value that is re-expressed). Callers can call the OS by invoking the. The string method of error gets the wrong string, or just prints it out:

F, err: = Sqrt ( -1) if err! = Nil {    FMT. PRINTLN (ERR)}

The FMT package can print any content with the string () string method, which includes the OS. The Error value.

This is a responsibility that the error implementation uses to outline the context of the environment. Os. Open returns a formatted error, such as "Open/etc/passwd:permission denied," and not just "permission denied." Information about illegal parameters is missing from the error returned by SQRT.

In order to add this information, there is a useful function Errorf in the FMT package. It formats a string in accordance with the rules of Printf, and then returns it as an OS. NewError the OS created. The Error type.

If f < 0 {    return 0, FMT. Errorf ("Math:square root of negative number%g", f)}

In most cases the FMT. Errorf is good enough, but due to OS. Error is an interface, or you can use a more detailed data structure as the error value to let callers check the details of the error.

For example, suppose a consumer wants to find illegal arguments that are passed to Sqrt. You can do this by defining a new error implementation instead of os.errorstring:

Type Negativesqrterror float64func (f negativesqrterror) string () string {    return FMT. Sprintf ("Math:square root of negative number%g", float64 (f))}

An experienced caller can use the type assertion to examine the negativesqrterror and specifically handle it, just passing the error to the FMT. Println or log. Fatal There is no change in behavior.

For another example, the JSON package specifies JSON. The Decode function returns the SyntaxError type when parsing a JSON blob that has a syntax error.

Type syntaxerror struct {    msg    string//Description Error    offset Int64  //error occurred after reading offset byte}func (e *syntaxerror) Str ing () string {return e.msg}

The Offset field is not displayed in the default format of the error, but it can be used by callers to add file and line information to their error message:

If err: = Dec. Decode (&val); Err! = Nil {    If serr, OK: = Err. ( *json. SyntaxError); OK {line        , col: = Findline (f, serr. Offset)        return to FMT. Errorf ("%s:%d:%d:%v", F.name (), line, col, err)    }    return err}

(There is also a slightly simpler version, some of the actual code from the Camlistore project.) )

Os. The error interface requires only a String method, and a special error implementation may have some additional methods. For example, net packages return types by convention os.Error , but some error implementations contain the net. Additional methods for ERROR definitions:

Package NetType Error Interface {    OS. Error    timeout () bool   //Is it a time-out?    temporary () bool//Is it a temporary error? }

Client code can test net with type assertions. Error so that temporary errors can be found from the persistence error. For example, a Web crawler might hibernate and retry when it encounters a temporary error, and give up completely if it persists.

If nerr, OK: = Err. (NET. ERROR); OK && Nerr. Temporary () {time    . Sleep (1e9)    continue}if Err! = Nil {    log. Fatal (ERR)}

Simplifies repetitive error handling

In Go, error handling is important. The design and specification of this language encourages a clear examination of where errors are generated (which is different from other languages, and then at some point processing them). In some cases, this makes the code of Go verbose, but fortunately there are some techniques that allow for the least repetitive error handling to work.

Consider app Engine applications, get records from the data store when HTTP is processed, and format them through templates.

Func init () {    http. Handlefunc ("/view", Viewrecord)}func Viewrecord (w http. Responsewriter, R *http. Request) {    c: = AppEngine. Newcontext (R)    key: = Datastore. NewKey ("Record", R.formvalue ("id"), 0, Nil)    Record: = new (Record)    If err: = Datastore. Get (c, key, record); Err! = Nil {        http. Error (W, err. String (), ())        return    }    If err: = Viewtemplate.execute (w, record); Err! = Nil {        http. Error (W, err. String (), ()    }}

This function was handled by Datastore. The Get function and the Viewtemplate Execute method return an error. In both cases, it is simple to return an error message to the user, with an HTTP status code of $ ("Internal Server error"). This code looks like it can be improved by adding some HTTP processing and then ending this situation with many of the same error-handling code.

You can customize the HTTP processing Apphandler type, including returning an OS. Error value to reduce duplication:

Type Apphandler func (http. Responsewriter, *http. Request) OS. Error

Then modify the Viewrecord function to return an error:

Func Viewrecord (w http. Responsewriter, R *http. Request) OS. Error {    c: = AppEngine. Newcontext (R)    key: = Datastore. NewKey ("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)}

This is simpler than the original version, but the HTTP packet does not understand the return OS. The Error function. To fix this problem, you can implement an HTTP on the Apphandler. Servehttp method for Handler interface:

Func (FN Apphandler) servehttp (w http. Responsewriter, R *http. Request) {    If Err: = FN (W, R); Err! = Nil {        http. Error (W, err. String (), ()    }}

The Servehttp method calls the Apphandler function and displays the returned error (if any) to the user. Note that the recipient of this method,--FN, is a function. (Go can do this!) The method invokes the recipient defined in the expression fn (W, R).

Now when Viewrecord is registered with the HTTP package, you can use the Handle function (instead of Handlefunc) Apphandler as an HTTP. Handler (instead of an HTTP. Handlerfunc).

Func init () {    http. Handle ("/view", Apphandler (Viewrecord))}

With this error handling in the infrastructure, you can make it more user-friendly. In addition to just displaying an error string, it is better to give the user some simple error messages and the appropriate HTTP status code, while logging the full error for debugging at the APP Engine Developer console.

To do this, create a APPERROR structure that contains the OS. Error and some other fields:

Type apperror struct {    Error os. Error    Message string    Code int}

Next we modify the Apphandler type to return the *apperror value:

Type Apphandler func (http. Responsewriter, *http. Request) *apperror

(Typically, the error message does not use the OS.) It is wrong to pass the actual type, but this is discussed in the article that will be published, but it is correct here, because Servehttp is the only place to see this value and use its content. )

and write the Apphandler Servehttp method to display the Apperror Message and the corresponding HTTP status Code to the user, while recording the complete Error to the developer console:

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)    }}

Finally, we update Viewrecord to the new function declaration and make it return more context when an error occurs:

Func Viewrecord (w http. Responsewriter, R *http. Request) *apperror {    c: = AppEngine. Newcontext (R)    key: = Datastore. NewKey ("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 ', ' $}    '    return nil}

This version of the Viewrecord is similar to the previous length, but now each line has a special meaning and provides a more user-friendly experience.

This is not over; You can further improve error handling in your application. There are some ideas:

    • Provide a nice HTML template for error handling,
    • When the user is an administrator, the stack trace is output to the HTTP response to facilitate debugging,
    • Write a apperror constructor, save stack trace makes debugging easier,
    • Recover from panic in Apphandler, the error is logged in the developer console as a "critical exception" and simply tells the user "a serious error has occurred." This is a good idea to avoid exposing users to unexpected error messages caused by coding errors. See Defer, Panic, and Recover articles for more details.

Summarize

Proper error handling is a basic requirement for good software. Based on the techniques discussed in this article, you can write a more reliable and introductory Go code.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.