terror

package module
v0.0.0-...-5dc5466 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 3, 2023 License: MIT Imports: 6 Imported by: 0

README

TError

Documentation

Overview

Package terror is an error-handling library.

Errors wrapped by this package include the code location that the error was wrapped at. They print detailed stack information when formatted via the "%v" printf directive. Otherwise, they print only the message prefix.

That is, given:

err := terror.New("some error")
err = terror.Wrap(err, "loading config")
err = terror.Wrap(err, "initializing %s", "system")

Then

fmt.Println(err)
fmt.Println(err.Error())
fmt.Printf("%s", err)

will each print

initializing system: loading config: some error

But

fmt.Printf("%v", err)
fmt.Printf("%+v", err)
fmt.Printf("%#v", err)

will print

initializing system
 --- at path/to/my/pkg/system.go:123 (system.Initialize) ---
caused by loading config
 --- at path/to/my/pkg/config.go:37 (system.LoadConfig) ---
caused by some error
 --- at path/to/my/pkg/config.go:62 (system.ReadConfig) ---

Error formatting notes

Most error libraries that include call site information have settled on only printing stack details when the error is formatted via "%+v". However, much existing code at Tanium only logs error using "%v". We therefore strongly encourage users to use "%+v" when logging errors and wanting to show detailed stack information, but we will support "%v" until a satisfactory auditing mechanism can be achieved.

Using fmt.Errorf to wrap an error commits to using the detailed format.

This library is inspired by github.com/palantir/stacktrace.

Example
package main

import (
	"fmt"
	"os"

	"github.com/Tanium-OSS/terror"
)

type Config struct{}

func ReadConfig(filename string) (Config, error) {
	_, err := os.ReadFile(filename)
	return Config{}, terror.WrapWithCode(err, 123, "loading config")
}

type System struct{}

func InitializeSystem(configPath string) (System, error) {
	_, err := ReadConfig(configPath)
	return System{}, terror.Wrap(err, "initializing system")
}

func main() {
	_, err := InitializeSystem("oops where mah bucket?")
	fmt.Println(err)         // prints stack details
	fmt.Println(err.Error()) // prints short message only
	fmt.Printf("%s\n", err)  // prints short message only
	fmt.Printf("%+v\n", err) // prints stack details
	fmt.Printf("Error code: %d\n", terror.GetCode(err))
}
Output:

initializing system
 --- at terror/example_test.go:21 (InitializeSystem) ---
caused by loading config
 --- at terror/example_test.go:14 (ReadConfig) ---
caused by open oops where mah bucket?: no such file or directory
initializing system: loading config: open oops where mah bucket?: no such file or directory
initializing system: loading config: open oops where mah bucket?: no such file or directory
initializing system
 --- at terror/example_test.go:21 (InitializeSystem) ---
caused by loading config
 --- at terror/example_test.go:14 (ReadConfig) ---
caused by open oops where mah bucket?: no such file or directory
Error code: 123

Index

Examples

Constants

This section is empty.

Variables

View Source
var CleanFileName = func(filename string) string {

	return filename
}

CleanFileName is a process global hook that enables sanitizing filenames in stack traces.

Functions

func Annotate

func Annotate(err error) error

Annotate annotates the provided error with the file and line of the call. If err is nil, Annotate returns nil.

func CloseAndAppendOnError

func CloseAndAppendOnError(pErr *error, c io.Closer, format string, args ...interface{})

CloseAndAppendOnError is a helper function that makes it easier to close Closers in defer statements and still capture any errors that arise during closing. Use this function when a failure to Close() indicates that something has gone wrong and it should not be ignored. For example, when writing to a file, a failure to Close() the file may indicate that not all of the bytes were in fact written, even if the call to Write() did not return an error. This should always be used with named return variables. See https://play.golang.org/p/ECMc6EfWXxt for examples of what can go wrong if a local variable is used instead.

Example
errExample := func() (err error) {
	file := allocateExampleResource()
	defer CloseAndAppendOnError(&err, file, "close file")

	if err := file.Use(); err != nil {
		return Wrap(err, "first use")
	}
	if err := file.Use(); err != nil {
		return Wrap(err, "second use")
	}
	return nil
}()

fmt.Println(errExample)
Output:

first use: could not use; close file: could not close

func CloseAndLogOnError

