[NOD-806] Exit early after panic (#650)

* [NOD-806] After panic, gracefully stop logs, and then exit immediately

* [NOD-806] Convert non-kaspad applications to use the new spawn

* [NOD-806] Fix disabled log at rpcclient

* [NOD-806] Refactor HandlePanic

* [NOD-806] Cancel Logger interface

* [NOD-806] Remove redundant spawn checks from waitgroup_test.go

* [NOD-806] Use caller subsystem when logging panics

* [NOD-806] Fix go vet errors
This commit is contained in:
Ori Newman 2020-03-08 11:24:37 +02:00 committed by GitHub
parent 606cd668ff
commit 5a99e4d2f3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 84 additions and 261 deletions

View File

@ -717,11 +717,7 @@ func (a *AddrManager) Start() {
// Start the address ticker to save addresses periodically.
a.wg.Add(1)
spawn(a.addressHandler, a.handlePanic)
}
func (a *AddrManager) handlePanic() {
atomic.AddInt32(&a.shutdown, 1)
spawn(a.addressHandler)
}
// Stop gracefully shuts down the address manager by stopping the main handler.

View File

@ -10,4 +10,4 @@ import (
)
var log, _ = logger.Get(logger.SubsystemTags.ADXR)
var spawn = panics.GoroutineWrapperFuncWithPanicHandler(log)
var spawn = panics.GoroutineWrapperFunc(log)

View File

@ -23,7 +23,7 @@ const (
var (
cfg *ConfigFlags
log logs.Logger
log *logs.Logger
spawn func(func())
)

View File

@ -28,7 +28,5 @@ func initLog(logFile, errLogFile string) {
}
func enableRPCLogging() {
rpclog := backendLog.Logger("RPCC")
rpclog.SetLevel(logs.LevelTrace)
rpcclient.UseLogger(rpclog)
rpcclient.UseLogger(backendLog, logs.LevelTrace)
}

View File

@ -12,7 +12,7 @@ import (
)
func main() {
defer panics.HandlePanic(log, nil, nil)
defer panics.HandlePanic(log, nil)
interrupt := signal.InterruptListener()
cfg, err := parseConfig()

View File

@ -234,7 +234,7 @@ func (cm *ConnManager) handleFailedConn(c *ConnReq, err error) {
}
spawnAfter(d, func() {
cm.Connect(c)
}, nil)
})
} else if cm.cfg.GetNewAddress != nil {
cm.failedAttempts++
if cm.failedAttempts >= maxFailedAttempts {
@ -243,9 +243,9 @@ func (cm *ConnManager) handleFailedConn(c *ConnReq, err error) {
"-- retrying further connections every %s", maxFailedAttempts,
cm.cfg.RetryDuration)
}
spawnAfter(cm.cfg.RetryDuration, cm.NewConnReq, cm.handlePanic)
spawnAfter(cm.cfg.RetryDuration, cm.NewConnReq)
} else {
spawn(cm.NewConnReq, cm.handlePanic)
spawn(cm.NewConnReq)
}
}
}
@ -377,7 +377,7 @@ out:
if cm.cfg.OnDisconnection != nil {
spawn(func() {
cm.cfg.OnDisconnection(connReq)
}, cm.handlePanic)
})
}
// All internal state has been cleaned up, if
@ -576,7 +576,7 @@ func (cm *ConnManager) listenHandler(listener net.Listener) {
}
spawn(func() {
cm.cfg.OnAccept(conn)
}, cm.handlePanic)
})
}
cm.wg.Done()
@ -592,7 +592,7 @@ func (cm *ConnManager) Start() {
log.Trace("Connection manager started")
cm.wg.Add(1)
spawn(cm.connHandler, cm.handlePanic)
spawn(cm.connHandler)
// Start all the listeners so long as the caller requested them and
// provided a callback to be invoked when connections are accepted.
@ -604,12 +604,12 @@ func (cm *ConnManager) Start() {
cm.wg.Add(1)
spawn(func() {
cm.listenHandler(listenerCopy)
}, cm.handlePanic)
})
}
}
for i := atomic.LoadUint64(&cm.connReqCount); i < uint64(cm.cfg.TargetOutbound); i++ {
spawn(cm.NewConnReq, cm.handlePanic)
spawn(cm.NewConnReq)
}
}
@ -618,10 +618,6 @@ func (cm *ConnManager) Wait() {
cm.wg.Wait()
}
func (cm *ConnManager) handlePanic() {
atomic.AddInt32(&cm.stop, 1)
}
// Stop gracefully shuts down the connection manager.
func (cm *ConnManager) Stop() {
if atomic.AddInt32(&cm.stop, 1) != 1 {

View File

@ -10,5 +10,5 @@ import (
)
var log, _ = logger.Get(logger.SubsystemTags.CMGR)
var spawn = panics.GoroutineWrapperFuncWithPanicHandler(log)
var spawnAfter = panics.AfterFuncWrapperFuncWithPanicHandler(log)
var spawn = panics.GoroutineWrapperFunc(log)
var spawnAfter = panics.AfterFuncWrapperFunc(log)

View File

@ -94,6 +94,6 @@ func SeedFromDNS(dagParams *dagconfig.Params, reqServices wire.ServiceFlag, incl
}
seedFn(addresses)
}, nil)
})
}
}

