-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcaller.go
127 lines (109 loc) · 2.88 KB
/
caller.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
package tower
import (
"encoding/json"
"os"
"runtime"
"strconv"
"strings"
"unicode"
)
const sep = string(os.PathSeparator)
type Caller interface {
// Function returns the function information.
Function() *runtime.Func
// Name returns the function name of the caller.
Name() string
// ShortName returns only function name of the caller.
ShortName() string
// ShortSource returns only the latest three items path in the File Path where the Caller comes from.
ShortSource() string
// String Sets this caller as `file_path:line` format.
String() string
// Line returns the line number of the caller.
Line() int
// File returns the file path of the caller.
File() string
// PC returns the program counter of the caller.
PC() uintptr
// FormatAsKey Like .String(), but runes other than letters, digits, `-` and `.` are set to `_`.
FormatAsKey() string
}
type caller struct {
pc uintptr
file string
line int
}
func (c caller) Name() string {
f := runtime.FuncForPC(c.pc)
return f.Name()
}
func (c caller) ShortName() string {
s := strings.Split(c.Name(), "/")
return s[len(s)-1]
}
func (c caller) Line() int {
return c.line
}
func (c caller) File() string {
return c.file
}
func (c caller) PC() uintptr {
return c.pc
}
func (c caller) MarshalJSON() ([]byte, error) {
return json.Marshal(c.String())
}
func (c caller) Function() *runtime.Func {
f := runtime.FuncForPC(c.pc)
return f
}
// ShortSource returns only the latest three items path in the File Path where the Caller comes from.
func (c caller) ShortSource() string {
s := strings.Split(c.file, sep)
for len(s) > 3 {
s = s[1:]
}
return strings.Join(s, sep)
}
// FormatAsKey Like .String(), but runes other than letters, digits, `-` and `.` are set to `_`.
func (c caller) FormatAsKey() string {
s := &strings.Builder{}
strLine := strconv.Itoa(c.line)
s.Grow(len(c.file) + 1 + len(strLine))
replaceSymbols(s, c.file, '_')
s.WriteRune('_')
s.WriteString(strLine)
return s.String()
}
// String Sets this caller as `file_path:line` format.
func (c caller) String() string {
s := &strings.Builder{}
strLine := strconv.Itoa(c.line)
s.Grow(len(c.file) + 1 + len(strLine))
s.WriteString(c.file)
s.WriteRune(':')
s.WriteString(strLine)
return s.String()
}
func replaceSymbols(builder *strings.Builder, s string, rep rune) {
for _, c := range s {
switch {
case unicode.In(c, unicode.Digit, unicode.Letter), c == '-', c == '.':
builder.WriteRune(c)
default:
builder.WriteRune(rep)
}
}
}
// GetCaller returns the caller information for who calls this function. A value of 1 will return this GetCaller location.
// So you may want the value to be 2 or higher if you wrap this call in another function.
//
// Returns zero value if the caller information cannot be obtained.
func GetCaller(depth int) Caller {
pc, file, line, _ := runtime.Caller(depth)
return &caller{
pc: pc,
file: file,
line: line,
}
}