-
Notifications
You must be signed in to change notification settings - Fork 0
/
utils_tracing.go
144 lines (121 loc) · 3.04 KB
/
utils_tracing.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
package glog
import (
"fmt"
"runtime"
"strings"
)
const (
STACK_TRACER_BASE_DEPTH = 3 // this controls how many frames to skip, so we can avoid to expose ourselves in every stack trace
)
type StackTraceFrame struct {
fn string
file string
line int
}
type StackTracer struct {
frames []*StackTraceFrame
// depth int
// maxDepth int
// baseDepth int
maxLenFn int // used to determine the maximum length of a method name in the stack trace
maxLenFile int // used to determine the maximum length of a file name in the stack trace
numFrames int
}
func (st *StackTracer) addFrame(frame runtime.Frame) {
fnName := ""
if frame.Func != nil {
fnName = frame.Func.Name()
} else {
// if frame.Func is nil we can still get the name from frame.Function
path := strings.Split(frame.Function, "/")
fnName = path[len(path)-1]
}
if strings.Contains(fnName, "glog.(*GError") {
return // let's not add ourselves
}
stf := &StackTraceFrame{
fn: fnName,
file: frame.File,
line: frame.Line,
}
st.maxLenFile = Max(st.maxLenFile, len(stf.file)+2)
st.maxLenFn = Max(st.maxLenFn, len(stf.fn)+2)
st.frames = append(st.frames, stf)
st.numFrames++
}
func (st *StackTracer) reset() {
st.maxLenFile = 0
st.maxLenFn = 0
st.numFrames = 0
st.frames = []*StackTraceFrame{}
}
// Sample repopulates the stack trace with the given `maxNumFrames`.
//
// `maxNumFrames` = 0 will sample all frames.
func (st *StackTracer) Sample(maxNumFrames int) *StackTracer {
st.reset()
allFrames := maxNumFrames == 0
pc := make([]uintptr, STACK_TRACER_BASE_DEPTH+100)
n := runtime.Callers(STACK_TRACER_BASE_DEPTH, pc)
if n == 0 {
return st // avoid processing zero frame in frames.Next() call below
}
frames := runtime.CallersFrames(pc[:n])
for {
frame, more := frames.Next() // get next frame, more == false indicates the last frame
st.addFrame(frame)
if !more || (!allFrames && st.numFrames >= maxNumFrames) {
break // no more frames to process or we have produced all that were requested
}
}
return st
}
func (st *StackTracer) getLines() []string {
res := []string{}
const indent = " "
const marker = "└──"
li := len(indent)
prefix := ""
maxPadLen := (len(st.frames) - 1) * li
paddedFnLen := st.maxLenFn + maxPadLen
for i, stf := range st.frames {
if i > 1 {
prefix = indent + prefix
paddedFnLen -= li
} else if i == 0 {
prefix = ""
paddedFnLen += li
} else if i == 1 {
prefix = marker
paddedFnLen -= li
}
pr := strings.Repeat("∙", Max(0, paddedFnLen-len(stf.fn)))
c := LoggerConfig.ColorIndicatorDebug - i
res = append(res,
fmt.Sprintf(
"%s%s %s %s:%s",
Wrap(prefix, c),
Auto(stf.fn),
Wrap(pr, c),
File(stf.file),
Int(stf.line),
),
)
}
return res
}
func (st *StackTracer) PrintWithLogger(l *Logger, indicator rune) {
lines := st.getLines()
for _, line := range lines {
l.write(indicator, "%s", line)
}
}
func NewStackTracer() *StackTracer {
st := &StackTracer{
frames: []*StackTraceFrame{},
maxLenFn: 0,
maxLenFile: 0,
numFrames: 0,
}
return st
}