1
0

debug.go 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. package debug
  2. import (
  3. "fmt"
  4. "io"
  5. "math/rand"
  6. "os"
  7. "regexp"
  8. "strconv"
  9. "strings"
  10. "sync"
  11. "time"
  12. )
  13. var (
  14. writer io.Writer = os.Stderr
  15. reg *regexp.Regexp
  16. m sync.Mutex
  17. enabled = false
  18. )
  19. // Debugger function.
  20. type DebugFunction func(string, ...interface{})
  21. // Terminal colors used at random.
  22. var colors []string = []string{
  23. "31",
  24. "32",
  25. "33",
  26. "34",
  27. "35",
  28. "36",
  29. }
  30. // Initialize with DEBUG environment variable.
  31. func init() {
  32. env := os.Getenv("DEBUG")
  33. if "" != env {
  34. Enable(env)
  35. }
  36. }
  37. // SetWriter replaces the default of os.Stderr with `w`.
  38. func SetWriter(w io.Writer) {
  39. m.Lock()
  40. defer m.Unlock()
  41. writer = w
  42. }
  43. // Disable all pattern matching. This function is thread-safe.
  44. func Disable() {
  45. m.Lock()
  46. defer m.Unlock()
  47. enabled = false
  48. }
  49. // Enable the given debug `pattern`. Patterns take a glob-like form,
  50. // for example if you wanted to enable everything, just use "*", or
  51. // if you had a library named mongodb you could use "mongodb:connection",
  52. // or "mongodb:*". Multiple matches can be made with a comma, for
  53. // example "mongo*,redis*".
  54. //
  55. // This function is thread-safe.
  56. func Enable(pattern string) {
  57. m.Lock()
  58. defer m.Unlock()
  59. pattern = regexp.QuoteMeta(pattern)
  60. pattern = strings.Replace(pattern, "\\*", ".*?", -1)
  61. pattern = strings.Replace(pattern, ",", "|", -1)
  62. pattern = "^(" + pattern + ")$"
  63. reg = regexp.MustCompile(pattern)
  64. enabled = true
  65. }
  66. // Debug creates a debug function for `name` which you call
  67. // with printf-style arguments in your application or library.
  68. func Debug(name string) DebugFunction {
  69. prevGlobal := time.Now()
  70. color := colors[rand.Intn(len(colors))]
  71. prev := time.Now()
  72. return func(format string, args ...interface{}) {
  73. if !enabled {
  74. return
  75. }
  76. if !reg.MatchString(name) {
  77. return
  78. }
  79. d := deltas(prevGlobal, prev, color)
  80. fmt.Fprintf(writer, d+" \033["+color+"m"+name+"\033[0m - "+format+"\n", args...)
  81. prevGlobal = time.Now()
  82. prev = time.Now()
  83. }
  84. }
  85. // Return formatting for deltas.
  86. func deltas(prevGlobal, prev time.Time, color string) string {
  87. now := time.Now()
  88. global := now.Sub(prevGlobal).Nanoseconds()
  89. delta := now.Sub(prev).Nanoseconds()
  90. ts := now.UTC().Format("15:04:05.000")
  91. deltas := fmt.Sprintf("%s %-6s \033["+color+"m%-6s", ts, humanizeNano(global), humanizeNano(delta))
  92. return deltas
  93. }
  94. // Humanize nanoseconds to a string.
  95. func humanizeNano(n int64) string {
  96. var suffix string
  97. switch {
  98. case n > 1e9:
  99. n /= 1e9
  100. suffix = "s"
  101. case n > 1e6:
  102. n /= 1e6
  103. suffix = "ms"
  104. case n > 1e3:
  105. n /= 1e3
  106. suffix = "us"
  107. default:
  108. suffix = "ns"
  109. }
  110. return strconv.Itoa(int(n)) + suffix
  111. }