github.com/mongodb/grip@v0.0.0-20240213223901-f906268d82b9/message/error_message.go (about) 1 package message 2 3 import ( 4 "fmt" 5 "io" 6 7 "github.com/mongodb/grip/level" 8 "github.com/pkg/errors" 9 ) 10 11 type errorComposerWrap struct { 12 err error 13 cached string 14 Composer 15 } 16 17 // NewErrorWrappedComposer provvides a way to construct a log message 18 // that annotates an error. 19 func NewErrorWrappedComposer(err error, m Composer) ErrorComposer { 20 return &errorComposerWrap{ 21 err: err, 22 Composer: m, 23 } 24 } 25 26 // NewErrorWrapMessage produces a fully configured message.Composer 27 // that combines the functionality of an Error composer that renders a 28 // loggable error message for non-nil errors with a normal formatted 29 // message (e.g. fmt.Sprintf). These messages only log if the error is 30 // non-nil. 31 func NewErrorWrapMessage(p level.Priority, err error, base string, args ...interface{}) ErrorComposer { 32 return NewErrorWrappedComposer(err, NewFormattedMessage(p, base, args...)) 33 } 34 35 // NewErrorWrap produces a message.Composer that combines the 36 // functionality of an Error composer that renders a loggable error 37 // message for non-nil errors with a normal formatted message 38 // (e.g. fmt.Sprintf). These messages only log if the error is 39 // non-nil. 40 func NewErrorWrap(err error, base string, args ...interface{}) ErrorComposer { 41 return NewErrorWrappedComposer(err, NewFormatted(base, args...)) 42 } 43 44 // WrapError wraps an error and creates a composer converting the 45 // argument into a composer in the same manner as the front end logging methods. 46 func WrapError(err error, m interface{}) ErrorComposer { 47 return NewErrorWrappedComposer(err, ConvertToComposer(level.Priority(0), m)) 48 } 49 50 // WrapErrorf wraps an error and creates a composer using a 51 // Sprintf-style formated composer. 52 func WrapErrorf(err error, msg string, args ...interface{}) ErrorComposer { 53 return NewErrorWrappedComposer(err, NewFormatted(msg, args...)) 54 } 55 56 func (m *errorComposerWrap) String() string { 57 if m.cached == "" { 58 context := m.Composer.String() 59 if context != "" { 60 m.cached = fmt.Sprintf("%s: %v", context, m.err.Error()) 61 } else { 62 m.cached = m.err.Error() 63 } 64 } 65 66 return m.cached 67 } 68 69 func (m *errorComposerWrap) Error() string { return m.String() } 70 func (m *errorComposerWrap) Cause() error { return m.err } 71 func (m *errorComposerWrap) Format(s fmt.State, verb rune) { 72 switch verb { 73 case 'v': 74 if s.Flag('+') { 75 fmt.Fprintf(s, "%+v\n", errors.Cause(m.err)) 76 _, _ = io.WriteString(s, m.String()) 77 return 78 } 79 fallthrough 80 case 's', 'q': 81 _, _ = io.WriteString(s, m.Error()) 82 } 83 } 84 85 func (m *errorComposerWrap) Loggable() bool { 86 return m.err != nil 87 } 88 89 func (m *errorComposerWrap) Raw() interface{} { 90 errStr := m.err.Error() 91 out := Fields{ 92 "error": errStr, 93 } 94 95 if m.Composer.Loggable() { 96 // special handling for fields - merge keys in with output keys 97 switch t := m.Composer.(type) { 98 case *fieldMessage: 99 t.fields["error"] = errStr 100 out = t.fields 101 default: 102 out["context"] = m.Composer.Raw() 103 } 104 } 105 106 ext := fmt.Sprintf("%+v", m.err) 107 if ext != errStr { 108 out["extended"] = ext 109 } 110 111 return out 112 } 113 114 func (m *errorComposerWrap) Annotate(k string, v interface{}) error { 115 return m.Composer.Annotate(k, v) 116 }