The first way
first and most common: is to propagate the error, so that a failure in a subroutine becomes a failure of the calling routine. like:
resp, err := http.Get(url)
if err != nil {
return nil, err
}
In contrast, if the call to html.Parse fails, findLinks does not return the HTML parser's error directly because it lacks two crucial pieces of information: that the error occurred in the parser, and the URL of the document that was being parsed. In this case, findLinks constructs a new error message that includes both pieces of information as well as the underlying parse error
doc, err := html.Parse(resp.Body)
resp.Body.Close()
if err != nil {
return nil, fmt.Errorf("parsing %s as HTML: %v", url, err)
}
Error chain
Attention: When the error is ultimately handled by the program's main function, it should provide a clear causal chain from the root problem to the overall failure, like this NASA accident investigation:
genesis: crashed: no parachute: G-switch failed: bad relay orientation
Because error messages are frequently chained together, message strings should not be capitalized and newlines should be avoided. That helps found by tools like grep.
Package behaviour
When designing error messages, be deliberate, so that each one is a meaningful description of the problem with sufficient and relevant detail, and be consistent, so that errors returned by the same function or by a group of functions in the same package are similar in form and can be dealt in the same way.
For example, the os package guarantees that every error returned by a file operation, such as os.Open or the Read, Write, or Close methods of an open file, describes not just the nature of the failure (permission denied, no such diretory, and so on) but also the name of the file, so the caller needn't include this information in the error message it constructs.
In general the call f(x) is responsible for reporting the attempted operation f and the argument x as they relate to the context of the error. The caller is responsible for adding further information that it has but the callf(x) does not, such as the URL in the call to html.Parse.
The second strategy
For errors the represent transient or unpredictable problems, it may make sense to retry the failed operation, possbily with a delay between tries, and perhaps with a limit on the number of attempts or the time spent trying before giving up entirely.
func WaitForServer(url string) error {
const timeout = 1 * time.Minute
deadline := time.Now().Add(timeout)
for tries := 0; time.Now().Before(deadline); tries ++ {
_, err := http.Head(url)
if err == nil {
return nil //success
}
log.Printf("server not responding (%s); retrying...", err)
time.Sleep(time.Second << uint(tries) // exponential back-off
}
return fmt.Errorf("server %s failed to respond afeter %s", url, timeout)
}
The third way
Third, if progress is impossible, the caller can print the error and stop the program gracefully, but this course of action should generally be reserved for the main package of a program. Library functions should usually progate the errors to the caller, unless the error is a sign of an internal inconsistency-that is, a bug.
if err := WaitForServer(url); err != nil {
fmt.Fprintf(os.Stderr, "Site is down: %v\n", err)
os.Exit(1)
}
// A more convinient way is using log.Fatalf
if err := WaiteForServer(url); err != nil {
log.Fatalf("site is down: %v\n",err)
}
// the default format is helpful in a long-running server, but less so for an interanctive pool
// 2006/01/02 15:04:05 Site is down: no such domain: bad.goly.io
// For a more attrative output, we can set prefix and suppress the display of date and time
log.SetPrefix("wait.")
log.SetFlags(0)
The fourth way
Fourth, in some cases, it's suffiencient just to log the error and then continue, perhaps with reduced funtionality. Again there's a choice between using the log package, which adds the usual prefix.
if err := Ping(); err != nil {
log.Printf("ping failed: %v; newworking disables", err)
}
and printing directly to the standard error stream:
if err := Ping(); err != nil {
fmt.Fprintf(os.Stderr, "ping failed: %v; networking disabled\n", err)
}
The fifth way
In rare case, we can safely ignore an error entirely:
dir, err := ioutil,TempDir("","scratch")
if err != nil {
return fmt.Errorf("failed to create temp dir : %v", err)
}
// ...use temp dir ...
os.RemoveAll(dir) // ignore errors; $TMPDIR is cleaned periodically
The call to os.RemoveAll() may fail, but the program ignores it because the operating system perodically cleans out the temporary directory.