gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

logger.go (6785B)


      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 	"io"
     10 	"net/http"
     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 an 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 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]any
     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 		param.Latency = param.Latency.Truncate(time.Second)
    142 	}
    143 	return fmt.Sprintf("[GIN] %v |%s %3d %s| %13v | %15s |%s %-7s %s %#v\n%s",
    144 		param.TimeStamp.Format("2006/01/02 - 15:04:05"),
    145 		statusColor, param.StatusCode, resetColor,
    146 		param.Latency,
    147 		param.ClientIP,
    148 		methodColor, param.Method, resetColor,
    149 		param.Path,
    150 		param.ErrorMessage,
    151 	)
    152 }
    153 
    154 // DisableConsoleColor disables color output in the console.
    155 func DisableConsoleColor() {
    156 	consoleColorMode = disableColor
    157 }
    158 
    159 // ForceConsoleColor force color output in the console.
    160 func ForceConsoleColor() {
    161 	consoleColorMode = forceColor
    162 }
    163 
    164 // ErrorLogger returns a HandlerFunc for any error type.
    165 func ErrorLogger() HandlerFunc {
    166 	return ErrorLoggerT(ErrorTypeAny)
    167 }
    168 
    169 // ErrorLoggerT returns a HandlerFunc for a given error type.
    170 func ErrorLoggerT(typ ErrorType) HandlerFunc {
    171 	return func(c *Context) {
    172 		c.Next()
    173 		errors := c.Errors.ByType(typ)
    174 		if len(errors) > 0 {
    175 			c.JSON(-1, errors)
    176 		}
    177 	}
    178 }
    179 
    180 // Logger instances a Logger middleware that will write the logs to gin.DefaultWriter.
    181 // By default, gin.DefaultWriter = os.Stdout.
    182 func Logger() HandlerFunc {
    183 	return LoggerWithConfig(LoggerConfig{})
    184 }
    185 
    186 // LoggerWithFormatter instance a Logger middleware with the specified log format function.
    187 func LoggerWithFormatter(f LogFormatter) HandlerFunc {
    188 	return LoggerWithConfig(LoggerConfig{
    189 		Formatter: f,
    190 	})
    191 }
    192 
    193 // LoggerWithWriter instance a Logger middleware with the specified writer buffer.
    194 // Example: os.Stdout, a file opened in write mode, a socket...
    195 func LoggerWithWriter(out io.Writer, notlogged ...string) HandlerFunc {
    196 	return LoggerWithConfig(LoggerConfig{
    197 		Output:    out,
    198 		SkipPaths: notlogged,
    199 	})
    200 }
    201 
    202 // LoggerWithConfig instance a Logger middleware with config.
    203 func LoggerWithConfig(conf LoggerConfig) HandlerFunc {
    204 	formatter := conf.Formatter
    205 	if formatter == nil {
    206 		formatter = defaultLogFormatter
    207 	}
    208 
    209 	out := conf.Output
    210 	if out == nil {
    211 		out = DefaultWriter
    212 	}
    213 
    214 	notlogged := conf.SkipPaths
    215 
    216 	isTerm := true
    217 
    218 	if w, ok := out.(*os.File); !ok || os.Getenv("TERM") == "dumb" ||
    219 		(!isatty.IsTerminal(w.Fd()) && !isatty.IsCygwinTerminal(w.Fd())) {
    220 		isTerm = false
    221 	}
    222 
    223 	var skip map[string]struct{}
    224 
    225 	if length := len(notlogged); length > 0 {
    226 		skip = make(map[string]struct{}, length)
    227 
    228 		for _, path := range notlogged {
    229 			skip[path] = struct{}{}
    230 		}
    231 	}
    232 
    233 	return func(c *Context) {
    234 		// Start timer
    235 		start := time.Now()
    236 		path := c.Request.URL.Path
    237 		raw := c.Request.URL.RawQuery
    238 
    239 		// Process request
    240 		c.Next()
    241 
    242 		// Log only when path is not being skipped
    243 		if _, ok := skip[path]; !ok {
    244 			param := LogFormatterParams{
    245 				Request: c.Request,
    246 				isTerm:  isTerm,
    247 				Keys:    c.Keys,
    248 			}
    249 
    250 			// Stop timer
    251 			param.TimeStamp = time.Now()
    252 			param.Latency = param.TimeStamp.Sub(start)
    253 
    254 			param.ClientIP = c.ClientIP()
    255 			param.Method = c.Request.Method
    256 			param.StatusCode = c.Writer.Status()
    257 			param.ErrorMessage = c.Errors.ByType(ErrorTypePrivate).String()
    258 
    259 			param.BodySize = c.Writer.Size()
    260 
    261 			if raw != "" {
    262 				path = path + "?" + raw
    263 			}
    264 
    265 			param.Path = path
    266 
    267 			fmt.Fprint(out, formatter(param))
    268 		}
    269 	}
    270 }