github.com/benz9527/xboot@v0.0.0-20240504061247-c23f15593274/lib/infra/err_stack.go (about)

     1  package infra
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"io"
     7  	"path"
     8  	"runtime"
     9  	"strconv"
    10  	"strings"
    11  
    12  	"go.uber.org/multierr"
    13  	"go.uber.org/zap/zapcore"
    14  )
    15  
    16  // References:
    17  // https://github.com/pkg/errors/blob/master/stack.go
    18  
    19  type frame uintptr
    20  
    21  func (fr frame) pc() uintptr {
    22  	return uintptr(fr) - 1
    23  }
    24  
    25  func (fr frame) file() string {
    26  	pc := fr.pc()
    27  	fn := runtime.FuncForPC(pc)
    28  	if fn == nil {
    29  		return "unknownFile"
    30  	}
    31  	f, _ := fn.FileLine(pc)
    32  	return f
    33  }
    34  
    35  func (fr frame) line() int {
    36  	pc := fr.pc()
    37  	fn := runtime.FuncForPC(pc)
    38  	if fn == nil {
    39  		return 0
    40  	}
    41  	_, l := fn.FileLine(pc)
    42  	return l
    43  }
    44  
    45  func (fr frame) name() string {
    46  	pc := fr.pc()
    47  	fn := runtime.FuncForPC(pc)
    48  	if fn == nil {
    49  		return "unknownFunc"
    50  	}
    51  	return fn.Name()
    52  }
    53  
    54  // Format characters:
    55  // %s - source file
    56  // %d - source line
    57  // %n - function name
    58  // %v - verbose, equivalent to %s:%d
    59  // %+s - full path, the root path is relative to the compile time GOPATH
    60  // separated by \n\t (<function-name>\n\t<path>)
    61  // %+v - equivalent to %+s:%d
    62  func (fr frame) Format(state fmt.State, verb rune) {
    63  	switch verb {
    64  	case 's':
    65  		if state.Flag('+') {
    66  			_, _ = io.WriteString(state, fr.name())
    67  			_, _ = io.WriteString(state, "\n\t")
    68  			_, _ = io.WriteString(state, fr.file())
    69  		} else {
    70  			_, _ = io.WriteString(state, path.Base(fr.file()))
    71  		}
    72  	case 'd':
    73  		_, _ = io.WriteString(state, strconv.Itoa(fr.line()))
    74  	case 'n':
    75  		_, _ = io.WriteString(state, funcName(fr.name()))
    76  	case 'v':
    77  		fr.Format(state, 's')
    78  		_, _ = io.WriteString(state, ":")
    79  		fr.Format(state, 'd')
    80  	}
    81  }
    82  
    83  // For fmt.Sprintf("%+v", frame).
    84  // If json.Marshaler interface isn't implemented, the MarshalText method is used.
    85  func (fr frame) MarshalText() ([]byte, error) {
    86  	name := fr.name()
    87  	if name == "unknownFunc" {
    88  		return []byte("unknownFrame"), nil
    89  	}
    90  	builder := strings.Builder{}
    91  	_, _ = builder.WriteString(name)
    92  	_, _ = builder.WriteString(" ")
    93  	_, _ = builder.WriteString(fr.file())
    94  	_, _ = builder.WriteString(":")
    95  	_, _ = builder.WriteString(strconv.Itoa(fr.line()))
    96  	return []byte(builder.String()), nil
    97  }
    98  
    99  func (fr frame) MarshalJSON() ([]byte, error) {
   100  	name := fr.name()
   101  	if name == "unknownFunc" {
   102  		return []byte("{\"frame\":\"unknownFrame\"}"), nil
   103  	}
   104  	builder := strings.Builder{}
   105  	_, _ = builder.WriteString("{")
   106  	_, _ = builder.WriteString("\"func\":\"")
   107  	_, _ = builder.WriteString(name)
   108  	_, _ = builder.WriteString("\",")
   109  	_, _ = builder.WriteString("\"fileAndLine\":\"")
   110  	_, _ = builder.WriteString(fr.file())
   111  	_, _ = builder.WriteString(":")
   112  	_, _ = builder.WriteString(strconv.Itoa(fr.line()))
   113  	_, _ = builder.WriteString("\"}")
   114  	return []byte(builder.String()), nil
   115  }
   116  
   117  func (fr frame) MarshalLogObject(enc zapcore.ObjectEncoder) error {
   118  	if enc == nil {
   119  		return errors.New("zapcore object marshal error")
   120  	}
   121  	name := fr.name()
   122  	if name == "unknownFunc" {
   123  		enc.AddString("frame", "unknownFrame")
   124  		return nil
   125  	}
   126  	enc.AddString("func", name)
   127  	enc.AddString("fileAndLine", fr.file()+":"+strconv.Itoa(fr.line()))
   128  	return nil
   129  }
   130  
   131  func funcName(name string) string {
   132  	i := strings.LastIndex(name, "/")
   133  	name = name[i+1:]
   134  	i = strings.Index(name, ".")
   135  	return name[i+1:]
   136  }
   137  
   138  type stackTrace []frame
   139  
   140  func (st stackTrace) Format(state fmt.State, verb rune) {
   141  	switch verb {
   142  	case 'v':
   143  		if state.Flag('#') {
   144  			_, _ = io.WriteString(state, "infra.stackTrace(")
   145  			if len(st) > 0 {
   146  				_, _ = io.WriteString(state, "[")
   147  				for i, frame := range st {
   148  					if i > 0 {
   149  						_, _ = io.WriteString(state, ",")
   150  					}
   151  					frame.Format(state, verb)
   152  				}
   153  				_, _ = io.WriteString(state, "]")
   154  			} else {
   155  				_, _ = io.WriteString(state, "nil")
   156  			}
   157  			_, _ = io.WriteString(state, ")")
   158  		} else if state.Flag('+') {
   159  			for i, frame := range st {
   160  				if i > 0 {
   161  					_, _ = io.WriteString(state, "\n")
   162  				}
   163  				frame.Format(state, verb)
   164  			}
   165  			if len(st) <= 0 {
   166  				_, _ = io.WriteString(state, "nil")
   167  			}
   168  		} else {
   169  			st.formatSlice(state, verb)
   170  		}
   171  	case 's':
   172  		st.formatSlice(state, verb)
   173  	}
   174  }
   175  
   176  func (st stackTrace) formatSlice(state fmt.State, verb rune) {
   177  	if !state.Flag('+') {
   178  		_, _ = io.WriteString(state, "[")
   179  		for i, frame := range st {
   180  			if i > 0 {
   181  				_, _ = io.WriteString(state, " ")
   182  			}
   183  			frame.Format(state, verb)
   184  		}
   185  		_, _ = io.WriteString(state, "]")
   186  	} else {
   187  		for i, frame := range st {
   188  			if i > 0 {
   189  				_, _ = io.WriteString(state, "\n")
   190  			}
   191  			frame.Format(state, verb)
   192  		}
   193  	}
   194  }
   195  
   196  func (st stackTrace) MarshalText() ([]byte, error) {
   197  	builder := strings.Builder{}
   198  	for i, frame := range st {
   199  		if i > 0 {
   200  			_, _ = builder.WriteString("\n")
   201  		}
   202  		_bytes, _ := frame.MarshalText()
   203  		_, _ = builder.Write(_bytes)
   204  	}
   205  	return []byte(builder.String()), nil
   206  }
   207  
   208  func (st stackTrace) MarshalJSON() ([]byte, error) {
   209  	builder := strings.Builder{}
   210  	_, _ = builder.WriteString("[")
   211  	for i, frame := range st {
   212  		if i > 0 {
   213  			_, _ = builder.WriteString(",")
   214  		}
   215  		_bytes, _ := frame.MarshalJSON()
   216  		_, _ = builder.Write(_bytes)
   217  	}
   218  	_, _ = builder.WriteString("]")
   219  	return []byte(builder.String()), nil
   220  }
   221  
   222  func (st stackTrace) MarshalLogObject(enc zapcore.ObjectEncoder) error {
   223  	if enc == nil {
   224  		return errors.New("zapcore object marshal error")
   225  	}
   226  	if err := enc.AddArray("frames", zapcore.ArrayMarshalerFunc(func(enc zapcore.ArrayEncoder) error {
   227  		if enc == nil {
   228  			return errors.New("zapcore array marshal error")
   229  		}
   230  		for _, frame := range st {
   231  			if err := enc.AppendObject(frame); err != nil {
   232  				return err
   233  			}
   234  		}
   235  		return nil
   236  	})); err != nil {
   237  		return err
   238  	}
   239  	return nil
   240  }
   241  
   242  type stack []uintptr
   243  
   244  func (stack stack) StackTrace() stackTrace {
   245  	st := make(stackTrace, len(stack))
   246  	for i := 0; i < len(st); i++ {
   247  		st[i] = frame(stack[i])
   248  	}
   249  	return st
   250  }
   251  
   252  func (stack stack) Format(state fmt.State, verb rune) {
   253  	switch verb {
   254  	case 'v':
   255  		if state.Flag('#') {
   256  			_, _ = io.WriteString(state, "infra.stack(")
   257  			stack.StackTrace().Format(state, verb)
   258  			_, _ = io.WriteString(state, ")")
   259  		} else if state.Flag('+') {
   260  			_, _ = io.WriteString(state, "stack:\n")
   261  			st := stack.StackTrace()
   262  			_, _ = fmt.Fprintf(state, "%+v", st)
   263  		} else {
   264  			stack.StackTrace().Format(state, 'v')
   265  		}
   266  	}
   267  }
   268  
   269  func (st stack) MarshalJSON() ([]byte, error) {
   270  	builder := strings.Builder{}
   271  	_bytes, _ := st.StackTrace().MarshalJSON()
   272  	_, _ = builder.Write(_bytes)
   273  	return []byte(builder.String()), nil
   274  }
   275  
   276  func (st stack) MarshalLogObject(enc zapcore.ObjectEncoder) error {
   277  	if enc == nil {
   278  		return errors.New("zapcore object marshal error")
   279  	}
   280  	return st.StackTrace().MarshalLogObject(enc)
   281  }
   282  
   283  func (st stack) MarshalLogArray(enc zapcore.ArrayEncoder) error {
   284  	if enc == nil {
   285  		return errors.New("zapcore object marshal error")
   286  	}
   287  	for _, frame := range st.StackTrace() {
   288  		if err := enc.AppendObject(frame); err != nil {
   289  			return err
   290  		}
   291  	}
   292  	return nil
   293  }
   294  
   295  func getCallers(depth int8) stack {
   296  	pcs := make([]uintptr, depth)
   297  	n := runtime.Callers(3, pcs[:])
   298  	var st stack = pcs[0:n]
   299  	return st
   300  }
   301  
   302  type ErrorStack interface {
   303  	Error() string
   304  	Unwrap() []error
   305  	MarshalJSON() ([]byte, error)
   306  	zapcore.ObjectMarshaler
   307  }
   308  
   309  type errorStack struct {
   310  	err   error
   311  	stack *stack
   312  	upper *errorStack
   313  }
   314  
   315  // For errors.Is(err, target error) and errors.As(err error, target any).
   316  func (es *errorStack) Unwrap() []error {
   317  	_errors := make([]error, 0, 8)
   318  	for err := es.err; es != nil; es = es.upper {
   319  		switch x := err.(type) {
   320  		case interface{ Unwrap() []error }:
   321  			_errors = append(_errors, x.Unwrap()...)
   322  		default:
   323  			_errors = append(_errors, multierr.Errors(err)...)
   324  		}
   325  	}
   326  	return _errors
   327  }
   328  
   329  func (es *errorStack) Error() string {
   330  	builder := strings.Builder{}
   331  	for i, iter := 0, es; iter != nil; i, iter = i+1, iter.upper {
   332  		if iter.err != nil {
   333  			if i > 0 {
   334  				_, _ = builder.WriteString("; ")
   335  			}
   336  			_, _ = builder.WriteString(iter.err.Error())
   337  		}
   338  	}
   339  	res := builder.String()
   340  	if res == "" {
   341  		res = "[{}]"
   342  	}
   343  	return res
   344  }
   345  
   346  func (es *errorStack) Format(state fmt.State, verb rune) {
   347  	switch verb {
   348  	case 'v':
   349  		if state.Flag('#') {
   350  			_, _ = io.WriteString(state, "infra.errorStacks([")
   351  			for i, iter := 0, es; iter != nil; i, iter = i+1, iter.upper {
   352  				if i > 0 {
   353  					_, _ = io.WriteString(state, ",")
   354  				}
   355  				_, _ = io.WriteString(state, "infra.errorStack({")
   356  				_, _ = io.WriteString(state, "error(")
   357  				if iter.err != nil {
   358  					_, _ = io.WriteString(state, iter.err.Error())
   359  				} else {
   360  					_, _ = io.WriteString(state, "nil")
   361  				}
   362  				_, _ = io.WriteString(state, "); ")
   363  				if iter.stack != nil {
   364  					iter.stack.Format(state, verb)
   365  				} else {
   366  					_, _ = io.WriteString(state, "nilStack")
   367  				}
   368  				_, _ = io.WriteString(state, "})")
   369  			}
   370  			_, _ = io.WriteString(state, "])")
   371  		} else if state.Flag('+') {
   372  			for i, iter := 0, es; iter != nil; i, iter = i+1, iter.upper {
   373  				if i > 0 {
   374  					_, _ = io.WriteString(state, "\n")
   375  				}
   376  				_, _ = io.WriteString(state, "error messages:\n\t")
   377  				if iter.err != nil {
   378  					_, _ = io.WriteString(state, iter.err.Error())
   379  				} else {
   380  					_, _ = io.WriteString(state, "nil")
   381  				}
   382  				_, _ = io.WriteString(state, "\n")
   383  				if iter.stack != nil {
   384  					iter.stack.Format(state, verb)
   385  				} else {
   386  					_, _ = io.WriteString(state, "no stack!")
   387  				}
   388  			}
   389  		} else {
   390  			_, _ = io.WriteString(state, es.Error())
   391  		}
   392  	case 's':
   393  		_, _ = io.WriteString(state, es.Error())
   394  	}
   395  }
   396  
   397  func (es *errorStack) MarshalJSON() ([]byte, error) {
   398  	builder := strings.Builder{}
   399  	_, _ = builder.WriteString("[")
   400  	for i, iter := 0, es; iter != nil; i, iter = i+1, iter.upper {
   401  		if i > 0 {
   402  			_, _ = builder.WriteString(",")
   403  		}
   404  		_, _ = builder.WriteString("{")
   405  		if iter.err != nil {
   406  			_, _ = builder.WriteString("\"error\":\"")
   407  			_, _ = builder.WriteString(iter.err.Error())
   408  			_, _ = builder.WriteString("\"")
   409  		}
   410  		if iter.stack != nil {
   411  			_, _ = builder.WriteString(",")
   412  			_, _ = builder.WriteString("\"errorStack\":")
   413  			_bytes, _ := iter.stack.MarshalJSON()
   414  			_, _ = builder.Write(_bytes)
   415  		}
   416  		_, _ = builder.WriteString("}")
   417  	}
   418  	_, _ = builder.WriteString("]")
   419  	return []byte(builder.String()), nil
   420  }
   421  
   422  func (es *errorStack) MarshalLogObject(enc zapcore.ObjectEncoder) error {
   423  	if es == nil || enc == nil {
   424  		return nil
   425  	}
   426  	if err := enc.AddArray("stacktrace", zapcore.ArrayMarshalerFunc(func(ae zapcore.ArrayEncoder) error {
   427  		for iter := es; iter != nil; iter = iter.upper {
   428  			if err := ae.AppendObject(zapcore.ObjectMarshalerFunc(func(oe zapcore.ObjectEncoder) error {
   429  				if iter.err != nil {
   430  					oe.AddString("error", iter.err.Error())
   431  				}
   432  				if iter.stack != nil {
   433  					if err := oe.AddArray("errorStack", iter.stack); err != nil {
   434  						return err
   435  					}
   436  				}
   437  				return nil
   438  			})); err != nil {
   439  				return err
   440  			}
   441  		}
   442  		return nil
   443  	})); err != nil {
   444  		return err
   445  	}
   446  	return nil
   447  }
   448  
   449  func NewErrorStack(errMsg string) error {
   450  	errMsg = strings.TrimSpace(errMsg)
   451  	if len(errMsg) <= 0 {
   452  		return nil
   453  	}
   454  	s := getCallers(32)
   455  	return &errorStack{
   456  		err:   errors.New(errMsg),
   457  		stack: &s,
   458  		upper: nil,
   459  	}
   460  }
   461  
   462  func WrapErrorStack(err error) error {
   463  	if err == nil {
   464  		return nil
   465  	}
   466  	if es, ok := err.(*errorStack); ok && es.err != nil && es.stack != nil {
   467  		return es
   468  	}
   469  	s := getCallers(32)
   470  	return &errorStack{
   471  		err:   err,
   472  		stack: &s,
   473  		upper: nil,
   474  	}
   475  }
   476  
   477  func AppendErrorStack(es error, errors ...error) error {
   478  	if len(errors) <= 0 {
   479  		return es
   480  	}
   481  
   482  	var merr error
   483  	for _, e := range errors {
   484  		if e != nil {
   485  			merr = multierr.Append(merr, e)
   486  		}
   487  	}
   488  	if es == nil {
   489  		s := getCallers(32)
   490  		return &errorStack{
   491  			err:   merr,
   492  			stack: &s,
   493  			upper: nil,
   494  		}
   495  	}
   496  	if _es, ok := es.(*errorStack); ok && _es.err != nil && _es.stack != nil {
   497  
   498  		s := getCallers(32)
   499  		return &errorStack{
   500  			err:   merr,
   501  			stack: &s,
   502  			upper: _es,
   503  		}
   504  	}
   505  	if estr := es.Error(); len(estr) > 0 && estr != "[{}]" {
   506  		merr = multierr.Append(es, merr)
   507  	}
   508  	s := getCallers(32)
   509  	return &errorStack{
   510  		err:   merr,
   511  		stack: &s,
   512  		upper: nil,
   513  	}
   514  }
   515  
   516  func WrapErrorStackWithMessage(es error, errMsg string) error {
   517  	if len(errMsg) <= 0 {
   518  		return es
   519  	}
   520  	err := fmt.Errorf("%s", errMsg)
   521  	if es == nil {
   522  		s := getCallers(32)
   523  		return &errorStack{
   524  			err:   err,
   525  			stack: &s,
   526  			upper: nil,
   527  		}
   528  	}
   529  	if _es, ok := es.(*errorStack); ok && _es.err != nil && _es.stack != nil {
   530  		s := getCallers(32)
   531  		return &errorStack{
   532  			err:   err,
   533  			stack: &s,
   534  			upper: _es,
   535  		}
   536  	}
   537  	if estr := es.Error(); len(estr) > 0 && estr != "[{}]" {
   538  		err = multierr.Append(es, err)
   539  	}
   540  	s := getCallers(32)
   541  	return &errorStack{
   542  		err:   err,
   543  		stack: &s,
   544  		upper: nil,
   545  	}
   546  }