package exerr import ( "github.com/rs/xid" "github.com/rs/zerolog" "gogs.mikescher.com/BlackForestBytes/goext/langext" "strings" "time" ) type ExErr struct { UniqueID string `json:"uniqueID"` Timestamp time.Time `json:"timestamp"` Category ErrorCategory `json:"category"` Severity ErrorSeverity `json:"severity"` Type ErrorType `json:"type"` StatusCode *int `json:"statusCode"` Message string `json:"message"` WrappedErrType string `json:"wrappedErrType"` Caller string `json:"caller"` OriginalError *ExErr Meta MetaMap `json:"meta"` } func (ee *ExErr) Error() string { return ee.Message } func (ee *ExErr) Unwrap() error { return ee.OriginalError } func (ee *ExErr) Log(evt *zerolog.Event) { evt.Msg(ee.FormatLog(LogPrintFull)) } func (ee *ExErr) FormatLog(lvl LogPrintLevel) string { if lvl == LogPrintShort { msg := ee.Message if ee.OriginalError != nil && ee.OriginalError.Category == CatForeign { msg = msg + " (" + strings.ReplaceAll(ee.OriginalError.Message, "\n", " ") + ")" } if ee.Type != TypeWrap { return "[" + ee.Type.Key + "] " + msg } else { return msg } } else if lvl == LogPrintOverview { str := "[" + ee.RecursiveType().Key + "] <" + ee.UniqueID + "> " + strings.ReplaceAll(ee.RecursiveMessage(), "\n", " ") + "\n" indent := "" for curr := ee; curr != nil; curr = curr.OriginalError { indent += " " str += indent str += "-> " strmsg := strings.Trim(curr.Message, " \r\n\t") if lbidx := strings.Index(curr.Message, "\n"); lbidx >= 0 { strmsg = strmsg[0:lbidx] } strmsg = langext.StrLimit(strmsg, 61, "...") str += strmsg str += "\n" } return str } else if lvl == LogPrintFull { str := "[" + ee.RecursiveType().Key + "] <" + ee.UniqueID + "> " + strings.ReplaceAll(ee.RecursiveMessage(), "\n", " ") + "\n" indent := "" for curr := ee; curr != nil; curr = curr.OriginalError { indent += " " etype := ee.Type.Key if ee.Type == TypeWrap { etype = "~" } str += indent str += "-> [" str += etype if curr.Category == CatForeign { str += "|Foreign" } str += "] " str += strings.ReplaceAll(curr.Message, "\n", " ") if curr.Caller != "" { str += " (@ " str += curr.Caller str += ")" } str += "\n" if curr.Meta.Any() { meta := indent + " {" + curr.Meta.FormatOneLine(240) + "}" if len(meta) < 200 { str += meta str += "\n" } else { str += curr.Meta.FormatMultiLine(indent+" ", " ", 1024) str += "\n" } } } return str } else { return "[?[" + ee.UniqueID + "]?]" } } func (ee *ExErr) ShortLog(evt *zerolog.Event) { ee.Meta.Apply(evt).Msg(ee.FormatLog(LogPrintShort)) } // RecursiveMessage returns the message to show // = first error (top-down) that is not wrapping/foreign/empty func (ee *ExErr) RecursiveMessage() string { for curr := ee; curr != nil; curr = curr.OriginalError { if curr.Message != "" && curr.Category != CatWrap && curr.Category != CatForeign { return curr.Message } } // fallback to self return ee.Message } // RecursiveType returns the statuscode to use // = first error (top-down) that is not wrapping/empty func (ee *ExErr) RecursiveType() ErrorType { for curr := ee; curr != nil; curr = curr.OriginalError { if curr.Type != TypeWrap { return curr.Type } } // fallback to self return ee.Type } // RecursiveStatuscode returns the HTTP Statuscode to use // = first error (top-down) that has a statuscode set func (ee *ExErr) RecursiveStatuscode() *int { for curr := ee; curr != nil; curr = curr.OriginalError { if curr.StatusCode != nil { return langext.Ptr(*curr.StatusCode) } } return nil } // RecursiveCategory returns the ErrorCategory to use // = first error (top-down) that has a statuscode set func (ee *ExErr) RecursiveCategory() ErrorCategory { for curr := ee; curr != nil; curr = curr.OriginalError { if curr.Category != CatWrap { return curr.Category } } // fallback to return ee.Category } // RecursiveMeta searches (top-down) for teh first error that has a meta value with teh specified key // and returns its value (or nil) func (ee *ExErr) RecursiveMeta(key string) *MetaValue { for curr := ee; curr != nil; curr = curr.OriginalError { if metaval, ok := curr.Meta[key]; ok { return langext.Ptr(metaval) } } return nil } func (ee *ExErr) Depth() int { if ee.OriginalError == nil { return 1 } else { return ee.OriginalError.Depth() + 1 } } func newID() string { return xid.New().String() }