github.com/insolar/vanilla@v0.0.0-20201023172447-248fdf805322/throw/fmtwrap.go (about) 1 // Copyright 2020 Insolar Network Ltd. 2 // All rights reserved. 3 // This material is licensed under the Insolar License version 1.0, 4 // available at https://github.com/insolar/assured-ledger/blob/master/LICENSE.md. 5 6 package throw 7 8 import ( 9 "fmt" 10 "reflect" 11 "sync/atomic" 12 ) 13 14 var panicFmtFn atomic.Value 15 16 type FormatterFunc func(string, interface{}) (msg string, extra interface{}, includeExtra bool) 17 18 func GetFormatter() FormatterFunc { 19 if vv, ok := panicFmtFn.Load().(FormatterFunc); ok { 20 return vv 21 } 22 return nil 23 } 24 25 // SetFormatter sets a formatter to be applied by NewDescription and New 26 func SetFormatter(fn FormatterFunc) { 27 if fn == nil { 28 panic(IllegalValue()) 29 } 30 panicFmtFn.Store(fn) 31 } 32 33 // NewDescription creates an error with the given description. Log structs can be used 34 func NewDescription(description interface{}) error { 35 if description == nil { 36 return nil 37 } 38 if fmtFn := GetFormatter(); fmtFn != nil { 39 return _wrap(fmtFn("", description)) 40 } 41 if err, ok := description.(error); ok { 42 return err 43 } 44 return _wrapDesc(description) 45 } 46 47 // New creates an error with the given message text and description. Log structs can be used 48 func New(msg string, description ...interface{}) error { 49 return Severe(0, msg, description...) 50 } 51 52 func Severe(severity Severity, msg string, description ...interface{}) error { 53 if description == nil && msg == "" && severity == 0 { 54 return nil 55 } 56 if fmtFn := GetFormatter(); fmtFn != nil { 57 return _wrap(fmtFn(msg, description)) 58 } 59 60 var err error 61 if len(description) > 0 { 62 d0 := description[0] 63 s := "" 64 if vv, ok := d0.(logStringer); ok { 65 s = vv.LogString() 66 } else { 67 s = defaultFmt(d0, false) 68 } 69 err = fmtWrap{msg: msg, severity: severity, extra: d0, useExtra: msg != s} 70 } else { 71 err = fmtWrap{msg: msg, severity: severity} 72 } 73 74 for i := 1; i < len(description); i++ { 75 if description[i] == nil { 76 continue 77 } 78 err = WithDetails(err, description[i]) 79 } 80 81 return err 82 } 83 84 func NewFmt(msg string, errorDescription interface{}, fmtFn FormatterFunc) error { 85 if fmtFn == nil { 86 panic(IllegalValue()) 87 } 88 if errorDescription == nil && msg == "" { 89 return nil 90 } 91 return _wrap(fmtFn(msg, errorDescription)) 92 } 93 94 func wrapInternal(errorDescription interface{}) fmtWrap { 95 if fmtFn := GetFormatter(); fmtFn != nil { 96 return _wrap(fmtFn("", errorDescription)) 97 } 98 return _wrapDesc(errorDescription) 99 } 100 101 func _wrapDesc(errorDescription interface{}) fmtWrap { 102 if vv, ok := errorDescription.(logStringer); ok { 103 msg := vv.LogString() 104 return fmtWrap{msg: msg, extra: errorDescription} 105 } 106 msg := defaultFmt(errorDescription, true) 107 return fmtWrap{msg: msg, extra: errorDescription} 108 } 109 110 func _wrap(msg string, extra interface{}, useExtra bool) fmtWrap { 111 return fmtWrap{msg: msg, extra: extra, useExtra: useExtra} 112 } 113 114 type extraInfo interface { 115 ExtraInfo() (string, Severity, interface{}) 116 } 117 118 func UnwrapExtraInfo(err interface{}) (string, Severity, interface{}, bool) { 119 if e, ok := err.(extraInfo); ok { 120 st, sv, ei := e.ExtraInfo() 121 return st, sv, ei, true 122 } 123 return "", 0, nil, false 124 } 125 126 type logStringer interface{ LogString() string } 127 128 func defaultFmt(v interface{}, force bool) string { 129 switch vv := v.(type) { 130 case fmt.Stringer: 131 return vv.String() 132 case error: 133 return vv.Error() 134 case string: 135 return vv 136 case *string: 137 if vv != nil { 138 return *vv 139 } 140 case nil: 141 // 142 default: 143 s := "" 144 t := reflect.TypeOf(v) 145 switch { 146 case t.Kind() == reflect.Struct && t.PkgPath() == "": 147 return fmt.Sprintf("%+v", vv) 148 case force: 149 s = fmt.Sprint(vv) 150 default: 151 return "" 152 } 153 if len(s) == 0 { 154 s = fmt.Sprintf("%T(%[1]p)", vv) 155 } 156 return s 157 } 158 return "<nil>" 159 }