forked from ikonglong/go-apperror
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathstack.go
More file actions
72 lines (66 loc) · 2.25 KB
/
Copy pathstack.go
File metadata and controls
72 lines (66 loc) · 2.25 KB
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
package apperror
import (
"runtime"
"strconv"
)
// maxStackDepth caps how many frames newAppError records. Deep enough to
// reach the inbound boundary from any realistic domain/application call
// site, shallow enough that runaway recursion can't bloat a single error.
const maxStackDepth = 32
// StackTrace is the sequence of program counters captured at the point an
// AppError was constructed, innermost (origin) frame first.
//
// It stores raw PCs rather than resolved frames on purpose: symbolizing a
// stack (file/line/function lookup) is comparatively expensive, and most
// errors are created on paths that never log a trace. Resolution is
// deferred to Frames / Strings, which the logging boundary calls only for
// the errors it actually decides to record.
type StackTrace []uintptr
// callers records the call stack at the point an AppError is built. skip is
// the number of leading frames to drop so the first recorded frame is the
// caller that constructed the error, not this package's own construction
// frames. It is marked noinline so the frame layout skip relies on stays
// fixed regardless of the optimizer.
//
//go:noinline
func callers(skip int) StackTrace {
var pcs [maxStackDepth]uintptr
n := runtime.Callers(skip, pcs[:])
st := make(StackTrace, n)
copy(st, pcs[:n])
return st
}
// Frames resolves the recorded program counters to runtime.Frame values,
// innermost first, expanding any inlined calls. Returns nil for an empty
// trace.
func (st StackTrace) Frames() []runtime.Frame {
if len(st) == 0 {
return nil
}
cf := runtime.CallersFrames(st)
out := make([]runtime.Frame, 0, len(st))
for {
f, more := cf.Next()
out = append(out, f)
if !more {
break
}
}
return out
}
// Strings renders the trace as one "file:line function" entry per frame,
// innermost first. Each entry is single-line by construction, so the result
// is safe to attach as a structured log field without tripping the
// no-newline-in-messages rule (see error-handling guidance). Returns nil
// for an empty trace.
func (st StackTrace) Strings() []string {
frames := st.Frames()
if len(frames) == 0 {
return nil
}
out := make([]string, len(frames))
for i, f := range frames {
out[i] = f.File + ":" + strconv.Itoa(f.Line) + " " + f.Function
}
return out
}