package common import ( "fmt" "runtime" ) //---------------------------------------- // Error & cmnError type Error interface { Error() string Trace(format string, a ...interface{}) Error TraceCause(cause error, format string, a ...interface{}) Error Cause() error Type() interface{} WithType(t interface{}) Error } // New Error with no cause where the type is the format string of the message.. func NewError(format string, a ...interface{}) Error { msg := Fmt(format, a...) return newError(msg, nil, format) } // New Error with cause where the type is the cause, with message.. func NewErrorWithCause(cause error, format string, a ...interface{}) Error { msg := Fmt(format, a...) return newError(msg, cause, cause) } // New Error with specified type and message. func NewErrorWithType(type_ interface{}, format string, a ...interface{}) Error { msg := Fmt(format, a...) return newError(msg, nil, type_) } type traceItem struct { msg string filename string lineno int } func (ti traceItem) String() string { return fmt.Sprintf("%v:%v %v", ti.filename, ti.lineno, ti.msg) } type cmnError struct { msg string cause error type_ interface{} traces []traceItem } // NOTE: Do not expose, it's not very friendly. func newError(msg string, cause error, type_ interface{}) *cmnError { return &cmnError{ msg: msg, cause: cause, type_: type_, traces: nil, } } func (err *cmnError) Error() string { return fmt.Sprintf("Error{%v:%s,%v,%v}", err.type_, err.msg, err.cause, len(err.traces)) } // Add tracing information with msg. func (err *cmnError) Trace(format string, a ...interface{}) Error { msg := Fmt(format, a...) return err.doTrace(msg, 2) } // Add tracing information with cause and msg. // If a cause was already set before, it is overwritten. func (err *cmnError) TraceCause(cause error, format string, a ...interface{}) Error { msg := Fmt(format, a...) err.cause = cause return err.doTrace(msg, 2) } // Return the "type" of this message, primarily for switching // to handle this error. func (err *cmnError) Type() interface{} { return err.type_ } // Overwrites the error's type. func (err *cmnError) WithType(type_ interface{}) Error { err.type_ = type_ return err } func (err *cmnError) doTrace(msg string, n int) Error { _, fn, line, ok := runtime.Caller(n) if !ok { if fn == "" { fn = "" } if line <= 0 { line = -1 } } // Include file & line number & msg. // Do not include the whole stack trace. err.traces = append(err.traces, traceItem{ filename: fn, lineno: line, msg: msg, }) return err } // Return last known cause. // NOTE: The meaning of "cause" is left for the caller to define. // There exists to canonical definition of "cause". // Instead of blaming, try to handle-or-organize it. func (err *cmnError) Cause() error { return err.cause } //---------------------------------------- // StackError // NOTE: Used by Tendermint p2p upon recovery. // Err could be "Reason", since it isn't an error type. type StackError struct { Err interface{} Stack []byte } func (se StackError) String() string { return fmt.Sprintf("Error: %v\nStack: %s", se.Err, se.Stack) } func (se StackError) Error() string { return se.String() } //---------------------------------------- // Panic wrappers // XXX DEPRECATED // A panic resulting from a sanity check means there is a programmer error // and some guarantee is not satisfied. // XXX DEPRECATED func PanicSanity(v interface{}) { panic(Fmt("Panicked on a Sanity Check: %v", v)) } // A panic here means something has gone horribly wrong, in the form of data corruption or // failure of the operating system. In a correct/healthy system, these should never fire. // If they do, it's indicative of a much more serious problem. // XXX DEPRECATED func PanicCrisis(v interface{}) { panic(Fmt("Panicked on a Crisis: %v", v)) } // Indicates a failure of consensus. Someone was malicious or something has // gone horribly wrong. These should really boot us into an "emergency-recover" mode // XXX DEPRECATED func PanicConsensus(v interface{}) { panic(Fmt("Panicked on a Consensus Failure: %v", v)) } // For those times when we're not sure if we should panic // XXX DEPRECATED func PanicQ(v interface{}) { panic(Fmt("Panicked questionably: %v", v)) }