Skip to content

Commit

Permalink
Merge 6f28374 into 193de0d
Browse files Browse the repository at this point in the history
  • Loading branch information
donutloop committed Jan 20, 2020
2 parents 193de0d + 6f28374 commit b5d31dc
Show file tree
Hide file tree
Showing 3 changed files with 108 additions and 0 deletions.
20 changes: 20 additions & 0 deletions xpanic/README.md
@@ -0,0 +1,20 @@
# Usage

It's a powerful panic handler to simplify reasoning of panics

## Example
```go
package main

import (
"github.com/donutloop/toolkit/xpanic"
"log"
)

func main() {
logF := func(format string, args ...interface{}) { log.Println(fmt.Sprintf(format, args...)) }
panicHandler := BuildPanicHandler(logF, xpanic.CrashOnErrorDeactivated)
defer panicHandler
panic("hello world")
}
```
59 changes: 59 additions & 0 deletions xpanic/handlepanic.go
@@ -0,0 +1,59 @@
package xpanic

import (
"bytes"
"fmt"
"os/signal"
"runtime/pprof"
"strings"
"syscall"
)

// If crashOnError is set a coredump will be produced and kill program else it continues.
const (
CrashOnErrorActivated = true
CrashOnErrorDeactivated = false
)

// BuildPanicHandler builds a panic handler and verifies a none nil logger got passed
func BuildPanicHandler(errorf func(format string, args ...interface{}), crashOnError bool) func() {
if errorf == nil {
panic("errorf is not set")
}
// handlePanic writes a message to the logger and causes a backtrace to be produced.
return func() {
r := recover()
if r != nil {
errorf("capture panic infos")

errorf(fmt.Sprintf("panic: %s", r))
Backtrace(errorf)

if crashOnError {
signal.Reset(syscall.SIGABRT)
errorf("finished capturing of panic infos")
syscall.Kill(0, syscall.SIGABRT)
} else {
errorf("finished capturing of panic infos")
}
}
}
}

// Backtrace writes a multi-line backtrace to the logger.
func Backtrace(errorf func(format string, args ...interface{})) {
profiles := pprof.Profiles()
buf := new(bytes.Buffer)

for _, p := range profiles {
// https://golang.org/pkg/runtime/pprof/#Profile.WriteTo.
err := pprof.Lookup(p.Name()).WriteTo(buf, 2)
if err != nil {
errorf("could not write profile: %v", err)
}
}

for _, line := range strings.Split(buf.String(), "\n") {
errorf(line)
}
}
29 changes: 29 additions & 0 deletions xpanic/handlepanic_test.go
@@ -0,0 +1,29 @@
package xpanic_test

import (
"bytes"
"fmt"
"github.com/donutloop/toolkit/xpanic"
"sync"
"testing"
)

func TestHandlePanic(t *testing.T) {

var buff bytes.Buffer
panicHandler := xpanic.BuildPanicHandler(func(format string, args ...interface{}) { buff.WriteString(fmt.Sprintf(format, args...)) }, xpanic.CrashOnErrorDeactivated)

var wait sync.WaitGroup
wait.Add(1)
go func() {
defer panicHandler()
defer wait.Done()
panic("hello world")
}()

wait.Wait()

if buff.Len() == 0 {
t.Fatal("buff is empty")
}
}

0 comments on commit b5d31dc

Please sign in to comment.