-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtrace.go
More file actions
98 lines (89 loc) · 2.45 KB
/
trace.go
File metadata and controls
98 lines (89 loc) · 2.45 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
90
91
92
93
94
95
96
97
98
/*
Copyright (c) 2023-2026 Microbus LLC and various contributors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package errors
import (
"runtime"
"strings"
)
// traceCaller appends the stack location of the caller to the error's stack trace.
func traceCaller(err error) error {
if err == nil {
return nil
}
level := 1
tracedErr := Convert(err)
for {
file, function, line, ok := runtimeTrace(level)
if !ok {
return tracedErr
}
if strings.HasPrefix(function, "runtime.") || (strings.HasPrefix(function, "errors.") && !strings.HasPrefix(function, "errors.Test")) {
level++
continue
}
tracedErr.Stack = append(tracedErr.Stack, &StackFrame{
File: file,
Function: function,
Line: line,
})
return tracedErr
}
}
// traceFull appends the full stack to the error's stack trace, starting at the indicated level.
// Level 0 captures the location of the caller.
func traceFull(err error, level int) error {
if err == nil {
return nil
}
if level < 0 {
level = 0
}
tracedErr := Convert(err)
levels := level - 1
for {
levels++
file, function, line, ok := runtimeTrace(1 + levels)
if !ok {
break
}
if function == "errors.CatchPanic" {
break
}
if strings.HasPrefix(function, "runtime.") || (strings.HasPrefix(function, "errors.") && !strings.HasPrefix(function, "errors.Test")) {
continue
}
tracedErr.Stack = append(tracedErr.Stack, &StackFrame{
File: file,
Function: function,
Line: line,
})
}
return tracedErr
}
// runtimeTrace traces back by the amount of levels to retrieve the runtime information used for tracing.
func runtimeTrace(levels int) (file string, function string, line int, ok bool) {
pc, file, line, ok := runtime.Caller(levels + 1)
if !ok {
return "", "", 0, false
}
function = "?"
runtimeFunc := runtime.FuncForPC(pc)
if runtimeFunc != nil {
function = runtimeFunc.Name()
p := strings.LastIndex(function, "/")
if p >= 0 {
function = function[p+1:]
}
}
return file, function, line, ok
}