github.com/duomi520/utils@v0.0.0-20240430123446-e03a4cddd6ec/errors.go (about)

     1  package utils
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"log/slog"
     7  	"runtime"
     8  	"strings"
     9  )
    10  
    11  /*
    12  Errors are just values not sentinel errors 防止包引入
    13  handle not just check errors
    14  Only handle errors once
    15  要求传入的字符串首字母小写,结尾不带标点符号
    16  将 errors 看成黑盒,判断它的行为,而不是类型
    17  使用 Wrap Unwrap
    18  最上层打印
    19  */
    20  
    21  type wError struct {
    22  	stack string
    23  	err   error
    24  }
    25  
    26  func (m wError) Error() string {
    27  	return m.err.Error()
    28  }
    29  
    30  func ErrorWithStack(err error) string {
    31  	if err == nil {
    32  		return ""
    33  	}
    34  	m, ok := err.(wError)
    35  	if ok {
    36  		return m.stack
    37  	}
    38  	return err.Error()
    39  
    40  }
    41  
    42  func WrapStack(skip int, err error) error {
    43  	if err == nil {
    44  		return nil
    45  	}
    46  	_, f, l, _ := runtime.Caller(skip)
    47  	result := strings.Split(f, "/")
    48  	m := wError{
    49  		stack: fmt.Sprintf("[%s:%d] %s", result[len(result)-1], l, err),
    50  		err:   err,
    51  	}
    52  	return m
    53  }
    54  func ErrorWithFullStack(w error) string {
    55  	v, ok := w.(wError)
    56  	if ok {
    57  		var builder strings.Builder
    58  		builder.WriteString(v.stack)
    59  		builder.WriteString("\n")
    60  		e := errors.Unwrap(v.err)
    61  		for e != nil {
    62  			w, ok := e.(wError)
    63  			if ok {
    64  				e = w.err
    65  				builder.WriteString(w.stack)
    66  				builder.WriteString("\n")
    67  			} else {
    68  				builder.WriteString(e.Error())
    69  				builder.WriteString("\n")
    70  			}
    71  			e = errors.Unwrap(e)
    72  		}
    73  		return builder.String()[:builder.Len()-2]
    74  	}
    75  	return w.Error()
    76  }
    77  func ReplaceAttr(_ []string, a slog.Attr) slog.Attr {
    78  	switch a.Value.Kind() {
    79  	case slog.KindAny:
    80  		switch v := a.Value.Any().(type) {
    81  		case wError:
    82  			a = slog.String("trace", ErrorWithFullStack(v))
    83  		}
    84  	}
    85  	return a
    86  }
    87  
    88  func FormatRecover() ([]byte, any) {
    89  	if r := recover(); r != nil {
    90  		const size = 65536
    91  		buf := make([]byte, size)
    92  		end := runtime.Stack(buf, false)
    93  		if end > size {
    94  			end = size
    95  		}
    96  		return buf[:end], r
    97  	}
    98  	return nil, nil
    99  }
   100  
   101  // https://zhuanlan.zhihu.com/p/82985617