debug.go 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. package neffos
  2. import (
  3. "log"
  4. "os"
  5. "reflect"
  6. )
  7. var debugPrinter interface{}
  8. // EnableDebug enables debug and optionally
  9. // sets a custom printer to print out debug messages.
  10. // The "printer" can be any compatible printer such as
  11. // the standard `log.Logger` or a custom one like the `kataras/golog`.
  12. //
  13. // A "printer" is compatible when it contains AT LEAST ONE of the following methods:
  14. // Debugf(string, ...interface{}) or
  15. // Logf(string, ...interface{}) or
  16. // Printf(string, ...interface{})
  17. //
  18. // If EnableDebug is called but the "printer" value is nil
  19. // then neffos will print debug messages through a new log.Logger prefixed with "| neffos |".
  20. //
  21. // Note that neffos, currently, uses debug mode only on the build state of the events.
  22. // Therefore enabling the debugger has zero performance cost on up-and-running servers and clients.
  23. //
  24. // There is no way to disable the debug mode on serve-time.
  25. func EnableDebug(printer interface{}) {
  26. if debugEnabled() {
  27. Debugf("debug mode is already set")
  28. return
  29. }
  30. if _, boolean := printer.(bool); boolean {
  31. // if for some reason by accident EnableDebug(true) instead of a printer value.
  32. printer = nil
  33. }
  34. if printer == nil {
  35. logger := log.New(os.Stderr, "| neffos | ", 0)
  36. printer = logger
  37. logger.Println("debug mode is set")
  38. }
  39. debugPrinter = printer
  40. }
  41. type (
  42. debugfer interface {
  43. Debugf(string, ...interface{})
  44. }
  45. logfer interface {
  46. Logf(string, ...interface{})
  47. }
  48. printfer interface {
  49. Printf(string, ...interface{})
  50. }
  51. )
  52. func debugEnabled() bool {
  53. return debugPrinter != nil
  54. }
  55. // Debugf prints debug messages to the printer defined on `EnableDebug`.
  56. // Runs only on debug mode.
  57. func Debugf(format string, args ...interface{}) {
  58. if !debugEnabled() {
  59. return
  60. }
  61. if len(args) == 1 {
  62. // handles:
  63. // Debugf("format", func() dargs {
  64. // time-consumed action that should run only on debug.
  65. // })
  66. if onDebugWithArgs, ok := args[0].(func() dargs); ok {
  67. args = onDebugWithArgs()
  68. }
  69. }
  70. switch printer := debugPrinter.(type) {
  71. case debugfer:
  72. printer.Debugf(format, args...)
  73. case logfer:
  74. printer.Logf(format, args...)
  75. case printfer:
  76. printer.Printf(format, args...)
  77. default:
  78. panic("unsported debug printer")
  79. }
  80. }
  81. type dargs []interface{}
  82. // DebugEach prints debug messages for each of "mapOrSlice" elements
  83. // to the printer defined on `EnableDebug`.
  84. // Runs only on debug mode.
  85. // Usage:
  86. //
  87. // DebugEach(staticFields, func(idx int, f reflect.Value) {
  88. // fval := f.Interface()
  89. // Debugf("field [%s.%s] will be automatically re-filled with [%T(%s)]", typ.Name(), typ.Field(idx).Name, fval, fval)
  90. // })
  91. func DebugEach(mapOrSlice interface{}, onDebugVisitor interface{}) {
  92. if !debugEnabled() || onDebugVisitor == nil {
  93. return
  94. }
  95. visitor := reflect.ValueOf(onDebugVisitor)
  96. visitorTyp := visitor.Type()
  97. if visitorTyp.Kind() != reflect.Func {
  98. return
  99. }
  100. userNumIn := visitorTyp.NumIn()
  101. v := reflect.ValueOf(mapOrSlice)
  102. switch v.Kind() {
  103. case reflect.Map:
  104. for ranger := v.MapRange(); ranger.Next(); {
  105. in := make([]reflect.Value, userNumIn)
  106. in[0] = ranger.Key()
  107. if userNumIn > 1 {
  108. // assume both key and value are expected.
  109. in[1] = ranger.Value()
  110. }
  111. // note that we don't make any further checks here, it's only for internal
  112. // use and I want to panic in my tests if I didn't expect the correct values.
  113. visitor.Call(in)
  114. }
  115. case reflect.Slice:
  116. // TODO: whenever I need this.
  117. }
  118. }