blob: 8bd69c7a71f097da6d909254ddfbf552b18f073a [file] [log] [blame]
package logex
import (
"bytes"
"errors"
"fmt"
"path"
"runtime"
"strconv"
"strings"
)
func Define(info string) *traceError {
return &traceError{
error: errors.New(info),
}
}
func NewError(info ...interface{}) *traceError {
return TraceEx(1, errors.New(sprint(info)))
}
func NewErrorf(format string, info ...interface{}) *traceError {
return TraceEx(1, errors.New(sprintf(format, info)))
}
func EqualAny(e error, es []error) bool {
for i := 0; i < len(es); i++ {
if Equal(e, es[i]) {
return true
}
}
return false
}
func Equal(e1, e2 error) bool {
if e, ok := e1.(*traceError); ok {
e1 = e.error
}
if e, ok := e2.(*traceError); ok {
e2 = e.error
}
return e1 == e2
}
type traceError struct {
error
format []interface{}
stack []string
code *int
}
func (t *traceError) SetCode(code int) *traceError {
if t.stack == nil {
t = TraceEx(1, t)
}
t.code = &code
return t
}
func (t *traceError) GetCode() int {
if t.code == nil {
return 500
}
return *t.code
}
func (t *traceError) Error() string {
if t == nil {
return "<nil>"
}
if t.format == nil {
if t.error == nil {
panic(t.stack)
}
return t.error.Error()
}
return fmt.Sprintf(t.error.Error(), t.format...)
}
func (t *traceError) Trace(info ...interface{}) *traceError {
return TraceEx(1, t, info...)
}
func (t *traceError) Follow(err error) *traceError {
if t == nil {
return nil
}
if te, ok := err.(*traceError); ok {
if len(te.stack) > 0 {
te.stack[len(te.stack)-1] += ":" + err.Error()
}
t.stack = append(te.stack, t.stack...)
}
return t
}
func (t *traceError) Format(obj ...interface{}) *traceError {
if t.stack == nil {
t = TraceEx(1, t)
}
t.format = obj
return t
}
func (t *traceError) StackError() string {
if t == nil {
return t.Error()
}
if len(t.stack) == 0 {
return t.Error()
}
return fmt.Sprintf("[%s] %s", strings.Join(t.stack, ";"), t.Error())
}
func Tracefmt(layout string, objs ...interface{}) error {
var teInfo *traceError
for idx, obj := range objs {
if te, ok := obj.(*traceError); ok {
teInfo = te
objs[idx] = te.Error()
}
}
return &traceError{
error: fmt.Errorf(layout, objs...),
format: teInfo.format,
stack: teInfo.stack,
code: teInfo.code,
}
}
func Tracef(err error, obj ...interface{}) *traceError {
e := TraceEx(1, err).Format(obj...)
return e
}
// set runtime info to error
func TraceError(err error, info ...interface{}) *traceError {
return TraceEx(1, err, info...)
}
func Trace(err error, info ...interface{}) error {
if err == nil {
return nil
}
return TraceEx(1, err, info...)
}
func joinInterface(info []interface{}, ch string) string {
ret := bytes.NewBuffer(make([]byte, 0, 512))
for idx, o := range info {
if idx > 0 {
ret.WriteString(ch)
}
ret.WriteString(fmt.Sprint(o))
}
return ret.String()
}
func TraceEx(depth int, err error, info ...interface{}) *traceError {
if err == nil {
return nil
}
pc, _, line, _ := runtime.Caller(1 + depth)
name := runtime.FuncForPC(pc).Name()
name = path.Base(name)
stack := name + ":" + strconv.Itoa(line)
if len(info) > 0 {
stack += "(" + joinInterface(info, ",") + ")"
}
if te, ok := err.(*traceError); ok {
if te.stack == nil { // define
return &traceError{
error: te.error,
stack: []string{stack},
}
}
te.stack = append(te.stack, stack)
return te
}
return &traceError{err, nil, []string{stack}, nil}
}