You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
299 lines
6.6 KiB
299 lines
6.6 KiB
// ------------------------------------------------------------------------
|
|
// 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())
|
|
} |