Codebase list golang-github-mitchellh-panicwrap / HEAD
HEAD

Tree @HEAD (Download .tar.gz)

# panicwrap

panicwrap is a Go library that re-executes a Go binary and monitors stderr
output from the binary for a panic. When it finds a panic, it executes a
user-defined handler function. Stdout, stderr, stdin, signals, and exit
codes continue to work as normal, making the existence of panicwrap mostly
invisible to the end user until a panic actually occurs.

Since a panic is truly a bug in the program meant to crash the runtime,
globally catching panics within Go applications is not supposed to be possible.
Despite this, it is often useful to have a way to know when panics occur.
panicwrap allows you to do something with these panics, such as writing them
to a file, so that you can track when panics occur.

panicwrap is ***not a panic recovery system***. Panics indicate serious
problems with your application and _should_ crash the runtime. panicwrap
is just meant as a way to monitor for panics. If you still think this is
the worst idea ever, read the section below on why.

## Features

* **SIMPLE!**
* Works with all Go applications on all platforms Go supports
* Custom behavior when a panic occurs
* Stdout, stderr, stdin, exit codes, and signals continue to work as
  expected.

## Usage

Using panicwrap is simple. It behaves a lot like `fork`, if you know
how that works. A basic example is shown below.

Because it would be sad to panic while capturing a panic, it is recommended
that the handler functions for panicwrap remain relatively simple and well
tested. panicwrap itself contains many tests.

```go
package main

import (
	"fmt"
	"github.com/mitchellh/panicwrap"
	"os"
)

func main() {
	exitStatus, err := panicwrap.BasicWrap(panicHandler)
	if err != nil {
		// Something went wrong setting up the panic wrapper. Unlikely,
		// but possible.
		panic(err)
	}

	// If exitStatus >= 0, then we're the parent process and the panicwrap
	// re-executed ourselves and completed. Just exit with the proper status.
	if exitStatus >= 0 {
		os.Exit(exitStatus)
	}

	// Otherwise, exitStatus < 0 means we're the child. Continue executing as
	// normal...

	// Let's say we panic
	panic("oh shucks")
}

func panicHandler(output string) {
	// output contains the full output (including stack traces) of the
	// panic. Put it in a file or something.
	fmt.Printf("The child panicked:\n\n%s\n", output)
	os.Exit(1)
}
```

## How Does it Work?

panicwrap works by re-executing the running program (retaining arguments,
environmental variables, etc.) and monitoring the stderr of the program.
Since Go always outputs panics in a predictable way with a predictable
exit code, panicwrap is able to reliably detect panics and allow the parent
process to handle them.

## WHY?! Panics should CRASH!

Yes, panics _should_ crash. They are 100% always indicative of bugs and having
information on a production server or application as to what caused the panic is critical.

### User Facing

In user-facing programs (programs like
[Packer](http://github.com/mitchellh/packer) or
[Docker](http://github.com/dotcloud/docker)), it is up to the user to
report such panics. This is unreliable, at best, and it would be better if the
program could have a way to automatically report panics. panicwrap provides
a way to do this.

### Server

For backend applications, it is easier to detect crashes (since the application exits) 
and having an idea as to why the crash occurs is equally important; 
particularly on a production server. 

At [HashiCorp](http://www.hashicorp.com),
we use panicwrap to log panics to timestamped files with some additional
data (configuration settings at the time, environmental variables, etc.)

The goal of panicwrap is _not_ to hide panics. It is instead to provide
a clean mechanism for capturing them and ultimately crashing.