github.com/hellobchain/third_party@v0.0.0-20230331131523-deb0478a2e52/gin/logger.go (about)

     1  // Copyright 2014 Manu Martinez-Almeida.  All rights reserved.
     2  // Use of this source code is governed by a MIT style
     3  // license that can be found in the LICENSE file.
     4  
     5  package gin
     6  
     7  import (
     8  	"fmt"
     9  	"github.com/hellobchain/newcryptosm/http"
    10  	"io"
    11  	"os"
    12  	"time"
    13  
    14  	"github.com/mattn/go-isatty"
    15  )
    16  
    17  type consoleColorModeValue int
    18  
    19  const (
    20  	autoColor consoleColorModeValue = iota
    21  	disableColor
    22  	forceColor
    23  )
    24  
    25  const (
    26  	green   = "\033[97;42m"
    27  	white   = "\033[90;47m"
    28  	yellow  = "\033[90;43m"
    29  	red     = "\033[97;41m"
    30  	blue    = "\033[97;44m"
    31  	magenta = "\033[97;45m"
    32  	cyan    = "\033[97;46m"
    33  	reset   = "\033[0m"
    34  )
    35  
    36  var consoleColorMode = autoColor
    37  
    38  // LoggerConfig defines the config for Logger middleware.
    39  type LoggerConfig struct {
    40  	// Optional. Default value is gin.defaultLogFormatter
    41  	Formatter LogFormatter
    42  
    43  	// Output is a writer where logs are written.
    44  	// Optional. Default value is gin.DefaultWriter.
    45  	Output io.Writer
    46  
    47  	// SkipPaths is a url path array which logs are not written.
    48  	// Optional.
    49  	SkipPaths []string
    50  }
    51  
    52  // LogFormatter gives the signature of the formatter function passed to LoggerWithFormatter
    53  type LogFormatter func(params LogFormatterParams) string
    54  
    55  // LogFormatterParams is the structure any formatter will be handed when time to log comes
    56  type LogFormatterParams struct {
    57  	Request *http.Request
    58  
    59  	// TimeStamp shows the time after the server returns a response.
    60  	TimeStamp time.Time
    61  	// StatusCode is HTTP response code.
    62  	StatusCode int
    63  	// Latency is how much time the server cost to process a certain request.
    64  	Latency time.Duration
    65  	// ClientIP equals Context's ClientIP method.
    66  	ClientIP string
    67  	// Method is the HTTP method given to the request.
    68  	Method string
    69  	// Path is a path the client requests.
    70  	Path string
    71  	// ErrorMessage is set if error has occurred in processing the request.
    72  	ErrorMessage string
    73  	// isTerm shows whether does gin's output descriptor refers to a terminal.
    74  	isTerm bool
    75  	// BodySize is the size of the Response Body
    76  	BodySize int
    77  	// Keys are the keys set on the request's context.
    78  	Keys map[string]interface{}
    79  }
    80  
    81  // StatusCodeColor is the ANSI color for appropriately logging http status code to a terminal.
    82  func (p *LogFormatterParams) StatusCodeColor() string {
    83  	code := p.StatusCode
    84  
    85  	switch {
    86  	case code >= http.StatusOK && code < http.StatusMultipleChoices:
    87  		return green
    88  	case code >= http.StatusMultipleChoices && code < http.StatusBadRequest:
    89  		return white
    90  	case code >= http.StatusBadRequest && code < http.StatusInternalServerError:
    91  		return yellow
    92  	default:
    93  		return red
    94  	}
    95  }
    96  
    97  // MethodColor is the ANSI color for appropriately logging http method to a terminal.
    98  func (p *LogFormatterParams) MethodColor() string {
    99  	method := p.Method
   100  
   101  	switch method {
   102  	case http.MethodGet:
   103  		return blue
   104  	case http.MethodPost:
   105  		return cyan
   106  	case http.MethodPut:
   107  		return yellow
   108  	case http.MethodDelete:
   109  		return red
   110  	case http.MethodPatch:
   111  		return green
   112  	case http.MethodHead:
   113  		return magenta
   114  	case http.MethodOptions:
   115  		return white
   116  	default:
   117  		return reset
   118  	}
   119  }
   120  
   121  // ResetColor resets all escape attributes.
   122  func (p *LogFormatterParams) ResetColor() string {
   123  	return reset
   124  }
   125  
   126  // IsOutputColor indicates whether can colors be outputted to the log.
   127  func (p *LogFormatterParams) IsOutputColor() bool {
   128  	return consoleColorMode == forceColor || (consoleColorMode == autoColor && p.isTerm)
   129  }
   130  
   131  // defaultLogFormatter is the default log format function Logger middleware uses.
   132  var defaultLogFormatter = func(param LogFormatterParams) string {
   133  	var statusColor, methodColor, resetColor string
   134  	if param.IsOutputColor() {
   135  		statusColor = param.StatusCodeColor()
   136  		methodColor = param.MethodColor()
   137  		resetColor = param.ResetColor()
   138  	}
   139  
   140  	if param.Latency > time.Minute {
   141  		// Truncate in a golang < 1.8 safe way
   142  		param.Latency = param.Latency - param.Latency%time.Second
   143  	}
   144  	return fmt.Sprintf("[GIN] %v |%s %3d %s| %13v | %15s |%s %-7s %s %#v\n%s",
   145  		param.TimeStamp.Format("2006/01/02 - 15:04:05"),
   146  		statusColor, param.StatusCode, resetColor,
   147  		param.Latency,
   148  		param.ClientIP,
   149  		methodColor, param.Method, resetColor,
   150  		param.Path,
   151  		param.ErrorMessage,
   152  	)
   153  }
   154  
   155  // DisableConsoleColor disables color output in the console.
   156  func DisableConsoleColor() {
   157  	consoleColorMode = disableColor
   158  }
   159  
   160  // ForceConsoleColor force color output in the console.
   161  func ForceConsoleColor() {
   162  	consoleColorMode = forceColor
   163  }
   164  
   165  // ErrorLogger returns a handlerfunc for any error type.
   166  func ErrorLogger() HandlerFunc {
   167  	return ErrorLoggerT(ErrorTypeAny)
   168  }
   169  
   170  // ErrorLoggerT returns a handlerfunc for a given error type.
   171  func ErrorLoggerT(typ ErrorType) HandlerFunc {
   172  	return func(c *Context) {
   173  		c.Next()
   174  		errors := c.Errors.ByType(typ)
   175  		if len(errors) > 0 {
   176  			c.JSON(-1, errors)
   177  		}
   178  	}
   179  }
   180  
   181  // Logger instances a Logger middleware that will write the logs to gin.DefaultWriter.
   182  // By default gin.DefaultWriter = os.Stdout.
   183  func Logger() HandlerFunc {
   184  	return LoggerWithConfig(LoggerConfig{})
   185  }
   186  
   187  // LoggerWithFormatter instance a Logger middleware with the specified log format function.
   188  func LoggerWithFormatter(f LogFormatter) HandlerFunc {
   189  	return LoggerWithConfig(LoggerConfig{
   190  		Formatter: f,
   191  	})
   192  }
   193  
   194  // LoggerWithWriter instance a Logger middleware with the specified writer buffer.
   195  // Example: os.Stdout, a file opened in write mode, a socket...
   196  func LoggerWithWriter(out io.Writer, notlogged ...string) HandlerFunc {
   197  	return LoggerWithConfig(LoggerConfig{
   198  		Output:    out,
   199  		SkipPaths: notlogged,
   200  	})
   201  }
   202  
   203  // LoggerWithConfig instance a Logger middleware with config.
   204  func LoggerWithConfig(conf LoggerConfig) HandlerFunc {
   205  	formatter := conf.Formatter
   206  	if formatter == nil {
   207  		formatter = defaultLogFormatter
   208  	}
   209  
   210  	out := conf.Output
   211  	if out == nil {
   212  		out = DefaultWriter
   213  	}
   214  
   215  	notlogged := conf.SkipPaths
   216  
   217  	isTerm := true
   218  
   219  	if w, ok := out.(*os.File); !ok || os.Getenv("TERM") == "dumb" ||
   220  		(!isatty.IsTerminal(w.Fd()) && !isatty.IsCygwinTerminal(w.Fd())) {
   221  		isTerm = false
   222  	}
   223  
   224  	var skip map[string]struct{}
   225  
   226  	if length := len(notlogged); length > 0 {
   227  		skip = make(map[string]struct{}, length)
   228  
   229  		for _, path := range notlogged {
   230  			skip[path] = struct{}{}
   231  		}
   232  	}
   233  
   234  	return func(c *Context) {
   235  		// Start timer
   236  		start := time.Now()
   237  		path := c.Request.URL.Path
   238  		raw := c.Request.URL.RawQuery
   239  
   240  		// Process request
   241  		c.Next()
   242  
   243  		// Log only when path is not being skipped
   244  		if _, ok := skip[path]; !ok {
   245  			param := LogFormatterParams{
   246  				Request: c.Request,
   247  				isTerm:  isTerm,
   248  				Keys:    c.Keys,
   249  			}
   250  
   251  			// Stop timer
   252  			param.TimeStamp = time.Now()
   253  			param.Latency = param.TimeStamp.Sub(start)
   254  
   255  			param.ClientIP = c.ClientIP()
   256  			param.Method = c.Request.Method
   257  			param.StatusCode = c.Writer.Status()
   258  			param.ErrorMessage = c.Errors.ByType(ErrorTypePrivate).String()
   259  
   260  			param.BodySize = c.Writer.Size()
   261  
   262  			if raw != "" {
   263  				path = path + "?" + raw
   264  			}
   265  
   266  			param.Path = path
   267  
   268  			fmt.Fprint(out, formatter(param))
   269  		}
   270  	}
   271  }