Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Stack traces for Go errors

License

NotificationsYou must be signed in to change notification settings

palantir/stacktrace

Repository files navigation

Look at Palantir, such a Java shop. I can't believe they want stack traces intheir Go code.

Why would anyone want stack traces in Go code?

This is difficult to debug:

Inverse tachyon pulse failed

This gives the full story and is easier to debug:

Failed to register for villain discovery --- at github.com/palantir/shield/agent/discovery.go:265 (ShieldAgent.reallyRegister) --- --- at github.com/palantir/shield/connector/impl.go:89 (Connector.Register) ---Caused by: Failed to load S.H.I.E.L.D. config from /opt/shield/conf/shield.yaml --- at github.com/palantir/shield/connector/config.go:44 (withShieldConfig) ---Caused by: There isn't enough time (4 picoseconds required) --- at github.com/palantir/shield/axiom/pseudo/resource.go:46 (PseudoResource.Adjust) --- --- at github.com/palantir/shield/axiom/pseudo/growth.go:110 (reciprocatingPseudo.growDown) --- --- at github.com/palantir/shield/axiom/pseudo/growth.go:121 (reciprocatingPseudo.verify) ---Caused by: Inverse tachyon pulse failed --- at github.com/palantir/shield/metaphysic/tachyon.go:72 (TryPulse) ---

Note that stack traces arenot designed to be user-visible. We have found themto be valuable in log files of server applications. Nobody wants to see these inCLI output or a web interface or a return value from library code.

Intent

The intent isnot that we capture the exact state of the stack when an errorhappens, including every function call. For a library that does that, seegithub.com/go-errors/errors. The intenthere is to attach relevant contextual information (messages, variables) atstrategic places along the call stack, keeping stack traces compact andmaximally useful.

Example Usage

func WriteAll(baseDir string, entities []Entity) error {    err := os.MkdirAll(baseDir, 0755)    if err != nil {        returnstacktrace.Propagate(err, "Failed to create base directory")    }    for _, ent := range entities {        path := filepath.Join(baseDir, fileNameForEntity(ent))        err = Write(path, ent)        if err != nil {            returnstacktrace.Propagate(err, "Failed to write %v to %s", ent, path)        }    }    return nil}

Functions

stacktrace.Propagate(cause error, msg string, vals ...interface{}) error

Propagate wraps an error to include line number information. This is going to beyour most common stacktrace call.

As in all of these functions, themsg andvals work likefmt.Errorf.

The message passed to Propagate should describe the action that failed,resulting incause. The canonical call looks like this:

result, err := process(arg)if err != nil {    return nil,stacktrace.Propagate(err, "Failed to process %v", arg)}

To write the message, ask yourself "what does this call do?" What doesprocess(arg) do? It processes${arg}, so the message is that we failed toprocess ${arg}.

Pay attention that the message is not redundant with the one inerr. In theWriteAll example above, any error fromos.MkdirAll will already contain thepath it failed to create, so it would be redundant to include it again in ourmessage. However, the error fromos.MkdirAll will not identify that path ascorresponding to the "base directory" so we propagate with that information.

If it is not possible to add any useful contextual information beyond what isalready included in an error,msg can be an empty string:

func Something() error {    mutex.Lock()    defer mutex.Unlock()    err := reallySomething()    returnstacktrace.Propagate(err, "")}

The purpose of"" as opposed to a separate function is to make you feel alittle guilty every time you do this.

This example also illustrates the behavior of Propagate whencause is nil– it returns nil as well. There is no need to checkif err != nil.

stacktrace.NewError(msg string, vals ...interface{}) error

NewError is a drop-in replacement forfmt.Errorf that includes line numberinformation. The canonical call looks like this:

if !IsOkay(arg) {    returnstacktrace.NewError("Expected %v to be okay", arg)}

Error Codes

Occasionally it can be useful to propagate an error code while unwinding thestack. For example, a RESTful API may use the error code to set the HTTP statuscode.

The typestacktrace.ErrorCode is a typedef for uint16. You name the set oferror codes relevant to your application.

const (    EcodeManifestNotFound = stacktrace.ErrorCode(iota)    EcodeBadInput    EcodeTimeout)

The special valuestacktrace.NoCode is equal tomath.MaxUint16, so avoidusing that. NoCode is the error code of errors with no code explicitly attached.

An ordinarystacktrace.Propagate preserves the error code of an error.

stacktrace.PropagateWithCode(cause error, code ErrorCode, msg string, vals ...interface{}) error

stacktrace.NewErrorWithCode(code ErrorCode, msg string, vals ...interface{}) error

PropagateWithCode and NewErrorWithCode are analogous to Propagate and NewErrorbut also attach an error code.

_, err := os.Stat(manifestPath)if os.IsNotExist(err) {    returnstacktrace.PropagateWithCode(err, EcodeManifestNotFound, "")}

stacktrace.NewMessageWithCode(code ErrorCode, msg string, vals ...interface{}) error

The error code mechanism can be useful by itself even where stack traces withline numbers are not required. NewMessageWithCode returns an error that printsjust likefmt.Errorf with no line number, but including a code.

ttl := req.URL.Query().Get("ttl")if ttl == "" {    return 0,stacktrace.NewMessageWithCode(EcodeBadInput, "Missing ttl query parameter")}

stacktrace.GetCode(err error) ErrorCode

GetCode extracts the error code from an error.

for i := 0; i < attempts; i++ {    err := Do()    ifstacktrace.GetCode(err) != EcodeTimeout {        return err    }    // try a few more times}return stacktrace.NewError("timed out after %d attempts", attempts)

GetCode returns the special valuestacktrace.NoCode iferr is nil or ifthere is no error code attached toerr.

License

Stacktrace is released by Palantir Technologies, Inc. under the Apache 2.0License. See the includedLICENSE file for details.

Contributing

We welcome contributions of backward-compatible changes to this library.

  • Write your code
  • Add tests for new functionality
  • Rungo test and verify that the tests pass
  • Fill out theIndividual orCorporate Contributor License Agreement and send it toopensource@palantir.com
  • Submit a pull request

Releases

No releases published

Packages

No packages published

Contributors4

  •  
  •  
  •  
  •  

Languages


[8]ページ先頭

©2009-2025 Movatter.jp