github.com/angenalZZZ/gofunc@v0.0.0-20210507121333-48ff1be3917b/http/fast/middleware/logger/logging.go (about) 1 package logger 2 3 import ( 4 "bytes" 5 "fmt" 6 "github.com/angenalZZZ/gofunc/configfile" 7 "github.com/angenalZZZ/gofunc/f" 8 "github.com/angenalZZZ/gofunc/http/fast" 9 "github.com/angenalZZZ/gofunc/log" 10 "github.com/valyala/fasttemplate" 11 "io" 12 "strconv" 13 "strings" 14 "sync" 15 "time" 16 ) 17 18 // Config defines the config for logger middleware 19 type Config struct { 20 // Filter defines a function to skip middleware. 21 // Optional. Default: nil 22 Filter func(*fast.Ctx) bool 23 // Format defines the logging format with defined variables 24 // Optional. Default: "${time} - ${method} ${path} - ${ip}\t${ua}\n" 25 // Possible values: ip, url, host, method, path, protocol 26 // referer, ua, header:<key>, query:<key>, form:<key>, cookie:<key> 27 Format string 28 // Format json defines 29 // Optional Default: ip,method,path,status,latency,query,data 30 JsonFormat string 31 // Output is a writer where logs are written 32 // Default: log.Log 33 Output *log.Logger 34 // Output json defines with JsonFormat 35 JsonOutput bool 36 // ConfigFile log.yaml 37 // Optional if cfg.Output equals nil. 38 ConfigFile string 39 // Tag extension field. 40 Tag string 41 } 42 43 // New middleware. 44 // cfg := logger.Config{ 45 // JsonOutput: true, 46 // ConfigFile: "log.yaml", 47 // Tag: "1", 48 // } 49 // app.Use(logger.New(cfg)) 50 func New(config ...Config) func(*fast.Ctx) { 51 // Init config 52 var cfg Config 53 // SetHeader config if provided 54 if len(config) > 0 { 55 cfg = config[0] 56 } 57 // SetHeader config default values 58 if cfg.Format == "" { 59 cfg.Format = "${ip} ${method} ${path} -> ${status} - ${latency} <- ${query} -d ${data}" 60 } 61 if cfg.JsonFormat == "" { 62 cfg.JsonFormat = "ip,method,path,status,latency,query,data" 63 } 64 tags := strings.Split(cfg.JsonFormat, ",") 65 if cfg.Output == nil { 66 if cfg.ConfigFile == "" { 67 cfg.Output = log.Log 68 } else { 69 logCfg := new(log.AConfig) 70 if err := configfile.YamlTo(cfg.ConfigFile, logCfg); err != nil { 71 _ = fmt.Errorf("%s\n", err.Error()) 72 } 73 cfg.Output = log.Init(logCfg.Log) 74 if log.Log == nil { 75 log.Log = cfg.Output 76 } 77 } 78 } 79 // Middleware settings 80 tmpl := fasttemplate.New(cfg.Format, "${", "}") 81 pool := &sync.Pool{ 82 New: func() interface{} { 83 return bytes.NewBuffer(make([]byte, 256)) 84 }, 85 } 86 // Middleware function 87 return func(c *fast.Ctx) { 88 // Filter request to skip middleware 89 if cfg.Filter != nil && cfg.Filter(c) { 90 c.Next() 91 return 92 } 93 start := time.Now() 94 // handle request 95 c.Next() 96 // build log 97 if cfg.Output == nil { 98 return 99 } 100 stop := time.Now() 101 if cfg.JsonOutput == false { 102 buf := pool.Get().(*bytes.Buffer) 103 buf.Reset() 104 defer pool.Put(buf) 105 _, err := tmpl.ExecuteFunc(buf, func(w io.Writer, tag string) (int, error) { 106 switch tag { 107 case "latency": 108 return buf.WriteString(stop.Sub(start).String()) 109 default: 110 if b := formatTag(c, tag); b != nil { 111 return buf.Write(b) 112 } else { 113 return 0, nil 114 } 115 } 116 }) 117 if err != nil { 118 buf.WriteString(err.Error()) 119 } 120 l := cfg.Output.Log() 121 if cfg.Tag != "" { 122 l.Str("tag", cfg.Tag) 123 } 124 l.Msg(fast.GetString(buf.Bytes())) 125 } else { 126 l := cfg.Output.Log() 127 if cfg.Tag != "" { 128 l.Str("tag", cfg.Tag) 129 } 130 for _, tag := range tags { 131 switch tag { 132 case "latency": 133 l.Str(tag, stop.Sub(start).String()) 134 default: 135 if val := formatTag(c, tag); val != nil { 136 l.Bytes(tag, val) 137 } 138 } 139 } 140 l.Send() 141 } 142 } 143 } 144 145 func formatTag(c *fast.Ctx, tag string) []byte { 146 switch tag { 147 case "referer": 148 return c.C.Request.Header.Peek("Referer") 149 case "protocol": 150 return f.Bytes(c.Protocol()) 151 case "ip": 152 return c.C.RemoteIP() 153 case "host": 154 return c.C.URI().Host() 155 case "method": 156 return f.Bytes(c.Method()) 157 case "path": 158 return f.Bytes(c.Path()) 159 case "query": 160 return c.C.QueryArgs().QueryString() 161 case "url": 162 return c.C.Request.Header.RequestURI() 163 case "header": 164 return c.C.Request.Header.Header() 165 case "data": 166 return c.C.Request.Body() 167 case "ua": 168 return c.C.Request.Header.Peek("User-Agent") 169 case "status": 170 return f.Bytes(strconv.FormatInt(int64(c.C.Response.StatusCode()), 10)) 171 default: 172 switch { 173 case strings.HasPrefix(tag, "header:"): 174 return c.C.Request.Header.Peek(tag[7:]) 175 case strings.HasPrefix(tag, "query:"): 176 return f.Bytes(c.Query(tag[6:])) 177 case strings.HasPrefix(tag, "form:"): 178 return f.Bytes(c.FormValue(tag[5:])) 179 case strings.HasPrefix(tag, "cookie:"): 180 return f.Bytes(c.Cookies(tag[7:])) 181 } 182 } 183 return nil 184 }