You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
85 lines
2.1 KiB
85 lines
2.1 KiB
package middleware |
|
|
|
import ( |
|
"io" |
|
"os" |
|
"strings" |
|
"time" |
|
|
|
"github.com/go-logfmt/logfmt" |
|
http "github.com/valyala/fasthttp" |
|
) |
|
|
|
type LogFmtConfig struct { |
|
// Skipper defines a function to skip middleware. |
|
Skipper Skipper |
|
|
|
// TODO(toby3d): allow select some tags |
|
|
|
// Output is a writer where logs in JSON format are written. |
|
// Optional. Default value os.Stdout. |
|
Output io.Writer |
|
} |
|
|
|
var DefaultLogFmtConfig = LogFmtConfig{ |
|
Skipper: DefaultSkipper, |
|
Output: os.Stdout, |
|
} |
|
|
|
func LogFmt() Interceptor { |
|
c := DefaultLogFmtConfig |
|
|
|
return LogFmtWithConfig(c) |
|
} |
|
|
|
func LogFmtWithConfig(config LogFmtConfig) Interceptor { |
|
if config.Skipper == nil { |
|
config.Skipper = DefaultLogFmtConfig.Skipper |
|
} |
|
|
|
if config.Output == nil { |
|
config.Output = DefaultLogFmtConfig.Output |
|
} |
|
|
|
encoder := logfmt.NewEncoder(config.Output) |
|
|
|
return func(ctx *http.RequestCtx, next http.RequestHandler) { |
|
next(ctx) |
|
|
|
encoder.EncodeKeyvals( |
|
"bytes_in", len(ctx.Request.Body()), |
|
"bytes_out", len(ctx.Response.Body()), |
|
"error", ctx.Err(), |
|
"host", ctx.Host(), |
|
"id", ctx.ID(), |
|
"latency", ctx.Time().Sub(ctx.ConnTime()).Nanoseconds(), |
|
"latency_human", ctx.Time().Sub(ctx.ConnTime()).String(), |
|
"method", ctx.Method(), |
|
"path", ctx.Path(), |
|
"protocol", ctx.Request.Header.Protocol(), |
|
"referer", ctx.Referer(), |
|
"remote_ip", ctx.RemoteIP(), |
|
"status", ctx.Response.StatusCode(), |
|
"time_rfc3339", ctx.Time().Format(time.RFC3339), |
|
"time_rfc3339_nano", ctx.Time().Format(time.RFC3339Nano), |
|
"time_unix", ctx.Time().Unix(), |
|
"time_unix_nano", ctx.Time().UnixNano(), |
|
"uri", ctx.URI(), |
|
"user_agent", ctx.UserAgent(), |
|
) |
|
ctx.Request.Header.VisitAllInOrder(func(key, value []byte) { |
|
encoder.EncodeKeyval(strings.ReplaceAll(strings.ToLower("header_"+string(key)), "-", "_"), value) |
|
}) |
|
ctx.QueryArgs().VisitAll(func(key, value []byte) { |
|
encoder.EncodeKeyval(strings.ReplaceAll(strings.ToLower("query_"+string(key)), "-", "_"), value) |
|
}) |
|
|
|
if form, err := ctx.MultipartForm(); err == nil { |
|
for k, v := range form.Value { |
|
encoder.EncodeKeyval(strings.ReplaceAll(strings.ToLower("form_"+k), "-", "_"), v) |
|
} |
|
} |
|
|
|
encoder.EndRecord() |
|
} |
|
}
|
|
|