func CloseAndLogOnError(fn func(template string, args ...interface{}), closers ...io.Closer)

CloseAndLogOnError is a helper function that makes it easier to close Closers in defer statements and still log any errors that arise during closing. Use this function when closing closers where a failure to Close() can happen and the errors cannot be handled.

func GetCode

func GetCode(err error) int

GetCode returns the error code most recently added via WrapWithCode or NewWithCode. If no error code is present or the error is not created by this package, 0 is returned.

func New

func New(format string, args ...interface{}) error

New creates an error with the specified message as well as the location of this call. This is a drop-in replacement for fmt.Errorf.

func NewWithCode

func NewWithCode(code int, format string, args ...interface{}) error

NewWithCode creates an error with the specified message as well as the location of this call and a specified error code. The error code can be retrieved using GetCode().

func Wrap

func Wrap(err error, format string, args ...interface{}) error

Wrap annotates the provided error with the file and line of the call along with the provided message. The format and args are formatted printf style. If err is nil, Wrap returns nil.

func WrapInto

func WrapInto(pErr *error, format string, args ...interface{})

WrapInto annotates the provided error with the file and line of the call along with the provided message. The format and args are formatted printf style. If * pErr is nil, WrapInto is a no-op.

The intended use is to use defer with a named return variable to provide additional context. If deferred, it will capture the line of the return statement, not the line of the defer statement.

Example
checkComputer := func() error {
	return nil
}

getQuestionForAnswer := func(answer string) (question string, err error) {
	// Set up error handling for this function.
	defer WrapInto(&err, "getQuestionForAnswer(%q)", answer)

	err = checkComputer()
	if err != nil {
		// Can also call Wrap here, if there is additional context to provide for this one error.
		return "", err
	}

	// Can defer another WrapInto, if there is additional context to provide for all subsequent errors.

	if answer == "42" {
		return "What is the answer to the ultimate question of life, the universe, and everything?", nil
	}
	return "", New("insufficient cheese")
}

_, errExample := getQuestionForAnswer("five tons of flax")

fmt.Println(errExample.Error())
Output:

getQuestionForAnswer("five tons of flax"): insufficient cheese

func WrapWithCode

func WrapWithCode(err error, code int, format string, args ...interface{}) error

WrapWithCode annotates the provided error with the file and line of the call along with the provided message and a specified error code. The error code can be retrieved using GetCode(). The format and args are formatted printf style. If err is nil, Wrap returns nil.

Types

type Const

type Const string

Const defines a type of error without capturing the line where it was defined. To use it in a function, call terror.Wrap func to capture the line where it is being wrapped, within the function. It can be returned directly to improve performance in tight loops if necessary. Compared to combining errors.New, it can be assigned to a const.

Example
package main

import (
	"fmt"
)

const errTestError = Const("test error")

func main() {
	err := Wrap(errTestError, "in test %q", "ExampleConstError")
	fmt.Println(err.Error())
}
Output:

in test "ExampleConstError": test error

func (Const) Error

func (err Const) Error() string

Error implements the conventional interface for representing an error condition.

type Error

type Error interface {
	error
	Location() Location
}

Error represents the terror-wrapped error which includes additional information such as the stack trace.

func RootError

func RootError(e error) Error

RootError returns the innermost terror-wrapped Error. If this error does not contain any terror-wrapped error, nil will be returned. This is contrasted with calling errors.As(...) which returns the outermost layer.

type Location

type Location struct {
	File     string
	Line     int
	Function string
}

Location describes a single stack frame position.

func (Location) String

func (l Location) String() string

String returns a string representation of the Location.

type TError

type TError struct {
	// contains filtered or unexported fields
}

TError is the wrapped error implementation.

func (TError) Error

func (e TError) Error() string

Error returns the simple, compact, one-line error format.

func (TError) Format

func (e TError) Format(f fmt.State, c rune)

Format implements fmt.Formatter so that we know when we're being formatted by a Printf-style func. This detects when we're being printed specifically by "%v" in which case we output the detailed stack trace.

func (TError) Location

func (e TError) Location() Location

Location returns a string representation of the tError after cleaning file and function names.

func (TError) Unwrap

func (e TError) Unwrap() error

Unwrap returns the base error, implementing the go1.13 error unwrapping to support errors.Is and errors.As.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL