diff --git a/application/transaction.go b/application/transaction.go index 56e33896ec..5a974e15a4 100644 --- a/application/transaction.go +++ b/application/transaction.go @@ -1,6 +1,7 @@ package application import ( + "runtime/debug" "time" "github.com/fabric8-services/fabric8-wit/log" @@ -35,7 +36,7 @@ func Transactional(db DB, todo func(f Application) error) error { go func(tx Transaction) { defer func() { if err := recover(); err != nil { - errorChan <- errors.Errorf("recovered %v", err) + errorChan <- errors.Errorf("recovered %v. stack: %s", err, debug.Stack()) } }() errorChan <- todo(tx) diff --git a/application/transaction_test.go b/application/transaction_test.go index f7a3a2ca02..410c3e4034 100644 --- a/application/transaction_test.go +++ b/application/transaction_test.go @@ -1,6 +1,7 @@ package application_test import ( + "fmt" "testing" "time" @@ -54,3 +55,31 @@ func (test *TestTransaction) TestTransactionOut() { require.Error(test.T(), err) assert.Contains(test.T(), err.Error(), "database transaction timeout!") } + +func (test *TestTransaction) TestTransactionPanicAndRecoverWithStack() { + // then + err := application.Transactional(test.db, func(appl application.Application) error { + bar := func(a, b interface{}) { + // This comparison while legal at compile time will cause a runtime + // error like this: "comparing uncomparable type + // map[string]interface {}". The transaction will panic and recover + // but you will probably never find out where the error came from if + // the stack is not captured in the transaction recovery. This test + // ensures that the stack is captured. + if a == b { + fmt.Printf("never executed") + } + } + foo := func() { + a := map[string]interface{}{} + b := map[string]interface{}{} + bar(a, b) + } + foo() + return nil + }) + // then + require.Error(test.T(), err) + // ensure there's a proper stack trace that contains the name of this test + require.Contains(test.T(), err.Error(), "(*TestTransaction).TestTransactionPanicAndRecoverWithStack.func1(") +}