github.com/sohaha/zlsgo@v1.7.13-0.20240501141223-10dd1a906f76/zlog/debug.go (about)

     1  package zlog
     2  
     3  import (
     4  	"fmt"
     5  	"go/ast"
     6  	"go/parser"
     7  	"go/printer"
     8  	"go/token"
     9  	"io"
    10  	"reflect"
    11  	"strconv"
    12  	"strings"
    13  	"text/tabwriter"
    14  
    15  	"github.com/sohaha/zlsgo/zreflect"
    16  )
    17  
    18  type indentWriter struct {
    19  	w   io.Writer
    20  	pre [][]byte
    21  	sel int
    22  	off int
    23  	bol bool
    24  }
    25  
    26  func newIndentWriter(w io.Writer, pre ...[]byte) io.Writer {
    27  	return &indentWriter{
    28  		w:   w,
    29  		pre: pre,
    30  		bol: true,
    31  	}
    32  }
    33  
    34  func (w *indentWriter) Write(p []byte) (n int, err error) {
    35  	for _, c := range p {
    36  		if w.bol {
    37  			var i int
    38  			i, err = w.w.Write(w.pre[w.sel][w.off:])
    39  			w.off += i
    40  			if err != nil {
    41  				return n, err
    42  			}
    43  		}
    44  		_, err = w.w.Write([]byte{c})
    45  		if err != nil {
    46  			return n, err
    47  		}
    48  		n++
    49  		w.bol = c == '\n'
    50  		if w.bol {
    51  			w.off = 0
    52  			if w.sel < len(w.pre)-1 {
    53  				w.sel++
    54  			}
    55  		}
    56  	}
    57  	return n, nil
    58  }
    59  
    60  func argName(arg ast.Expr) string {
    61  	name := ""
    62  
    63  	switch a := arg.(type) {
    64  	case *ast.Ident:
    65  		switch {
    66  		case a.Obj == nil:
    67  			name = a.Name
    68  		case a.Obj.Kind == ast.Var, a.Obj.Kind == ast.Con:
    69  			name = a.Obj.Name
    70  		}
    71  	case *ast.BinaryExpr,
    72  		*ast.CallExpr,
    73  		*ast.IndexExpr,
    74  		*ast.KeyValueExpr,
    75  		*ast.ParenExpr,
    76  		*ast.SelectorExpr,
    77  		*ast.SliceExpr,
    78  		*ast.TypeAssertExpr,
    79  		*ast.UnaryExpr:
    80  		name = exprToString(arg)
    81  	}
    82  
    83  	return name
    84  }
    85  
    86  func argNames(filename string, line int) ([]string, error) {
    87  	fset := token.NewFileSet()
    88  	f, err := parser.ParseFile(fset, filename, nil, 0)
    89  	if err != nil {
    90  		return nil, fmt.Errorf("failed to parse %q: %v", filename, err)
    91  	}
    92  
    93  	var names []string
    94  	ast.Inspect(f, func(n ast.Node) bool {
    95  		call, is := n.(*ast.CallExpr)
    96  		if !is {
    97  			return true
    98  		}
    99  		if fset.Position(call.End()).Line != line {
   100  			return true
   101  		}
   102  		for _, arg := range call.Args {
   103  			names = append(names, argName(arg))
   104  		}
   105  		return true
   106  	})
   107  
   108  	return names, nil
   109  }
   110  
   111  func exprToString(arg ast.Expr) string {
   112  	var buf strings.Builder
   113  	fset := token.NewFileSet()
   114  	if err := printer.Fprint(&buf, fset, arg); err != nil {
   115  		return ""
   116  	}
   117  	return strings.Replace(buf.String(), "\t", "    ", -1)
   118  }
   119  
   120  func (fo formatter) String() string {
   121  	return fmt.Sprint(fo.v.Interface())
   122  }
   123  
   124  func (fo formatter) Format(f fmt.State, c rune) {
   125  	if fo.force || c == 'v' && f.Flag('#') && f.Flag(' ') {
   126  		w := tabwriter.NewWriter(f, 4, 4, 1, ' ', 0)
   127  		p := &zprinter{tw: w, Writer: w, visited: make(map[visit]int)}
   128  		p.printValue(fo.v, true, fo.quote)
   129  		_ = w.Flush()
   130  		return
   131  	}
   132  	fo.passThrough(f, c)
   133  }
   134  
   135  func (fo formatter) passThrough(f fmt.State, c rune) {
   136  	s := "%"
   137  	for i := 0; i < 128; i++ {
   138  		if f.Flag(i) {
   139  			s += strconv.FormatInt(int64(i), 10)
   140  		}
   141  	}
   142  	if w, ok := f.Width(); ok {
   143  		s += fmt.Sprintf("%d", w)
   144  	}
   145  	if p, ok := f.Precision(); ok {
   146  		s += fmt.Sprintf(".%d", p)
   147  	}
   148  	s += string(c)
   149  	_, _ = fmt.Fprintf(f, s, fo.v.Interface())
   150  }
   151  
   152  func (p *zprinter) indent() *zprinter {
   153  	q := *p
   154  	q.tw = tabwriter.NewWriter(p.Writer, 4, 4, 1, ' ', 0)
   155  	q.Writer = newIndentWriter(q.tw, []byte{'\t'})
   156  	return &q
   157  }
   158  
   159  func (p *zprinter) printInline(v reflect.Value, x interface{}, showType bool) {
   160  	if showType {
   161  		_, _ = io.WriteString(p, v.Type().String())
   162  		_, _ = fmt.Fprintf(p, "(%+v)", x)
   163  	} else {
   164  		_, _ = fmt.Fprintf(p, "%+v", x)
   165  	}
   166  }
   167  
   168  func (p *zprinter) printStruct(v reflect.Value, showType bool) (stop bool) {
   169  	t := v.Type()
   170  	if v.CanAddr() {
   171  		addr := v.UnsafeAddr()
   172  		vis := visit{v: addr, typ: t}
   173  		if vd, ok := p.visited[vis]; ok && vd < p.depth {
   174  			p.fmtString(t.String()+"{(CYCLIC REFERENCE)}", false)
   175  			return true
   176  		}
   177  		p.visited[vis] = p.depth
   178  	}
   179  
   180  	if showType {
   181  		_, _ = io.WriteString(p, t.String())
   182  	}
   183  
   184  	writeByte(p, '{')
   185  	if zreflect.Nonzero(v) {
   186  		expand := !zreflect.CanInline(v.Type())
   187  		pp := p
   188  		if expand {
   189  			writeByte(p, '\n')
   190  			pp = p.indent()
   191  		}
   192  		for i := 0; i < v.NumField(); i++ {
   193  			showTypeInStruct := true
   194  			if f := t.Field(i); f.Name != "" {
   195  				_, _ = io.WriteString(pp, f.Name)
   196  				writeByte(pp, ':')
   197  				if expand {
   198  					writeByte(pp, '\t')
   199  				}
   200  				showTypeInStruct = zreflect.IsLabel(f.Type)
   201  			}
   202  			val := v.Field(i)
   203  			if val.Kind() == reflect.Interface && !val.IsNil() {
   204  				val = val.Elem()
   205  			}
   206  			pp.printValue(val, showTypeInStruct, true)
   207  			if expand {
   208  				_, _ = io.WriteString(pp, ",\n")
   209  			} else if i < v.NumField()-1 {
   210  				_, _ = io.WriteString(pp, ", ")
   211  			}
   212  		}
   213  		if expand {
   214  			_ = pp.tw.Flush()
   215  		}
   216  	}
   217  	writeByte(p, '}')
   218  	return false
   219  }
   220  
   221  func (p *zprinter) printValue(v reflect.Value, showType, quote bool) {
   222  	if p.depth > 10 {
   223  		_, _ = io.WriteString(p, "!%v(DEPTH EXCEEDED)")
   224  		return
   225  	}
   226  	switch v.Kind() {
   227  	case reflect.Bool:
   228  		p.printInline(v, v.Bool(), showType)
   229  	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
   230  		p.printInline(v, v.Int(), showType)
   231  	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
   232  		p.printInline(v, v.Uint(), showType)
   233  	case reflect.Float32, reflect.Float64:
   234  		p.printInline(v, v.Float(), showType)
   235  	case reflect.Complex64, reflect.Complex128:
   236  		_, _ = fmt.Fprintf(p, "%#v", v.Complex())
   237  	case reflect.String:
   238  		p.fmtString(v.String(), quote)
   239  	case reflect.Map:
   240  		t := v.Type()
   241  		if showType {
   242  			_, _ = io.WriteString(p, t.String())
   243  		}
   244  		writeByte(p, '{')
   245  		if zreflect.Nonzero(v) {
   246  			expand := !zreflect.CanInline(v.Type())
   247  			pp := p
   248  			if expand {
   249  				writeByte(p, '\n')
   250  				pp = p.indent()
   251  			}
   252  			keys := v.MapKeys()
   253  			for i := 0; i < v.Len(); i++ {
   254  				k := keys[i]
   255  				mv := v.MapIndex(k)
   256  				pp.printValue(k, false, true)
   257  				writeByte(pp, ':')
   258  				if expand {
   259  					writeByte(pp, '\t')
   260  				}
   261  				showTypeInStruct := t.Elem().Kind() == reflect.Interface
   262  				pp.printValue(mv, showTypeInStruct, true)
   263  				if expand {
   264  					_, _ = io.WriteString(pp, ",\n")
   265  				} else if i < v.Len()-1 {
   266  					_, _ = io.WriteString(pp, ", ")
   267  				}
   268  			}
   269  			if expand {
   270  				_ = pp.tw.Flush()
   271  			}
   272  		}
   273  		writeByte(p, '}')
   274  	case reflect.Struct:
   275  		if p.printStruct(v, showType) {
   276  			break
   277  		}
   278  	case reflect.Interface:
   279  		switch e := v.Elem(); {
   280  		case e.Kind() == reflect.Invalid:
   281  			_, _ = io.WriteString(p, "nil")
   282  		case e.IsValid():
   283  			pp := *p
   284  			pp.depth++
   285  			pp.printValue(e, showType, true)
   286  		default:
   287  			_, _ = io.WriteString(p, v.Type().String())
   288  			_, _ = io.WriteString(p, "(nil)")
   289  		}
   290  	case reflect.Array, reflect.Slice:
   291  		t := v.Type()
   292  		if showType {
   293  			_, _ = io.WriteString(p, t.String())
   294  		}
   295  		if v.Kind() == reflect.Slice && v.IsNil() && showType {
   296  			_, _ = io.WriteString(p, "(nil)")
   297  			break
   298  		}
   299  		if v.Kind() == reflect.Slice && v.IsNil() {
   300  			_, _ = io.WriteString(p, "nil")
   301  			break
   302  		}
   303  		writeByte(p, '{')
   304  		expand := !zreflect.CanInline(v.Type())
   305  		pp := p
   306  		if expand {
   307  			writeByte(p, '\n')
   308  			pp = p.indent()
   309  		}
   310  		for i := 0; i < v.Len(); i++ {
   311  			showTypeInSlice := t.Elem().Kind() == reflect.Interface
   312  			pp.printValue(v.Index(i), showTypeInSlice, true)
   313  			if expand {
   314  				_, _ = io.WriteString(pp, ",\n")
   315  			} else if i < v.Len()-1 {
   316  				_, _ = io.WriteString(pp, ", ")
   317  			}
   318  		}
   319  		if expand {
   320  			_ = pp.tw.Flush()
   321  		}
   322  		writeByte(p, '}')
   323  	case reflect.Ptr:
   324  		e := v.Elem()
   325  		if !e.IsValid() {
   326  			writeByte(p, '(')
   327  			_, _ = io.WriteString(p, v.Type().String())
   328  			_, _ = io.WriteString(p, ")(nil)")
   329  		} else {
   330  			pp := *p
   331  			pp.depth++
   332  			writeByte(pp, '&')
   333  			pp.printValue(e, true, true)
   334  		}
   335  	case reflect.Chan:
   336  		x := v.Pointer()
   337  		if showType {
   338  			writeByte(p, '(')
   339  			_, _ = io.WriteString(p, v.Type().String())
   340  			_, _ = fmt.Fprintf(p, ")(%#v)", x)
   341  		} else {
   342  			_, _ = fmt.Fprintf(p, "%#v", x)
   343  		}
   344  	case reflect.Func:
   345  		_, _ = io.WriteString(p, v.Type().String())
   346  		_, _ = io.WriteString(p, " {...}")
   347  	case reflect.UnsafePointer:
   348  		p.printInline(v, v.Pointer(), showType)
   349  	case reflect.Invalid:
   350  		_, _ = io.WriteString(p, "nil")
   351  	}
   352  }
   353  
   354  func (p *zprinter) fmtString(s string, quote bool) {
   355  	if quote {
   356  		s = strconv.Quote(s)
   357  	}
   358  	_, _ = io.WriteString(p, s)
   359  }