You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
96 lines
2.5 KiB
96 lines
2.5 KiB
package util |
|
|
|
import ( |
|
"bytes" |
|
"fmt" |
|
"io/ioutil" |
|
"runtime" |
|
"strings" |
|
|
|
"github.com/liangdas/mqant/log" |
|
) |
|
|
|
// 捕获异常并打日志 |
|
// Usage: defer Recover() |
|
func Recover() { |
|
if err := recover(); err != nil { |
|
buf := make([]byte, 1024) |
|
runtime.Stack(buf, false) |
|
log.Error("panic(%+v), stack:\n%s", err, string(buf)) |
|
} |
|
} |
|
|
|
var ( |
|
dunno = []byte("???") |
|
centerDot = []byte("·") |
|
dot = []byte(".") |
|
slash = []byte("/") |
|
) |
|
|
|
// 和Recover差不多, 但是会舍弃最近两个没用的栈 |
|
func Catch(desc string) { |
|
if err := recover(); err != nil { |
|
log.Error("[%s] -- panic(%+v), stack:\n%s", desc, err, stack(3)) |
|
} |
|
} |
|
|
|
// stack returns a nicely formated stack frame, skipping skip frames |
|
func stack(skip int) []byte { |
|
buf := new(bytes.Buffer) // the returned data |
|
// As we loop, we open files and read them. These variables record the currently |
|
// loaded file. |
|
var lines [][]byte |
|
var lastFile string |
|
for i := skip; ; i = i + 1 { // Skip the expected number of frames |
|
pc, file, line, ok := runtime.Caller(i) |
|
if !ok || strings.HasSuffix(file, ".s") { |
|
break |
|
} |
|
// Print this much at least. If we can't find the source, it won't show. |
|
fmt.Fprintf(buf, "%s:%d (0x%x)\n", file, line, pc) |
|
if file != lastFile { |
|
data, err := ioutil.ReadFile(file) |
|
if err != nil { |
|
continue |
|
} |
|
lines = bytes.Split(data, []byte{'\n'}) |
|
lastFile = file |
|
} |
|
fmt.Fprintf(buf, "\t%s: %s\n", function(pc), source(lines, line)) |
|
} |
|
return buf.Bytes() |
|
} |
|
|
|
// source returns a space-trimmed slice of the n'th line. |
|
func source(lines [][]byte, n int) []byte { |
|
n = n - 1 // in stack trace, lines are 1-indexed but our array is 0-indexed |
|
if n < 0 || n >= len(lines) { |
|
return dunno |
|
} |
|
return bytes.TrimSpace(lines[n]) |
|
} |
|
|
|
// function returns, if possible, the name of the function containing the PC. |
|
func function(pc uintptr) []byte { |
|
fn := runtime.FuncForPC(pc) |
|
if fn == nil { |
|
return dunno |
|
} |
|
name := []byte(fn.Name()) |
|
// The name includes the path name to the package, which is unnecessary |
|
// since the file name is already included. Plus, it has center dots. |
|
// That is, we see |
|
// runtime/debug.*T·ptrmethod |
|
// and want |
|
// *T.ptrmethod |
|
// Also the package path might contains dot (e.g. code.google.com/...), |
|
// so first eliminate the path prefix |
|
if lastslash := bytes.LastIndex(name, slash); lastslash >= 0 { |
|
name = name[lastslash+1:] |
|
} |
|
if period := bytes.Index(name, dot); period >= 0 { |
|
name = name[period+1:] |
|
} |
|
name = bytes.Replace(name, centerDot, dot, -1) |
|
return name |
|
}
|
|
|