109 lines
3.7 KiB
Markdown
109 lines
3.7 KiB
Markdown
# 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.
|