# errxer

Command to generate interface wrappers to ensures all returned errors have their stacktrace attached for debugging.
With `--timings` can also provide call durations for tracking.


## Install

```bash
go get code.justin.tv/devrel/errxer/cmd/errxer
```

NOTE: To update, `go get -u` doesn't seem to work with this path. You may have to cd into `$GOPATH/src/code.justin.tv/devrel/errxer`, git pull and then `go install cmd/errxer` to update.

## Usage

Given an interface in your code:

```go
type MyStuff interface {
	DoStuff(ctx context.Context) (string, error)
}
```

You can generate a wrapper to wrap returned errors with `errx`:

```go
// ... Generated with errxer

type MyStuffErrx struct {
	MyStuff MyStuff
}

func (e *MyStuffErrx) DoStuff(ctx context.Context) (string, error) {
	r0, err := e.MyStuff.DoStuff(ctx)
	return r0, errx.NewWithSkip(err, 1)
}
```

Use wrapper in code:

```go
impl := MyStuffImplentation{}
errxWrap := MyStuffErrx{
    MyStuff: impl,
}
str, err := errxWrap.DoStuff(0) // err has stacktrace
```

### Generate from interfaces in your package

```bash
# cd to folder with interface
> errxer MyStuff
Generated errxer wrapper for "MyStuff"
```

### Generate from interfaces in other packages

```bash
# cd to folder where you want to place the generated file
> errxer code.justin.tv/chat/db.DB
Generated errxer wrapper for "code.justin.tv/chat/db.DB"
```

### Generate with Timings (call duration tracking)

Use the `--timings` flag to track the duration of each call.

```bash
> errxer --timing MyStuff
```

Adds a new field to the wrapper that can be used to track stats:

```go
// ... Generated with errxer

type MyStuffErrx struct {
	MyStuff MyStuff
	TimingFunc func(ctx context.Context, d time.Duration, method string, err error)
}

func (e *MyStuffErrx) DoStuff(ctx context.Context) (string, error) {
	start := time.Now()
	r0, err := e.MyStuff.DoStuff(ctx)
	if e.TimingFunc {
		e.TimingFunc(ctx, time.Since(start), "DoStuff", err)
	}
	return r0, errx.NewWithSkip(err, 1)
}
```

Configure the wrapper to track the durations, for example with statsd:

```go
func trackTimings(ctx context.Context, d time.Duration, method string, err error) {
	status := "success"
	if err != nil {
		status = "error"
	}
	statName := fmt.Sprintf("mystuff.%s.%s", method, status)
	_ = stats.TimingDuration(statName, d, 1.0)
}

impl := MyStuffImplentation{}
errxWrap := MyStuffErrx{
    MyStuff: impl,
    TimingFunc: trackTimings,
}
str, err := errxWrap.DoStuff(0) // tracks timings on "mystuff.DoStuff.{success|error}"
```

## Features

- [x] In package generation
- [x] External package generation
- [x] Timings tracking, configurable with a TimingsFunc
- [ ] Recursive same-package return interface generation
- [ ] Support input and output locations
- [ ] Configuration for including interface function parameter values as errx.Fields
