podman
270 строк · 6.6 Кб
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
5package gin6
7import (8"fmt"9"io"10"net/http"11"os"12"time"13
14"github.com/mattn/go-isatty"15)
16
17type consoleColorModeValue int18
19const (20autoColor consoleColorModeValue = iota21disableColor
22forceColor
23)
24
25const (26green = "\033[97;42m"27white = "\033[90;47m"28yellow = "\033[90;43m"29red = "\033[97;41m"30blue = "\033[97;44m"31magenta = "\033[97;45m"32cyan = "\033[97;46m"33reset = "\033[0m"34)
35
36var consoleColorMode = autoColor37
38// LoggerConfig defines the config for Logger middleware.
39type LoggerConfig struct {40// Optional. Default value is gin.defaultLogFormatter41Formatter LogFormatter
42
43// Output is a writer where logs are written.44// Optional. Default value is gin.DefaultWriter.45Output io.Writer46
47// SkipPaths is an url path array which logs are not written.48// Optional.49SkipPaths []string50}
51
52// LogFormatter gives the signature of the formatter function passed to LoggerWithFormatter
53type LogFormatter func(params LogFormatterParams) string54
55// LogFormatterParams is the structure any formatter will be handed when time to log comes
56type LogFormatterParams struct {57Request *http.Request58
59// TimeStamp shows the time after the server returns a response.60TimeStamp time.Time61// StatusCode is HTTP response code.62StatusCode int63// Latency is how much time the server cost to process a certain request.64Latency time.Duration65// ClientIP equals Context's ClientIP method.66ClientIP string67// Method is the HTTP method given to the request.68Method string69// Path is a path the client requests.70Path string71// ErrorMessage is set if error has occurred in processing the request.72ErrorMessage string73// isTerm shows whether gin's output descriptor refers to a terminal.74isTerm bool75// BodySize is the size of the Response Body76BodySize int77// Keys are the keys set on the request's context.78Keys map[string]any79}
80
81// StatusCodeColor is the ANSI color for appropriately logging http status code to a terminal.
82func (p *LogFormatterParams) StatusCodeColor() string {83code := p.StatusCode84
85switch {86case code >= http.StatusOK && code < http.StatusMultipleChoices:87return green88case code >= http.StatusMultipleChoices && code < http.StatusBadRequest:89return white90case code >= http.StatusBadRequest && code < http.StatusInternalServerError:91return yellow92default:93return red94}95}
96
97// MethodColor is the ANSI color for appropriately logging http method to a terminal.
98func (p *LogFormatterParams) MethodColor() string {99method := p.Method100
101switch method {102case http.MethodGet:103return blue104case http.MethodPost:105return cyan106case http.MethodPut:107return yellow108case http.MethodDelete:109return red110case http.MethodPatch:111return green112case http.MethodHead:113return magenta114case http.MethodOptions:115return white116default:117return reset118}119}
120
121// ResetColor resets all escape attributes.
122func (p *LogFormatterParams) ResetColor() string {123return reset124}
125
126// IsOutputColor indicates whether can colors be outputted to the log.
127func (p *LogFormatterParams) IsOutputColor() bool {128return consoleColorMode == forceColor || (consoleColorMode == autoColor && p.isTerm)129}
130
131// defaultLogFormatter is the default log format function Logger middleware uses.
132var defaultLogFormatter = func(param LogFormatterParams) string {133var statusColor, methodColor, resetColor string134if param.IsOutputColor() {135statusColor = param.StatusCodeColor()136methodColor = param.MethodColor()137resetColor = param.ResetColor()138}139
140if param.Latency > time.Minute {141param.Latency = param.Latency.Truncate(time.Second)142}143return fmt.Sprintf("[GIN] %v |%s %3d %s| %13v | %15s |%s %-7s %s %#v\n%s",144param.TimeStamp.Format("2006/01/02 - 15:04:05"),145statusColor, param.StatusCode, resetColor,146param.Latency,147param.ClientIP,148methodColor, param.Method, resetColor,149param.Path,150param.ErrorMessage,151)152}
153
154// DisableConsoleColor disables color output in the console.
155func DisableConsoleColor() {156consoleColorMode = disableColor157}
158
159// ForceConsoleColor force color output in the console.
160func ForceConsoleColor() {161consoleColorMode = forceColor162}
163
164// ErrorLogger returns a HandlerFunc for any error type.
165func ErrorLogger() HandlerFunc {166return ErrorLoggerT(ErrorTypeAny)167}
168
169// ErrorLoggerT returns a HandlerFunc for a given error type.
170func ErrorLoggerT(typ ErrorType) HandlerFunc {171return func(c *Context) {172c.Next()173errors := c.Errors.ByType(typ)174if len(errors) > 0 {175c.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.
182func Logger() HandlerFunc {183return LoggerWithConfig(LoggerConfig{})184}
185
186// LoggerWithFormatter instance a Logger middleware with the specified log format function.
187func LoggerWithFormatter(f LogFormatter) HandlerFunc {188return LoggerWithConfig(LoggerConfig{189Formatter: 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...
195func LoggerWithWriter(out io.Writer, notlogged ...string) HandlerFunc {196return LoggerWithConfig(LoggerConfig{197Output: out,198SkipPaths: notlogged,199})200}
201
202// LoggerWithConfig instance a Logger middleware with config.
203func LoggerWithConfig(conf LoggerConfig) HandlerFunc {204formatter := conf.Formatter205if formatter == nil {206formatter = defaultLogFormatter207}208
209out := conf.Output210if out == nil {211out = DefaultWriter212}213
214notlogged := conf.SkipPaths215
216isTerm := true217
218if w, ok := out.(*os.File); !ok || os.Getenv("TERM") == "dumb" ||219(!isatty.IsTerminal(w.Fd()) && !isatty.IsCygwinTerminal(w.Fd())) {220isTerm = false221}222
223var skip map[string]struct{}224
225if length := len(notlogged); length > 0 {226skip = make(map[string]struct{}, length)227
228for _, path := range notlogged {229skip[path] = struct{}{}230}231}232
233return func(c *Context) {234// Start timer235start := time.Now()236path := c.Request.URL.Path237raw := c.Request.URL.RawQuery238
239// Process request240c.Next()241
242// Log only when path is not being skipped243if _, ok := skip[path]; !ok {244param := LogFormatterParams{245Request: c.Request,246isTerm: isTerm,247Keys: c.Keys,248}249
250// Stop timer251param.TimeStamp = time.Now()252param.Latency = param.TimeStamp.Sub(start)253
254param.ClientIP = c.ClientIP()255param.Method = c.Request.Method256param.StatusCode = c.Writer.Status()257param.ErrorMessage = c.Errors.ByType(ErrorTypePrivate).String()258
259param.BodySize = c.Writer.Size()260
261if raw != "" {262path = path + "?" + raw263}264
265param.Path = path266
267fmt.Fprint(out, formatter(param))268}269}270}
271