View File

@ -24,7 +24,7 @@ const (
)
var (
log logs.Logger
log *logs.Logger
spawn func(func())
shutdownChannel = make(chan error)
)

View File

@ -59,7 +59,7 @@ func kaspadMain(serverChan chan<- *server.Server) error {
return err
}
cfg = config.ActiveConfig()
defer panics.HandlePanic(kasdLog, nil, nil)
defer panics.HandlePanic(kasdLog, nil)
// Get a channel that will be closed when a shutdown signal has been
// triggered either from an OS signal such as SIGINT (Ctrl+C) or from

View File

@ -87,7 +87,7 @@ var SubsystemTags = struct {
}
// subsystemLoggers maps each subsystem identifier to its associated logger.
var subsystemLoggers = map[string]logs.Logger{
var subsystemLoggers = map[string]*logs.Logger{
SubsystemTags.ADXR: adxrLog,
SubsystemTags.AMGR: amgrLog,
SubsystemTags.CMGR: cmgrLog,
@ -180,7 +180,7 @@ func SupportedSubsystems() []string {
}
// Get returns a logger of a specific sub system
func Get(tag string) (logger logs.Logger, ok bool) {
func Get(tag string) (logger *logs.Logger, ok bool) {
logger, ok = subsystemLoggers[tag]
return
}

View File

@ -1,64 +0,0 @@
// Copyright (c) 2013-2017 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package logs
// Logger is an interface which describes a level-based logger. A default
// implementation of Logger is implemented by this package and can be created
// by calling (*Backend).Logger.
type Logger interface {
// Tracef formats message according to format specifier and writes to
// to log with LevelTrace.
Tracef(format string, params ...interface{})
// Debugf formats message according to format specifier and writes to
// log with LevelDebug.
Debugf(format string, params ...interface{})
// Infof formats message according to format specifier and writes to
// log with LevelInfo.
Infof(format string, params ...interface{})
// Warnf formats message according to format specifier and writes to
// to log with LevelWarn.
Warnf(format string, params ...interface{})
// Errorf formats message according to format specifier and writes to
// to log with LevelError.
Errorf(format string, params ...interface{})
// Criticalf formats message according to format specifier and writes to
// log with LevelCritical.
Criticalf(format string, params ...interface{})
// Trace formats message using the default formats for its operands
// and writes to log with LevelTrace.
Trace(v ...interface{})
// Debug formats message using the default formats for its operands
// and writes to log with LevelDebug.
Debug(v ...interface{})
// Info formats message using the default formats for its operands
// and writes to log with LevelInfo.
Info(v ...interface{})
// Warn formats message using the default formats for its operands
// and writes to log with LevelWarn.
Warn(v ...interface{})
// Error formats message using the default formats for its operands
// and writes to log with LevelError.
Error(v ...interface{})
// Critical formats message using the default formats for its operands
// and writes to log with LevelCritical.
Critical(v ...interface{})
// Level returns the current logging level.
Level() Level
// SetLevel changes the logging level to the passed level.
SetLevel(level Level)
}

View File

@ -354,12 +354,12 @@ func (b *Backend) Close() {
// Logger returns a new logger for a particular subsystem that writes to the
// Backend b. A tag describes the subsystem and is included in all log
// messages. The logger uses the info verbosity level by default.
func (b *Backend) Logger(subsystemTag string) Logger {
return &slog{LevelInfo, subsystemTag, b}
func (b *Backend) Logger(subsystemTag string) *Logger {
return &Logger{LevelInfo, subsystemTag, b}
}
// slog is a subsystem logger for a Backend. Implements the Logger interface.
type slog struct {
// Logger is a subsystem logger for a Backend.
type Logger struct {
lvl Level // atomic
tag string
b *Backend
@ -367,9 +367,7 @@ type slog struct {
// Trace formats message using the default formats for its operands, prepends
// the prefix as necessary, and writes to log with LevelTrace.
//
// This is part of the Logger interface implementation.
func (l *slog) Trace(args ...interface{}) {
func (l *Logger) Trace(args ...interface{}) {
lvl := l.Level()
if lvl <= LevelTrace {
l.b.print(LevelTrace, l.tag, args...)
@ -378,9 +376,7 @@ func (l *slog) Trace(args ...interface{}) {
// Tracef formats message according to format specifier, prepends the prefix as
// necessary, and writes to log with LevelTrace.
//
// This is part of the Logger interface implementation.
func (l *slog) Tracef(format string, args ...interface{}) {
func (l *Logger) Tracef(format string, args ...interface{}) {
lvl := l.Level()
if lvl <= LevelTrace {
l.b.printf(LevelTrace, l.tag, format, args...)
@ -389,9 +385,7 @@ func (l *slog) Tracef(format string, args ...interface{}) {
// Debug formats message using the default formats for its operands, prepends
// the prefix as necessary, and writes to log with LevelDebug.
//
// This is part of the Logger interface implementation.
func (l *slog) Debug(args ...interface{}) {
func (l *Logger) Debug(args ...interface{}) {
lvl := l.Level()
if lvl <= LevelDebug {
l.b.print(LevelDebug, l.tag, args...)
@ -400,9 +394,7 @@ func (l *slog) Debug(args ...interface{}) {
// Debugf formats message according to format specifier, prepends the prefix as
// necessary, and writes to log with LevelDebug.
//
// This is part of the Logger interface implementation.
func (l *slog) Debugf(format string, args ...interface{}) {
func (l *Logger) Debugf(format string, args ...interface{}) {
lvl := l.Level()
if lvl <= LevelDebug {
l.b.printf(LevelDebug, l.tag, format, args...)
@ -411,9 +403,7 @@ func (l *slog) Debugf(format string, args ...interface{}) {
// Info formats message using the default formats for its operands, prepends
// the prefix as necessary, and writes to log with LevelInfo.
//
// This is part of the Logger interface implementation.
func (l *slog) Info(args ...interface{}) {
func (l *Logger) Info(args ...interface{}) {
lvl := l.Level()
if lvl <= LevelInfo {
l.b.print(LevelInfo, l.tag, args...)
@ -422,9 +412,7 @@ func (l *slog) Info(args ...interface{}) {
// Infof formats message according to format specifier, prepends the prefix as
// necessary, and writes to log with LevelInfo.
//
// This is part of the Logger interface implementation.
func (l *slog) Infof(format string, args ...interface{}) {
func (l *Logger) Infof(format string, args ...interface{}) {
lvl := l.Level()
if lvl <= LevelInfo {
l.b.printf(LevelInfo, l.tag, format, args...)
@ -433,9 +421,7 @@ func (l *slog) Infof(format string, args ...interface{}) {
// Warn formats message using the default formats for its operands, prepends
// the prefix as necessary, and writes to log with LevelWarn.
//
// This is part of the Logger interface implementation.
func (l *slog) Warn(args ...interface{}) {
func (l *Logger) Warn(args ...interface{}) {
lvl := l.Level()
if lvl <= LevelWarn {
l.b.print(LevelWarn, l.tag, args...)
@ -444,9 +430,7 @@ func (l *slog) Warn(args ...interface{}) {
// Warnf formats message according to format specifier, prepends the prefix as
// necessary, and writes to log with LevelWarn.
//
// This is part of the Logger interface implementation.
func (l *slog) Warnf(format string, args ...interface{}) {
func (l *Logger) Warnf(format string, args ...interface{}) {
lvl := l.Level()
if lvl <= LevelWarn {
l.b.printf(LevelWarn, l.tag, format, args...)
@ -455,9 +439,7 @@ func (l *slog) Warnf(format string, args ...interface{}) {
// Error formats message using the default formats for its operands, prepends
// the prefix as necessary, and writes to log with LevelError.
//
// This is part of the Logger interface implementation.
func (l *slog) Error(args ...interface{}) {
func (l *Logger) Error(args ...interface{}) {
lvl := l.Level()
if lvl <= LevelError {
l.b.print(LevelError, l.tag, args...)
@ -466,9 +448,7 @@ func (l *slog) Error(args ...interface{}) {
// Errorf formats message according to format specifier, prepends the prefix as
// necessary, and writes to log with LevelError.
//
// This is part of the Logger interface implementation.
func (l *slog) Errorf(format string, args ...interface{}) {
func (l *Logger) Errorf(format string, args ...interface{}) {
lvl := l.Level()
if lvl <= LevelError {
l.b.printf(LevelError, l.tag, format, args...)
@ -477,9 +457,7 @@ func (l *slog) Errorf(format string, args ...interface{}) {
// Critical formats message using the default formats for its operands, prepends
// the prefix as necessary, and writes to log with LevelCritical.
//
// This is part of the Logger interface implementation.
func (l *slog) Critical(args ...interface{}) {
func (l *Logger) Critical(args ...interface{}) {
lvl := l.Level()
if lvl <= LevelCritical {
l.b.print(LevelCritical, l.tag, args...)
@ -488,9 +466,7 @@ func (l *slog) Critical(args ...interface{}) {
// Criticalf formats message according to format specifier, prepends the prefix
// as necessary, and writes to log with LevelCritical.
//
// This is part of the Logger interface implementation.
func (l *slog) Criticalf(format string, args ...interface{}) {
func (l *Logger) Criticalf(format string, args ...interface{}) {
lvl := l.Level()
if lvl <= LevelCritical {
l.b.printf(LevelCritical, l.tag, format, args...)
@ -498,22 +474,16 @@ func (l *slog) Criticalf(format string, args ...interface{}) {
}
// Level returns the current logging level
//
// This is part of the Logger interface implementation.
func (l *slog) Level() Level {
func (l *Logger) Level() Level {
return Level(atomic.LoadUint32((*uint32)(&l.lvl)))
}
// SetLevel changes the logging level to the passed level.
//
// This is part of the Logger interface implementation.
func (l *slog) SetLevel(level Level) {
func (l *Logger) SetLevel(level Level) {
atomic.StoreUint32((*uint32)(&l.lvl), uint32(level))
}
// Disabled is a Logger that will never output anything.
var Disabled Logger
func init() {
Disabled = &slog{lvl: LevelOff, b: NewBackend()}
// Backend returns the log backend
func (l *Logger) Backend() *Backend {
return l.b
}

View File

@ -306,7 +306,7 @@ func (g *BlkTmplGenerator) populateTemplateFromCandidates(candidateTxs []*candid
txsForBlockTemplate.totalMass += selectedTx.txMass
txsForBlockTemplate.totalFees += selectedTx.txDesc.Fee
log.Tracef("Adding tx %s (feePerKB %.2f)",
log.Tracef("Adding tx %s (feePerKB %d)",
tx.ID(), selectedTx.txDesc.FeePerKB)
markCandidateTxForDeletion(selectedTx)

View File

@ -20,7 +20,7 @@ type blockProgressLogger struct {
receivedLogTx int64
lastBlockLogTime time.Time
subsystemLogger logs.Logger
subsystemLogger *logs.Logger
progressAction string
sync.Mutex
}
@ -29,7 +29,7 @@ type blockProgressLogger struct {
// The progress message is templated as follows:
// {progressAction} {numProcessed} {blocks|block} in the last {timePeriod}
// ({numTxs}, height {lastBlockHeight}, {lastBlockTimeStamp})
func newBlockProgressLogger(progressMessage string, logger logs.Logger) *blockProgressLogger {
func newBlockProgressLogger(progressMessage string, logger *logs.Logger) *blockProgressLogger {
return &blockProgressLogger{
lastBlockLogTime: time.Now(),
progressAction: progressMessage,

View File

@ -10,4 +10,4 @@ import (
)
var log, _ = logger.Get(logger.SubsystemTags.SYNC)
var spawn = panics.GoroutineWrapperFuncWithPanicHandler(log)
var spawn = panics.GoroutineWrapperFunc(log)

View File

@ -995,7 +995,7 @@ func (sm *SyncManager) handleBlockDAGNotification(notification *blockdag.Notific
if err != nil {
panic(fmt.Sprintf("HandleNewBlock failed to handle block %s", block.Hash()))
}
}, sm.handlePanic)
})
// Relay if we are current and the block was not just now unorphaned.
// Otherwise peers that are current should already know about it
@ -1098,11 +1098,7 @@ func (sm *SyncManager) Start() {
log.Trace("Starting sync manager")
sm.wg.Add(1)
spawn(sm.messageHandler, sm.handlePanic)
}
func (sm *SyncManager) handlePanic() {
atomic.AddInt32(&sm.shutdown, 1)
spawn(sm.messageHandler)
}
// Stop gracefully shuts down the sync manager by stopping all asynchronous

View File

@ -1199,7 +1199,7 @@ out:
handlerActive = false
default:
log.Warnf("Unsupported message command %s",
log.Warnf("Unsupported message command %d",
msg.command)
}

View File

@ -12,9 +12,11 @@ import (
// log is a logger that is initialized with no output filters. This
// means the package will not perform any logging by default until the caller
// requests it.
var log logs.Logger
var log *logs.Logger
var spawn func(func())
const logSubsytem = "RPCC"
// The default amount of logging is none.
func init() {
DisableLog()
@ -23,12 +25,15 @@ func init() {
// DisableLog disables all library log output. Logging output is disabled
// by default until UseLogger is called.
func DisableLog() {
log = logs.Disabled
backend := logs.NewBackend()
log = backend.Logger(logSubsytem)
log.SetLevel(logs.LevelOff)
spawn = panics.GoroutineWrapperFunc(log)
}
// UseLogger uses a specified Logger to output package logging info.
func UseLogger(logger logs.Logger) {
log = logger
func UseLogger(backend *logs.Backend, level logs.Level) {
log = backend.Logger(logSubsytem)
log.SetLevel(level)
spawn = panics.GoroutineWrapperFunc(log)
}

View File

@ -21,16 +21,14 @@ func (sp *Peer) OnGetAddr(_ *peer.Peer, msg *wire.MsgGetAddr) {
// Do not accept getaddr requests from outbound peers. This reduces
// fingerprinting attacks.
if !sp.Inbound() {
peerLog.Debugf("Ignoring getaddr request from outbound peer ",
"%s", sp)
peerLog.Debugf("Ignoring getaddr request from outbound peer %s", sp)
return
}
// Only allow one getaddr request per connection to discourage
// address stamping of inv announcements.
if sp.sentAddrs {
peerLog.Debugf("Ignoring repeated getaddr request from peer ",
"%s", sp)
peerLog.Debugf("Ignoring repeated getaddr request from peer %s", sp)
return
}
sp.sentAddrs = true

View File

@ -13,10 +13,6 @@ import (
// subsystems using the same code paths as when an interrupt signal is received.
var ShutdownRequestChannel = make(chan struct{})
// PanicShutdownChannel is used to initiate shutdown when any thread
// panics using the same code paths as when an interrupt signal is received.
var PanicShutdownChannel = make(chan struct{})
// interruptSignals defines the default signals to catch in order to do a proper
// shutdown. This may be modified during init depending on the platform.
var interruptSignals = []os.Signal{os.Interrupt}
@ -39,9 +35,6 @@ func InterruptListener() <-chan struct{} {
case <-ShutdownRequestChannel:
kasdLog.Info("Shutdown requested. Shutting down...")
case <-PanicShutdownChannel:
kasdLog.Info("Panic occurred. Shutting down...")
}
close(c)
@ -57,11 +50,6 @@ func InterruptListener() <-chan struct{} {
case <-ShutdownRequestChannel:
kasdLog.Info("Shutdown requested. Already " +
"shutting down...")
case <-PanicShutdownChannel:
kasdLog.Info("Panic occurred while shutting down. " +
"Forcing shut down...")
os.Exit(1)
}
}
}()

View File

@ -1,15 +0,0 @@
// Copyright (c) 2013-2014 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package locks
import (
"github.com/kaspanet/kaspad/logger"
"github.com/kaspanet/kaspad/util/panics"
)
var (
log, _ = logger.Get(logger.SubsystemTags.UTIL)
spawn = panics.GoroutineWrapperFunc(log)
)

View File

@ -7,7 +7,6 @@ package locks
import (
"sync/atomic"
"testing"
"time"
)
// All of the tests, except TestAddAfterWait and
@ -17,29 +16,6 @@ import (
// behaves the same, except enabling the use of add()
// concurrently with wait()
func spawnPatch(t *testing.T) (checkIfRunningSpawnsAreLeft func()) {
realSpawn := spawn
runningSpawns := int32(0)
spawn = func(f func()) {
atomic.AddInt32(&runningSpawns, 1)
realSpawn(func() {
f()
atomic.AddInt32(&runningSpawns, -1)
})
}
return func() {
defer func() {
spawn = realSpawn
}()
if runningSpawns != 0 {
time.Sleep(10 * time.Millisecond)
if runningSpawns != 0 {
t.Fatalf("%d running spawns left", runningSpawns)
}
}
}
}
func testWaitGroup(t *testing.T, wg1 *waitGroup, wg2 *waitGroup) {
n := int64(16)
wg1.add(n)
@ -67,8 +43,6 @@ func testWaitGroup(t *testing.T, wg1 *waitGroup, wg2 *waitGroup) {
}
func TestWaitGroup(t *testing.T) {
checkIfRunningSpawnsAreLeft := spawnPatch(t)
defer checkIfRunningSpawnsAreLeft()
wg1 := newWaitGroup()
wg2 := newWaitGroup()
@ -94,8 +68,6 @@ func TestWaitGroupMisuse(t *testing.T) {
}
func TestAddAfterWait(t *testing.T) {
checkIfRunningSpawnsAreLeft := spawnPatch(t)
defer checkIfRunningSpawnsAreLeft()
wg := newWaitGroup()
wg.add(1)
syncChan := make(chan struct{})
@ -113,8 +85,6 @@ func TestAddAfterWait(t *testing.T) {
}
func TestWaitGroupRace(t *testing.T) {
checkIfRunningSpawnsAreLeft := spawnPatch(t)
defer checkIfRunningSpawnsAreLeft()
// Run this test for about 1ms.
for i := 0; i < 1000; i++ {
wg := newWaitGroup()
@ -141,8 +111,6 @@ func TestWaitGroupRace(t *testing.T) {
}
func TestWaitGroupAlign(t *testing.T) {
checkIfRunningSpawnsAreLeft := spawnPatch(t)
defer checkIfRunningSpawnsAreLeft()
type X struct {
x byte
wg *waitGroup
@ -157,8 +125,6 @@ func TestWaitGroupAlign(t *testing.T) {
}
func TestWaitAfterAddDoneCounterHasReset(t *testing.T) {
checkIfRunningSpawnsAreLeft := spawnPatch(t)
defer checkIfRunningSpawnsAreLeft()
wg := newWaitGroup()
wg.add(1)
wg.done()

View File

@ -1,69 +1,58 @@
package panics
import (
"fmt"
"github.com/kaspanet/kaspad/logs"
"github.com/kaspanet/kaspad/signal"
"os"
"runtime/debug"
"time"
)
// HandlePanic recovers panics, log them, runs an optional panicHandler,
// and then initiates a clean shutdown.
func HandlePanic(log logs.Logger, goroutineStackTrace []byte, panicHandler func()) {
if err := recover(); err != nil {
func HandlePanic(log *logs.Logger, goroutineStackTrace []byte) {
err := recover()
if err == nil {
return
}
panicHandlerDone := make(chan struct{})
go func() {
log.Criticalf("Fatal error: %+v", err)
if goroutineStackTrace != nil {
log.Criticalf("goroutine stack trace: %s", goroutineStackTrace)
log.Criticalf("Goroutine stack trace: %s", goroutineStackTrace)
}
log.Criticalf("Stack trace: %s", debug.Stack())
if panicHandler != nil {
panicHandler()
}
signal.PanicShutdownChannel <- struct{}{}
log.Backend().Close()
panicHandlerDone <- struct{}{}
}()
const panicHandlerTimeout = 5 * time.Second
select {
case <-time.Tick(panicHandlerTimeout):
fmt.Fprintln(os.Stderr, "Couldn't handle a fatal error. Exiting...")
case <-panicHandlerDone:
}
os.Exit(1)
}
// GoroutineWrapperFunc returns a goroutine wrapper function that handles panics and writes them to the log.
func GoroutineWrapperFunc(log logs.Logger) func(func()) {
func GoroutineWrapperFunc(log *logs.Logger) func(func()) {
return func(f func()) {
stackTrace := debug.Stack()
go func() {
defer HandlePanic(log, stackTrace, nil)
f()
}()
}
}
// GoroutineWrapperFuncWithPanicHandler returns a goroutine wrapper function that handles panics,
// writes them to the log, and executes a handler function for panics.
func GoroutineWrapperFuncWithPanicHandler(log logs.Logger) func(func(), func()) {
return func(f func(), panicHandler func()) {
stackTrace := debug.Stack()
go func() {
defer HandlePanic(log, stackTrace, panicHandler)
defer HandlePanic(log, stackTrace)
f()
}()
}
}
// AfterFuncWrapperFunc returns a time.AfterFunc wrapper function that handles panics.
func AfterFuncWrapperFunc(log logs.Logger) func(d time.Duration, f func()) *time.Timer {
func AfterFuncWrapperFunc(log *logs.Logger) func(d time.Duration, f func()) *time.Timer {
return func(d time.Duration, f func()) *time.Timer {
stackTrace := debug.Stack()
return time.AfterFunc(d, func() {
defer HandlePanic(log, stackTrace, nil)
f()
})
}
}
// AfterFuncWrapperFuncWithPanicHandler returns a time.AfterFunc wrapper function that handles panics,
// writes them to the log, and executes a handler function for panics.
func AfterFuncWrapperFuncWithPanicHandler(log logs.Logger) func(d time.Duration, f func(), panicHandler func()) *time.Timer {
return func(d time.Duration, f func(), panicHandler func()) *time.Timer {
stackTrace := debug.Stack()
return time.AfterFunc(d, func() {
defer HandlePanic(log, stackTrace, panicHandler)
defer HandlePanic(log, stackTrace)
f()
})
}