// ------------------------------------------------------------------------ // Project active2 // Active Thing (activething.com) git.activething.com/go // // File name error_text_formatter.go // Created by DEV // Modified 27/03/2024 // // Copyright 2024 activething.com // ------------------------------------------------------------------------ // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // ------------------------------------------------------------------------ package errs import ( "fmt" "reflect" "strconv" "sync" "time" ) type ( FmtText struct { FmtTextAtt fixName int fieldSep string indent string dateLayout string fmtField string errorHead func(s *FmtTextState, e error) prevHead func(s *FmtTextState, e error) traceHead func(s *FmtTextState, e error) traceFmt func(s *FmtTextState, t StackTrace) providers sync.Map } FmtTextFnc func(f *FmtText) ) func NewFmtText(options ...FmtTextFnc) *FmtText { fm := applyFmtTextOptions(&FmtText{ FmtTextAtt: FmtTextAttIndent | FmtTextAttDetails | FmtTextAttTrace | FmtTextAttColors | FmtTextAttEmpty, dateLayout: time.RFC822Z, indent : " ", fixName : 12, fieldSep : " : ", traceFmt : formatStackTrace , }, options...) if fm.fixName > 0 { fm.fmtField = "%-" + strconv.Itoa(fm.fixName) + "s" } else { fm.fmtField = "%s" } return fm } func (f *FmtText) Format (err error) (res []byte) { if err == nil { return nil } st := newFmtTextState(f) dn := false defer func(d *bool, s *FmtTextState) { if !(*d) { if e := recover(); e != nil { panic(e) f.format(s,NewErrPanic(e).ToTrace(1),true) res = s.Buffer.Bytes() } } s.Reset() }(&dn,st) f.format(st,err,true) if f.AddStackTrace() { if tc := UnwrapStackTrace(err); tc != nil { st.WriteString("\nStack Trace\n") f.traceFmt(st, tc.StackTrace()) } } dn=true return st.Buffer.Bytes() } func (f *FmtText) format (s *FmtTextState, err error, head bool) { rt := reflect.TypeOf(err) if rt.Kind() == reflect.Pointer { err = reflect.ValueOf(err).Elem().Interface().(error) if err == nil { return } rt = rt.Elem() } if head { s.WriteFieldString("error", err.Error(), FormatColorRed) s.WriteFieldString("error-type", rt.String()) } f.getFmtTextProvider(rt).Format(s,err) ue := Unwrap(err) for ue != nil { f.format(s,ue,false) ue = Unwrap(ue) } /* if c := UnwrapCause(err); c != nil { s.writeString("-Previous-----------------------------------------\n",[]FormatColor{FormatColorCyan}) s.IncLevel() f.format(s,c.Cause(),true) s.DecLevel() } */ } func (f *FmtText) getFmtTextProvider(rt reflect.Type) FmtTextProvider { pt := rt.Kind() == reflect.Pointer if pt { rt = rt.Elem() } if fp,ok := f.providers.Load(rt); ok { return fp.(FmtTextProvider) } var wg sync.WaitGroup var np FmtTextProvider wg.Add(1) fp,ld := f.providers.LoadOrStore(rt, FmtTextProviderFnc(func (s *FmtTextState, err error){ wg.Wait() np.Format(s,err) })) if ld { return fp.(FmtTextProvider) } np = f.buildFmtTextProvider(rt) wg.Done() f.providers.Store(rt,np) return np } func (f *FmtText) buildFmtTextProvider (rtype reflect.Type) FmtTextProvider { if rtype.Kind() == reflect.Pointer { rtype = rtype.Elem() } switch rtype { case G.errCodeRT : return FmtTextProviderFnc(formatErrCode ) case G.errCauseRT: return FmtTextProviderFnc(formatErrCause) case G.errTraceRT: return FmtTextProviderFnc(formatErrTrace) case G.errPanicRT: return FmtTextProviderFnc(formatErrPanic) case G.errInfoRT : return FmtTextProviderFnc(formatErrInfo ) case G.errGroupRT: return FmtTextProviderFnc(formatErrGroup) case G.errMultiRT: return FmtTextProviderFnc(formatErrMulti) default : return FmtTextProviderFnc(formatErrAny) } } func formatErrCode (s *FmtTextState, e error) { //s.WriteFieldString("error-code",e.(ErrCode).Code()) } func formatErrPanic (s *FmtTextState, e error ){ //s.WriteFieldString("error-panic",e.(ErrPanic).Error()) } func formatErrTrace (s *FmtTextState, e error) { s.WriteTrace (e.(ErrTrace).StackTrace()) //s.WriteFieldString("error-Trace",e.(ErrTrace).Error()) } func formatErrCause (s *FmtTextState, e error) { s.WriteCause(e.(ErrCause).Cause()) } func formatErrGroup (s *FmtTextState, e error) { i := 0 e.(ErrGroup).Map(func(err error) bool { i++ s.Buffer.WriteString("** Error Group (") s.Buffer.WriteString(strconv.Itoa(i)) s.Buffer.WriteString(") ********************\n") s.fmt.format(s,err,true) return true }) } func formatErrMulti (s *FmtTextState, e error) { i := 0 e.(ErrMulti).Map(func(err error) bool { i++ s.Buffer.WriteString("** Multiple errors (") s.Buffer.WriteString(strconv.Itoa(i)) s.Buffer.WriteString(") ********************\n") s.fmt.format(s,err,true) return true }) } func formatErrInfo (s *FmtTextState, e error) { ei := e.(ErrInfo) s.WriteFieldString("message" ,ei.Message()) s.WriteFieldString("info" ,ei.Info ()) if s.Level() == 0 { s.WriteFieldTime("date-time",ei.Date(), FormatColorRed, FormatColorMagenta) ei.MapMetas(func(n string, v any) bool { s.WriteFieldString(n,fmt.Sprintf("%v",v)) return true }) } } func formatErrAny (s *FmtTextState, err error) { //s.WriteFieldString("error",err.Error()) } func formatStackTrace(s *FmtTextState, t StackTrace) { /* for _,f := range t.Frames() { s.WriteString(f.Package+"."+f.FuncName, FormatColorYellow) s.WriteString(fmt.Sprintf(" %.90s:%d",f.File,f.LineNumber), FormatColorBlue) if sc,er := f.SourceLine(); er == nil { if len(sc) > 80 { //ls := typex.SplitString(sc,70) for _,l := range ls { s.Buffer.WriteString(FormatColorMagenta.BeginColorString()) s.Buffer.WriteString(" ") s.Buffer.WriteString(l) s.Buffer.WriteByte('\n') s.Buffer.WriteString(FormatColorMagenta.EndColorString()) } continue } s.Buffer.WriteString(" ") s.Buffer.WriteString(FormatColorMagenta.Sprintf(sc)) s.Buffer.WriteByte('\n') } } */ } func errorHead (s *FmtTextState, e error) { s.WriteFieldString("error", e.Error(), FormatColorRed) s.WriteFieldString("error-type", reflect.TypeOf(e).String()) }