-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathcaller.go
More file actions
89 lines (77 loc) · 2.7 KB
/
Copy pathcaller.go
File metadata and controls
89 lines (77 loc) · 2.7 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
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
package logf
import (
"runtime"
"strconv"
"strings"
"unsafe"
)
// CallerPC captures the program counter of the caller, skipping the given
// number of stack frames. Returns 0 if the caller cannot be determined.
// You usually do not need to call this directly — the Logger handles it.
func CallerPC(skip int) uintptr {
var pcs [1]uintptr
if runtime.Callers(skip+2, pcs[:]) < 1 {
return 0
}
return pcs[0]
}
// callerFrame resolves a program counter to file and line.
func callerFrame(pc uintptr) (file string, line int) {
frames := runtime.CallersFrames([]uintptr{pc})
f, _ := frames.Next()
return f.File, f.Line
}
// fileWithPackage cuts a package name and a file name from a full file path.
//
// As for os-specific path separator battle here, my opinion coincides
// with the last comment here https://github.com/golang/go/issues/3335.
//
// Go team should simply document the current behavior of always using
// '/' in stack frame data. That's the way it's been implemented for
// years, and packages like github.com/go-stack/stack that have been
// stable for years expect it. Changing the behavior in a future version
// of Go will break working code without a clearly documented benefit.
// Documenting the behavior will help avoid new code from making the
// wrong assumptions.
func fileWithPackage(file string) string {
found := strings.LastIndexByte(file, '/')
if found == -1 {
return file
}
found = strings.LastIndexByte(file[:found], '/')
if found == -1 {
return file
}
return file[found+1:]
}
// CallerEncoder is a function that resolves a program counter and writes
// the caller location (file + line) into the log output via TypeEncoder.
type CallerEncoder func(pc uintptr, m TypeEncoder)
// ShortCallerEncoder formats the caller as "package/file.go:line" — compact
// enough for log output while still letting you find the source. This is
// the default CallerEncoder.
func ShortCallerEncoder(pc uintptr, m TypeEncoder) {
file, line := callerFrame(pc)
var callerBuf [64]byte
var b []byte
b = callerBuf[:0]
b = append(b, fileWithPackage(file)...)
b = append(b, ':')
b = strconv.AppendInt(b, int64(line), 10)
m.EncodeTypeUnsafeBytes(noescape(unsafe.Pointer(&b)))
runtime.KeepAlive(&b)
}
// FullCallerEncoder formats the caller as the full filesystem path with
// line number. More verbose than ShortCallerEncoder but unambiguous when
// you have multiple packages with the same file name.
func FullCallerEncoder(pc uintptr, m TypeEncoder) {
file, line := callerFrame(pc)
var callerBuf [256]byte
var b []byte
b = callerBuf[:0]
b = append(b, file...)
b = append(b, ':')
b = strconv.AppendInt(b, int64(line), 10)
m.EncodeTypeUnsafeBytes(noescape(unsafe.Pointer(&b)))
runtime.KeepAlive(&b)
}