// Copyright 2014 Manu Martinez-Almeida. All rights reserved. // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. package gin import ( "fmt" "io" "os" "time" "github.com/mattn/go-isatty" ) var ( green = string([]byte{27, 91, 57, 55, 59, 52, 50, 109}) white = string([]byte{27, 91, 57, 48, 59, 52, 55, 109}) yellow = string([]byte{27, 91, 57, 55, 59, 52, 51, 109}) red = string([]byte{27, 91, 57, 55, 59, 52, 49, 109}) blue = string([]byte{27, 91, 57, 55, 59, 52, 52, 109}) magenta = string([]byte{27, 91, 57, 55, 59, 52, 53, 109}) cyan = string([]byte{27, 91, 57, 55, 59, 52, 54, 109}) reset = string([]byte{27, 91, 48, 109}) disableColor = false ) // DisableConsoleColor disables color output in the console func DisableConsoleColor() { disableColor = true } // ErrorLogger returns a handlerfunc for any error type func ErrorLogger() HandlerFunc { return ErrorLoggerT(ErrorTypeAny) } // ErrorLoggerT returns a handlerfunc for a given error type func ErrorLoggerT(typ ErrorType) HandlerFunc { return func(c *Context) { c.Next() errors := c.Errors.ByType(typ) if len(errors) > 0 { c.JSON(-1, errors) } } } // Logger instances a Logger middleware that will write the logs to gin.DefaultWriter // By default gin.DefaultWriter = os.Stdout func Logger() HandlerFunc { return LoggerWithWriter(DefaultWriter) } // LoggerWithWriter instance a Logger middleware with the specified writter buffer. // Example: os.Stdout, a file opened in write mode, a socket... func LoggerWithWriter(out io.Writer, notlogged ...string) HandlerFunc { isTerm := true if w, ok := out.(*os.File); !ok || (os.Getenv("TERM") == "dumb" || (!isatty.IsTerminal(w.Fd()) && !isatty.IsCygwinTerminal(w.Fd()))) || disableColor { isTerm = false } var skip map[string]struct{} if length := len(notlogged); length > 0 { skip = make(map[string]struct{}, length) for _, path := range notlogged { skip[path] = struct{}{} } } return func(c *Context) { // Start timer start := time.Now() path := c.Request.URL.Path raw := c.Request.URL.RawQuery // Process request c.Next() // Log only when path is not being skipped if _, ok := skip[path]; !ok { // Stop timer end := time.Now() latency := end.Sub(start) clientIP := c.ClientIP() method := c.Request.Method statusCode := c.Writer.Status() var statusColor, methodColor string if isTerm { statusColor = colorForStatus(statusCode) methodColor = colorForMethod(method) } comment := c.Errors.ByType(ErrorTypePrivate).String() if raw != "" { path = path + "?" + raw } fmt.Fprintf(out, "[GIN] %v |%s %3d %s| %13v | %15s |%s %-7s %s %s\n%s", end.Format("2006/01/02 - 15:04:05"), statusColor, statusCode, reset, latency, clientIP, methodColor, method, reset, path, comment, ) } } } func colorForStatus(code int) string { switch { case code >= 200 && code < 300: return green case code >= 300 && code < 400: return white case code >= 400 && code < 500: return yellow default: return red } } func colorForMethod(method string) string { switch method { case "GET": return blue case "POST": return cyan case "PUT": return yellow case "DELETE": return red case "PATCH": return green case "HEAD": return magenta case "OPTIONS": return white default: return reset } }