44 // %n function name |
44 // %n function name |
45 // %v equivalent to %s:%d |
45 // %v equivalent to %s:%d |
46 // |
46 // |
47 // Format accepts flags that alter the printing of some verbs, as follows: |
47 // Format accepts flags that alter the printing of some verbs, as follows: |
48 // |
48 // |
49 // %+s path of source file relative to the compile time GOPATH |
49 // %+s function name and path of source file relative to the compile time |
|
50 // GOPATH separated by \n\t (<funcname>\n\t<path>) |
50 // %+v equivalent to %+s:%d |
51 // %+v equivalent to %+s:%d |
51 func (f Frame) Format(s fmt.State, verb rune) { |
52 func (f Frame) Format(s fmt.State, verb rune) { |
52 switch verb { |
53 switch verb { |
53 case 's': |
54 case 's': |
54 switch { |
55 switch { |
77 } |
78 } |
78 |
79 |
79 // StackTrace is stack of Frames from innermost (newest) to outermost (oldest). |
80 // StackTrace is stack of Frames from innermost (newest) to outermost (oldest). |
80 type StackTrace []Frame |
81 type StackTrace []Frame |
81 |
82 |
|
83 // Format formats the stack of Frames according to the fmt.Formatter interface. |
|
84 // |
|
85 // %s lists source files for each Frame in the stack |
|
86 // %v lists the source file and line number for each Frame in the stack |
|
87 // |
|
88 // Format accepts flags that alter the printing of some verbs, as follows: |
|
89 // |
|
90 // %+v Prints filename, function, and line number for each Frame in the stack. |
82 func (st StackTrace) Format(s fmt.State, verb rune) { |
91 func (st StackTrace) Format(s fmt.State, verb rune) { |
83 switch verb { |
92 switch verb { |
84 case 'v': |
93 case 'v': |
85 switch { |
94 switch { |
86 case s.Flag('+'): |
95 case s.Flag('+'): |
134 i := strings.LastIndex(name, "/") |
143 i := strings.LastIndex(name, "/") |
135 name = name[i+1:] |
144 name = name[i+1:] |
136 i = strings.Index(name, ".") |
145 i = strings.Index(name, ".") |
137 return name[i+1:] |
146 return name[i+1:] |
138 } |
147 } |
139 |
|
140 func trimGOPATH(name, file string) string { |
|
141 // Here we want to get the source file path relative to the compile time |
|
142 // GOPATH. As of Go 1.6.x there is no direct way to know the compiled |
|
143 // GOPATH at runtime, but we can infer the number of path segments in the |
|
144 // GOPATH. We note that fn.Name() returns the function name qualified by |
|
145 // the import path, which does not include the GOPATH. Thus we can trim |
|
146 // segments from the beginning of the file path until the number of path |
|
147 // separators remaining is one more than the number of path separators in |
|
148 // the function name. For example, given: |
|
149 // |
|
150 // GOPATH /home/user |
|
151 // file /home/user/src/pkg/sub/file.go |
|
152 // fn.Name() pkg/sub.Type.Method |
|
153 // |
|
154 // We want to produce: |
|
155 // |
|
156 // pkg/sub/file.go |
|
157 // |
|
158 // From this we can easily see that fn.Name() has one less path separator |
|
159 // than our desired output. We count separators from the end of the file |
|
160 // path until it finds two more than in the function name and then move |
|
161 // one character forward to preserve the initial path segment without a |
|
162 // leading separator. |
|
163 const sep = "/" |
|
164 goal := strings.Count(name, sep) + 2 |
|
165 i := len(file) |
|
166 for n := 0; n < goal; n++ { |
|
167 i = strings.LastIndex(file[:i], sep) |
|
168 if i == -1 { |
|
169 // not enough separators found, set i so that the slice expression |
|
170 // below leaves file unmodified |
|
171 i = -len(sep) |
|
172 break |
|
173 } |
|
174 } |
|
175 // get back to 0 or trim the leading separator |
|
176 file = file[i+len(sep):] |
|
177 return file |
|
178 } |
|