Jelajahi Sumber

Better debug logging + unit tests

Manu Mtz-Almeida 10 tahun lalu
induk
melakukan
b690611c38
5 mengubah file dengan 99 tambahan dan 16 penghapusan
  1. 11 1
      debug.go
  2. 55 0
      debug_test.go
  3. 21 8
      errors.go
  4. 11 6
      gin.go
  5. 1 1
      routergroup.go

+ 11 - 1
debug.go

@@ -15,7 +15,7 @@ func IsDebugging() bool {
 	return ginMode == debugCode
 }
 
-func debugRoute(httpMethod, absolutePath string, handlers HandlersChain) {
+func debugPrintRoute(httpMethod, absolutePath string, handlers HandlersChain) {
 	if IsDebugging() {
 		nuHandlers := len(handlers)
 		handlerName := nameOfFunction(handlers[nuHandlers-1])
@@ -28,3 +28,13 @@ func debugPrint(format string, values ...interface{}) {
 		debugLogger.Printf(format, values...)
 	}
 }
+
+func debugPrintWARNING() {
+	debugPrint("[WARNING] Running in DEBUG mode! Disable it before going production\n")
+}
+
+func debugPrintError(err error) {
+	if err != nil {
+		debugPrint("[ERROR] %v\n", err)
+	}
+}

+ 55 - 0
debug_test.go

@@ -5,11 +5,17 @@
 package gin
 
 import (
+	"bytes"
+	"errors"
+	"io"
+	"log"
 	"testing"
 
 	"github.com/stretchr/testify/assert"
 )
 
+var cachedDebugLogger *log.Logger = nil
+
 // TODO
 // func debugRoute(httpMethod, absolutePath string, handlers HandlersChain) {
 // func debugPrint(format string, values ...interface{}) {
@@ -22,3 +28,52 @@ func TestIsDebugging(t *testing.T) {
 	SetMode(TestMode)
 	assert.False(t, IsDebugging())
 }
+
+func TestDebugPrint(t *testing.T) {
+	var w bytes.Buffer
+	setup(&w)
+	defer teardown()
+
+	SetMode(ReleaseMode)
+	debugPrint("DEBUG this!")
+	SetMode(TestMode)
+	debugPrint("DEBUG this!")
+	assert.Empty(t, w.String())
+
+	SetMode(DebugMode)
+	debugPrint("these are %d %s\n", 2, "error messages")
+	assert.Equal(t, w.String(), "[GIN-debug] these are 2 error messages\n")
+}
+
+func TestDebugPrintError(t *testing.T) {
+	var w bytes.Buffer
+	setup(&w)
+	defer teardown()
+
+	SetMode(DebugMode)
+	debugPrintError(nil)
+	assert.Empty(t, w.String())
+
+	debugPrintError(errors.New("this is an error"))
+	assert.Equal(t, w.String(), "[GIN-debug] [ERROR] this is an error\n")
+}
+
+func setup(w io.Writer) {
+	SetMode(DebugMode)
+	if cachedDebugLogger == nil {
+		cachedDebugLogger = debugLogger
+		debugLogger = log.New(w, debugLogger.Prefix(), 0)
+	} else {
+		panic("setup failed")
+	}
+}
+
+func teardown() {
+	SetMode(TestMode)
+	if cachedDebugLogger != nil {
+		debugLogger = cachedDebugLogger
+		cachedDebugLogger = nil
+	} else {
+		panic("teardown failed")
+	}
+}

+ 21 - 8
errors.go

@@ -10,16 +10,19 @@ import (
 )
 
 const (
-	ErrorTypeInternal = 1 << iota
-	ErrorTypeExternal = 1 << iota
-	ErrorTypeAll      = 0xffffffff
+	ErrorTypePrivate = 1 << iota
+	ErrorTypePublic  = 1 << iota
+)
+
+const (
+	ErrorMaskAny = 0xffffffff
 )
 
 // Used internally to collect errors that occurred during an http request.
 type errorMsg struct {
-	Err  string      `json:"error"`
-	Type int         `json:"-"`
-	Meta interface{} `json:"meta"`
+	Error error       `json:"error"`
+	Type  int         `json:"-"`
+	Meta  interface{} `json:"meta"`
 }
 
 type errorMsgs []errorMsg
@@ -37,14 +40,24 @@ func (a errorMsgs) ByType(typ int) errorMsgs {
 	return result
 }
 
+func (a errorMsgs) Errors() []string {
+	if len(a) == 0 {
+		return []string{}
+	}
+	errors := make([]string, len(a))
+	for i, err := range a {
+		errors[i] = err.Error.Error()
+	}
+	return errors
+}
+
 func (a errorMsgs) String() string {
 	if len(a) == 0 {
 		return ""
 	}
 	var buffer bytes.Buffer
 	for i, msg := range a {
-		text := fmt.Sprintf("Error #%02d: %s\n     Meta: %v\n", (i + 1), msg.Err, msg.Meta)
-		buffer.WriteString(text)
+		fmt.Fprintf(&buffer, "Error #%02d: %s\n     Meta: %v\n", (i + 1), msg.Error, msg.Meta)
 	}
 	return buffer.String()
 }

+ 11 - 6
gin.go

@@ -62,6 +62,7 @@ type (
 // Returns a new blank Engine instance without any middleware attached.
 // The most basic configuration
 func New() *Engine {
+	debugPrintWARNING()
 	engine := &Engine{
 		RouterGroup: RouterGroup{
 			Handlers:     nil,
@@ -156,16 +157,20 @@ func (engine *Engine) handle(method, path string, handlers HandlersChain) {
 	root.addRoute(path, handlers)
 }
 
-func (engine *Engine) Run(addr string) error {
-	debugPrint("[WARNING] Running in DEBUG mode! Disable it before going production")
+func (engine *Engine) Run(addr string) (err error) {
 	debugPrint("Listening and serving HTTP on %s\n", addr)
-	return http.ListenAndServe(addr, engine)
+	defer debugPrintError(err)
+
+	err = http.ListenAndServe(addr, engine)
+	return
 }
 
-func (engine *Engine) RunTLS(addr string, cert string, key string) error {
-	debugPrint("[WARNING] Running in DEBUG mode! Disable it before going production")
+func (engine *Engine) RunTLS(addr string, cert string, key string) (err error) {
 	debugPrint("Listening and serving HTTPS on %s\n", addr)
-	return http.ListenAndServeTLS(addr, cert, key, engine)
+	defer debugPrintError(err)
+
+	err = http.ListenAndServe(addr, engine)
+	return
 }
 
 // ServeHTTP makes the router implement the http.Handler interface.

+ 1 - 1
routergroup.go

@@ -45,7 +45,7 @@ func (group *RouterGroup) Group(relativePath string, handlers ...HandlerFunc) *R
 func (group *RouterGroup) Handle(httpMethod, relativePath string, handlers HandlersChain) {
 	absolutePath := group.calculateAbsolutePath(relativePath)
 	handlers = group.combineHandlers(handlers)
-	debugRoute(httpMethod, absolutePath, handlers)
+	debugPrintRoute(httpMethod, absolutePath, handlers)
 	group.engine.handle(httpMethod, absolutePath, handlers)
 }