geth: better error notification

This commit is contained in:
Victor Farazdagi 2017-01-12 20:56:36 +03:00
parent 1cc8259589
commit 26841d91e7
6 changed files with 59 additions and 5 deletions

View File

@ -1101,6 +1101,11 @@ func startTestNode(t *testing.T) <-chan struct{} {
t.Errorf("cannot unmarshal event's JSON: %s", jsonEvent) t.Errorf("cannot unmarshal event's JSON: %s", jsonEvent)
return return
} }
if envelope.Type == geth.EventNodeCrashed {
geth.TriggerDefaultNodeNotificationHandler(jsonEvent)
return
}
if envelope.Type == geth.EventTransactionQueued { if envelope.Type == geth.EventTransactionQueued {
} }
if envelope.Type == geth.EventNodeStarted { if envelope.Type == geth.EventNodeStarted {

View File

@ -9,6 +9,7 @@ import (
"path/filepath" "path/filepath"
"reflect" "reflect"
"runtime" "runtime"
"runtime/debug"
"strings" "strings"
"syscall" "syscall"
@ -43,6 +44,7 @@ const (
DatabaseCacheSize = 128 // Megabytes of memory allocated to internal caching (min 16MB / database forced) DatabaseCacheSize = 128 // Megabytes of memory allocated to internal caching (min 16MB / database forced)
EventNodeStarted = "node.started" EventNodeStarted = "node.started"
EventNodeCrashed = "node.crashed"
) )
// Gas price settings // Gas price settings
@ -313,5 +315,24 @@ func Fatalf(reason interface{}, args ...interface{}) {
fmt.Fprintf(w, "Fatal Failure: %v\n", reason.(error)) fmt.Fprintf(w, "Fatal Failure: %v\n", reason.(error))
} }
debug.PrintStack()
os.Exit(1) os.Exit(1)
} }
// HaltOnPanic recovers from panic, logs issue, sends upward notification, and exits
func HaltOnPanic() {
if r := recover(); r != nil {
err := fmt.Errorf("%v: %v", ErrNodeStartFailure, r)
// send signal up to native app
SendSignal(SignalEnvelope{
Type: EventNodeCrashed,
Event: NodeCrashEvent{
Error: err.Error(),
},
})
Fatalf(err) // os.exit(1) is called internally
}
}

View File

@ -67,6 +67,8 @@ var (
// CreateAndRunNode creates and starts running Geth node locally (exposing given RPC port along the way) // CreateAndRunNode creates and starts running Geth node locally (exposing given RPC port along the way)
func CreateAndRunNode(dataDir string, rpcPort int) error { func CreateAndRunNode(dataDir string, rpcPort int) error {
defer HaltOnPanic()
nodeManager := NewNodeManager(dataDir, rpcPort) nodeManager := NewNodeManager(dataDir, rpcPort)
if nodeManager.NodeInited() { if nodeManager.NodeInited() {
@ -101,6 +103,8 @@ func NodeManagerInstance() *NodeManager {
// RunNode starts Geth node // RunNode starts Geth node
func (m *NodeManager) RunNode() { func (m *NodeManager) RunNode() {
go func() { go func() {
defer HaltOnPanic()
m.StartNode() m.StartNode()
if _, err := m.AccountManager(); err != nil { if _, err := m.AccountManager(); err != nil {

View File

@ -1,6 +1,8 @@
package geth_test package geth_test
import ( import (
"encoding/json"
"fmt"
"os" "os"
"path/filepath" "path/filepath"
"testing" "testing"
@ -37,6 +39,15 @@ func TestMain(m *testing.M) {
} }
geth.SetDefaultNodeNotificationHandler(func(jsonEvent string) { geth.SetDefaultNodeNotificationHandler(func(jsonEvent string) {
var envelope geth.SignalEnvelope
if err := json.Unmarshal([]byte(jsonEvent), &envelope); err != nil {
panic(fmt.Errorf("cannot unmarshal event's JSON: %s", jsonEvent))
}
if envelope.Type == geth.EventNodeCrashed {
geth.TriggerDefaultNodeNotificationHandler(jsonEvent)
return
}
if jsonEvent == `{"type":"node.started","event":{}}` { if jsonEvent == `{"type":"node.started","event":{}}` {
signalRecieved <- struct{}{} signalRecieved <- struct{}{}
} }

View File

@ -21,6 +21,10 @@ type JSONError struct {
Error string `json:"error"` Error string `json:"error"`
} }
type NodeCrashEvent struct {
Error string `json:"error"`
}
type AddPeerResult struct { type AddPeerResult struct {
Success bool `json:"success"` Success bool `json:"success"`
Error string `json:"error"` Error string `json:"error"`

View File

@ -31,15 +31,19 @@ const (
type NodeNotificationHandler func(jsonEvent string) type NodeNotificationHandler func(jsonEvent string)
var notificationHandler NodeNotificationHandler = func(jsonEvent string) { // internal signal handler (used in tests) var notificationHandler NodeNotificationHandler = TriggerDefaultNodeNotificationHandler
glog.V(logger.Info).Infof("notification received (default notification handler): %s\n", jsonEvent)
}
// SetDefaultNodeNotificationHandler sets notification handler to invoke on SendSignal
func SetDefaultNodeNotificationHandler(fn NodeNotificationHandler) { func SetDefaultNodeNotificationHandler(fn NodeNotificationHandler) {
notificationHandler = fn notificationHandler = fn
} }
// SendSignal sends application signal (JSON, normally) upwards // TriggerDefaultNodeNotificationHandler triggers default notification handler (helpful in tests)
func TriggerDefaultNodeNotificationHandler(jsonEvent string) {
glog.V(logger.Info).Infof("notification received (default notification handler): %s\n", jsonEvent)
}
// SendSignal sends application signal (JSON, normally) upwards to application (via default notification handler)
func SendSignal(signal SignalEnvelope) { func SendSignal(signal SignalEnvelope) {
data, _ := json.Marshal(&signal) data, _ := json.Marshal(&signal)
C.StatusServiceSignalEvent(C.CString(string(data))) C.StatusServiceSignalEvent(C.CString(string(data)))
@ -98,6 +102,8 @@ func PrepareTestNode() (err error) {
return nil return nil
} }
defer HaltOnPanic()
syncRequired := false syncRequired := false
if _, err := os.Stat(filepath.Join(TestDataDir, "testnet")); os.IsNotExist(err) { if _, err := os.Stat(filepath.Join(TestDataDir, "testnet")); os.IsNotExist(err) {
syncRequired = true syncRequired = true
@ -122,7 +128,10 @@ func PrepareTestNode() (err error) {
// start geth node and wait for it to initialize // start geth node and wait for it to initialize
// internally once.Do() is used, so call below is thread-safe // internally once.Do() is used, so call below is thread-safe
CreateAndRunNode(dataDir, 8546) // to avoid conflicts with running react-native app, run on different port err = CreateAndRunNode(dataDir, 8546) // to avoid conflicts with running react-native app, run on different port
if err != nil {
panic(err)
}
manager = NodeManagerInstance() manager = NodeManagerInstance()
if !manager.NodeInited() { if !manager.NodeInited() {