gin_recov.go 2.0 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879
  1. package websubsvc
  2. import (
  3. "bytes"
  4. "fmt"
  5. "os"
  6. "runtime"
  7. "time"
  8. )
  9. var (
  10. dunno = []byte("???")
  11. centerDot = []byte("·")
  12. dot = []byte(".")
  13. slash = []byte("/")
  14. )
  15. func ginStack(skip int) []byte {
  16. buf := new(bytes.Buffer) // the returned data
  17. // As we loop, we open files and read them. These variables record the currently
  18. // loaded file.
  19. var lines [][]byte
  20. var lastFile string
  21. for i := skip; ; i++ { // Skip the expected number of frames
  22. pc, file, line, ok := runtime.Caller(i)
  23. if !ok {
  24. break
  25. }
  26. // Print this much at least. If we can't find the source, it won't show.
  27. fmt.Fprintf(buf, "%s:%d (0x%x)\n", file, line, pc)
  28. if file != lastFile {
  29. data, err := os.ReadFile(file)
  30. if err != nil {
  31. continue
  32. }
  33. lines = bytes.Split(data, []byte{'\n'})
  34. lastFile = file
  35. }
  36. fmt.Fprintf(buf, "\t%s: %s\n", ginFunction(pc), source(lines, line))
  37. }
  38. return buf.Bytes()
  39. }
  40. func source(lines [][]byte, n int) []byte {
  41. n-- // in stack trace, lines are 1-indexed but our array is 0-indexed
  42. if n < 0 || n >= len(lines) {
  43. return dunno
  44. }
  45. return bytes.TrimSpace(lines[n])
  46. }
  47. // function returns, if possible, the name of the function containing the PC.
  48. func ginFunction(pc uintptr) []byte {
  49. fn := runtime.FuncForPC(pc)
  50. if fn == nil {
  51. return dunno
  52. }
  53. name := []byte(fn.Name())
  54. // The name includes the path name to the package, which is unnecessary
  55. // since the file name is already included. Plus, it has center dots.
  56. // That is, we see
  57. // runtime/debug.*T·ptrmethod
  58. // and want
  59. // *T.ptrmethod
  60. // Also the package path might contain dot (e.g. code.google.com/...),
  61. // so first eliminate the path prefix
  62. if lastSlash := bytes.LastIndex(name, slash); lastSlash >= 0 {
  63. name = name[lastSlash+1:]
  64. }
  65. if period := bytes.Index(name, dot); period >= 0 {
  66. name = name[period+1:]
  67. }
  68. name = bytes.ReplaceAll(name, centerDot, dot)
  69. return name
  70. }
  71. // timeFormat returns a customized time string for logger.
  72. func ginTimeFormat(t time.Time) string {
  73. return t.Format("2006/01/02 - 15:04:05")
  74. }