merry [![Build Status](https://travis-ci.org/ansel1/merry.svg?branch=master)](https://travis-ci.org/ansel1/merry) [![GoDoc](https://godoc.org/github.com/ansel1/merry?status.png)](https://godoc.org/github.com/ansel1/merry) [![Go Report Card](https://goreportcard.com/badge/github.com/ansel1/merry)](https://goreportcard.com/report/github.com/ansel1/merry)
=====

Make your golang errors merry, with stacktraces, inheritance, and arbitrary additional context.
            
The package is largely based on http://github.com/go-errors/errors, with additional
inspiration from https://github.com/go-errgo/errgo and https://github.com/amattn/deeperror.

Installation
------------

    go get github.com/ansel1/merry
    
Features
--------

Merry errors work a lot like google's golang.org/x/net/context package.
Merry errors wrap normal errors with a context of key/value pairs.
Like contexts, merry errors are immutable: adding a key/value to an error
always creates a new error which wraps the original.  

`merry` comes with built-in support for adding information to errors:

* stacktraces
* overriding the error message
* HTTP status codes
* End user error messages
 
You can also add your own additional information.

The stack capturing feature can be turned off for better performance, though it's pretty fast.  Benchmarks
on an early 2011 MacBook Pro, with go 1.7rc1:

    BenchmarkNew_withStackCapture-8      	 1000000	      1413 ns/op
    BenchmarkNew_withoutStackCapture-8   	10000000	       218 ns/op

Details
-------

* New errors have a stacktrace captured where they are created
* Add a stacktrace to existing errors (captured where they are wrapped)

    ```go
    err := lib.Read()
    return merry.Wrap(err)  // no-op if err is already merry
    ```
        
* Allow golang idiom of comparing an err value to an exported value, using `Is()`

    ```go
    var ParseError = merry.New("Parse error")
    
    func Parse() error {
        err := ParseError.Here() // captures a stacktrace here
        merry.Is(err, ParseError)  // instead of err == ParseError
    }
    ```
        
* Change the message on an error, while still using `Is()` to compare to the original error

    ```go
    err := merry.WithMessage(ParseError, "Bad input")
    merry.Is(err, ParseError) // yes it is
    ```
        
* `Is()` supports hierarchies of errors

    ```go
    var ParseError = merry.New("Parse error")
    var InvalidCharSet = merry.WithMessage(ParseError, "Invalid char set")
    var InvalidSyntax = merry.WithMessage(ParseError, "Invalid syntax")
    
    func Parse(s string) error {
        // use chainable methods to add context
        return InvalidCharSet.Here().WithMessagef("Invalid char set: %s", "UTF-8")
        // or functions
        // return merry.WithMessagef(merry.Here(InvalidCharSet), "Invalid char set: %s", "UTF-8")
    }
    
    func Check() {
        err := Parse("fields")
        merry.Is(err, ParseError) // yup
        merry.Is(err, InvalidCharSet) // yup
        merry.Is(err, InvalidSyntax) // nope
    }
    ```
        
* Add an HTTP status code

    ```go
    merry.HTTPCode(errors.New("regular error")) // 500
    merry.HTTPCode(merry.New("merry error").WithHTTPCode(404)) // 404
    ```

* Set an alternate error message for end users
 
    ```go
    e := merry.New("crash").WithUserMessage("nothing to see here")
    merry.UserMessage(e)  // returns "nothing to see here"
    ```
        
* Functions for printing error details
 
    ```go
    err := merry.New("boom")
    m := merry.Stacktrace(err) // just the stacktrace
    m = merry.Details(err) // error message and stacktrace
    fmt.Sprintf("%+v", err) == merry.Details(err) // errors implement fmt.Formatter
    ```
   
* Add your own context info

    ```go
    err := merry.New("boom").WithValue("explosive", "black powder")
    ```
    
Basic Usage
-----------

The package contains functions for creating new errors with stacks, or adding a stack to `error` 
instances.  Functions with add context (e.g. `WithValue()`) work on any `error`, and will 
automatically convert them to merry errors (with a stack) if necessary.

Capturing the stack can be globally disabled with `SetStackCaptureEnabled(false)`

Functions which get context values from errors also accept `error`, and will return default
values if the error is not merry, or doesn't have that key attached.

All the functions which create or attach context return concrete instances of `*Error`.  `*Error`
implements methods to add context to the error (they mirror the functions and do
the same thing).  They allow for a chainable syntax for adding context.

Example:

```go
package main

import (
    "github.com/ansel1/merry"
    "errors"
)

var InvalidInputs = errors.New("Input is invalid")

func main() {
    // create a new error, with a stacktrace attached
    err := merry.New("bad stuff happened")
    
    // create a new error with format string, like fmt.Errorf
    err = merry.Errorf("bad input: %v", os.Args)
    
    // capture a fresh stacktrace from this callsite
    err = merry.Here(InvalidInputs)
    
    // Make err merry if it wasn't already.  The stacktrace will be captured here if the
    // error didn't already have one.  Also useful to cast to *Error 
    err = merry.Wrap(err, 0)

    // override the original error's message
    err.WithMessagef("Input is invalid: %v", os.Args)
    
    // Use Is to compare errors against values, which is a common golang idiom
    merry.Is(err, InvalidInputs) // will be true
    
    // associated an http code
    err.WithHTTPCode(400)
    
    perr := parser.Parse("blah")
    err = Wrap(perr, 0)
    // Get the original error back
    merry.Unwrap(err) == perr  // will be true
    
    // Print the error to a string, with the stacktrace, if it has one
    s := merry.Details(err)
    
    // Just print the stacktrace (empty string if err is not a RichError)
    s := merry.Stacktrace(err)

    // Get the location of the error (the first line in the stacktrace)
    file, line := merry.Location(err)
    
    // Get an HTTP status code for an error.  Defaults to 500 for non-nil errors, and 200 if err is nil.
    code := merry.HTTPCode(err)
    
}
```
    
See inline docs for more details.

License
-------

This package is licensed under the MIT license, see LICENSE.MIT for details.
