From d79926ed122911511448e18825ea247190f8d0ff Mon Sep 17 00:00:00 2001 From: Duc Nguyen Date: Sat, 30 Sep 2023 21:26:11 +0700 Subject: [PATCH] Refactor code --- echo/log.go | 5 +- echo/mask_logger.go | 122 ++++++++++++++++++ ...ult_format_log.go => structured_logger.go} | 0 echo/v3/log.go | 5 +- echo/v3/mask_logger.go | 122 ++++++++++++++++++ ...ult_format_log.go => structured_logger.go} | 0 gin/log.go | 5 +- gin/mask_logger.go | 122 ++++++++++++++++++ ...ult_format_log.go => structured_logger.go} | 0 mask_logger.go | 122 ++++++++++++++++++ default_format_log.go => structured_logger.go | 15 ++- 11 files changed, 502 insertions(+), 16 deletions(-) create mode 100644 echo/mask_logger.go rename echo/{default_format_log.go => structured_logger.go} (100%) create mode 100644 echo/v3/mask_logger.go rename echo/v3/{default_format_log.go => structured_logger.go} (100%) create mode 100644 gin/mask_logger.go rename gin/{default_format_log.go => structured_logger.go} (100%) create mode 100644 mask_logger.go rename default_format_log.go => structured_logger.go (99%) diff --git a/echo/log.go b/echo/log.go index 6aac9e7..ff2f9fd 100644 --- a/echo/log.go +++ b/echo/log.go @@ -19,9 +19,8 @@ type EchoLogger struct { Mask func(fieldName, s string) string } -func NewEchoLogger(c LogConfig, logInfo func(ctx context.Context, msg string, fields map[string]interface{}), mask func(fieldName, s string) string) *EchoLogger { - logger := NewLogger() - return &EchoLogger{c, logInfo, logger, mask} +func NewEchoLogger(c LogConfig, logInfo func(ctx context.Context, msg string, fields map[string]interface{}), f Formatter, mask func(fieldName, s string) string) *EchoLogger { + return &EchoLogger{c, logInfo, f, mask} } func (l *EchoLogger) Logger(next echo.HandlerFunc) echo.HandlerFunc { diff --git a/echo/mask_logger.go b/echo/mask_logger.go new file mode 100644 index 0000000..369cf2c --- /dev/null +++ b/echo/mask_logger.go @@ -0,0 +1,122 @@ +package echo + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "strings" + "time" +) + +type MaskLogger struct { + send func(ctx context.Context, data []byte, attributes map[string]string) (string, error) + KeyMap map[string]string + Goroutines bool + MaskRequest func(fieldName string, v interface{}) interface{} + MaskResponse func(fieldName string, v interface{}) interface{} +} + +func NewMaskLogger(maskRequest func(fieldName string, v interface{}) interface{}, maskResponse func(fieldName string, v interface{}) interface{}) *MaskLogger { + return &MaskLogger{MaskRequest: maskRequest, MaskResponse: maskResponse} +} +func NewMaskLoggerWithSending(maskRequest func(fieldName string, v interface{}) interface{}, maskResponse func(fieldName string, v interface{}) interface{}, send func(context.Context, []byte, map[string]string) (string, error), goroutines bool, options ...map[string]string) *MaskLogger { + var keyMap map[string]string + if len(options) >= 1 { + keyMap = options[0] + } + return &MaskLogger{MaskRequest: maskRequest, MaskResponse: maskResponse, send: send, Goroutines: goroutines, KeyMap: keyMap} +} +func (l *MaskLogger) LogResponse(log func(context.Context, string, map[string]interface{}), r *http.Request, ww WrapResponseWriter, + c LogConfig, t1 time.Time, response string, fields map[string]interface{}, singleLog bool) { + fs := BuildMaskedResponseBody(ww, c, t1, response, fields, l.MaskResponse) + var msg string + if singleLog { + msg = r.Method + " " + r.RequestURI + } else { + msg = "Response " + r.Method + " " + r.RequestURI + } + log(r.Context(), msg, fs) + if l.send != nil { + if l.Goroutines { + go Send(r.Context(), l.send, msg, fields, l.KeyMap) + } else { + Send(r.Context(), l.send, msg, fields, l.KeyMap) + } + } +} +func (l *MaskLogger) LogRequest(log func(context.Context, string, map[string]interface{}), r *http.Request, c LogConfig, fields map[string]interface{}, singleLog bool) { + var fs map[string]interface{} + fs = fields + if len(c.Request) > 0 && r.Method != "GET" && r.Method != "DELETE" && !strings.Contains(r.Header.Get("Content-Type"), "multipart/form-data") { + fs = BuildMaskedRequestBody(r, c.Request, fields, l.MaskRequest) + } + if !singleLog { + msg := "Request " + r.Method + " " + r.RequestURI + log(r.Context(), msg, fs) + if l.send != nil { + if l.Goroutines { + go Send(r.Context(), l.send, msg, fields, l.KeyMap) + } else { + Send(r.Context(), l.send, msg, fields, l.KeyMap) + } + } + } +} + +func BuildMaskedResponseBody(ww WrapResponseWriter, c LogConfig, t1 time.Time, response string, fields map[string]interface{}, mask func(fieldName string, s interface{}) interface{}) map[string]interface{} { + if len(c.Response) > 0 { + fields[c.Response] = response + responseBody := response + responseMap := map[string]interface{}{} + json.Unmarshal([]byte(responseBody), &responseMap) + if len(responseMap) > 0 { + for key, v := range responseMap { + responseMap[key] = mask(key, v) + } + responseString, err := json.Marshal(responseMap) + if err != nil { + fmt.Printf("Error: %s", err.Error()) + } else { + fields[c.Response] = string(responseString) + } + } + } + if len(c.ResponseStatus) > 0 { + fields[c.ResponseStatus] = ww.Status() + } + if len(fieldConfig.Duration) > 0 { + t2 := time.Now() + duration := t2.Sub(t1) + fields[fieldConfig.Duration] = duration.Milliseconds() + } + if len(c.Size) > 0 { + fields[c.Size] = ww.BytesWritten() + } + return fields +} +func BuildMaskedRequestBody(r *http.Request, request string, fields map[string]interface{}, mask func(fieldName string, s interface{}) interface{}) map[string]interface{} { + if r.Body != nil { + buf := new(bytes.Buffer) + buf.ReadFrom(r.Body) + fields[request] = buf.String() + r.Body = ioutil.NopCloser(buf) + requestBody := fields[request].(string) + requestMap := map[string]interface{}{} + json.Unmarshal([]byte(requestBody), &requestMap) + if len(requestMap) > 0 { + for key, v := range requestMap { + requestMap[key] = mask(key, v) + } + requestString, err := json.Marshal(requestMap) + if err != nil { + fmt.Printf("Error: %s", err.Error()) + } else { + fields[request] = string(requestString) + } + } + } + return fields +} diff --git a/echo/default_format_log.go b/echo/structured_logger.go similarity index 100% rename from echo/default_format_log.go rename to echo/structured_logger.go diff --git a/echo/v3/log.go b/echo/v3/log.go index 68cf442..bfdf73e 100644 --- a/echo/v3/log.go +++ b/echo/v3/log.go @@ -19,9 +19,8 @@ type EchoLogger struct { Mask func(fieldName, s string) string } -func NewEchoLogger(c LogConfig, logInfo func(ctx context.Context, msg string, fields map[string]interface{}), mask func(fieldName, s string) string) *EchoLogger { - logger := NewLogger() - return &EchoLogger{c, logInfo, logger, mask} +func NewEchoLogger(c LogConfig, logInfo func(ctx context.Context, msg string, fields map[string]interface{}), f Formatter, mask func(fieldName, s string) string) *EchoLogger { + return &EchoLogger{c, logInfo, f, mask} } func (l *EchoLogger) Logger(next echo.HandlerFunc) echo.HandlerFunc { diff --git a/echo/v3/mask_logger.go b/echo/v3/mask_logger.go new file mode 100644 index 0000000..369cf2c --- /dev/null +++ b/echo/v3/mask_logger.go @@ -0,0 +1,122 @@ +package echo + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "strings" + "time" +) + +type MaskLogger struct { + send func(ctx context.Context, data []byte, attributes map[string]string) (string, error) + KeyMap map[string]string + Goroutines bool + MaskRequest func(fieldName string, v interface{}) interface{} + MaskResponse func(fieldName string, v interface{}) interface{} +} + +func NewMaskLogger(maskRequest func(fieldName string, v interface{}) interface{}, maskResponse func(fieldName string, v interface{}) interface{}) *MaskLogger { + return &MaskLogger{MaskRequest: maskRequest, MaskResponse: maskResponse} +} +func NewMaskLoggerWithSending(maskRequest func(fieldName string, v interface{}) interface{}, maskResponse func(fieldName string, v interface{}) interface{}, send func(context.Context, []byte, map[string]string) (string, error), goroutines bool, options ...map[string]string) *MaskLogger { + var keyMap map[string]string + if len(options) >= 1 { + keyMap = options[0] + } + return &MaskLogger{MaskRequest: maskRequest, MaskResponse: maskResponse, send: send, Goroutines: goroutines, KeyMap: keyMap} +} +func (l *MaskLogger) LogResponse(log func(context.Context, string, map[string]interface{}), r *http.Request, ww WrapResponseWriter, + c LogConfig, t1 time.Time, response string, fields map[string]interface{}, singleLog bool) { + fs := BuildMaskedResponseBody(ww, c, t1, response, fields, l.MaskResponse) + var msg string + if singleLog { + msg = r.Method + " " + r.RequestURI + } else { + msg = "Response " + r.Method + " " + r.RequestURI + } + log(r.Context(), msg, fs) + if l.send != nil { + if l.Goroutines { + go Send(r.Context(), l.send, msg, fields, l.KeyMap) + } else { + Send(r.Context(), l.send, msg, fields, l.KeyMap) + } + } +} +func (l *MaskLogger) LogRequest(log func(context.Context, string, map[string]interface{}), r *http.Request, c LogConfig, fields map[string]interface{}, singleLog bool) { + var fs map[string]interface{} + fs = fields + if len(c.Request) > 0 && r.Method != "GET" && r.Method != "DELETE" && !strings.Contains(r.Header.Get("Content-Type"), "multipart/form-data") { + fs = BuildMaskedRequestBody(r, c.Request, fields, l.MaskRequest) + } + if !singleLog { + msg := "Request " + r.Method + " " + r.RequestURI + log(r.Context(), msg, fs) + if l.send != nil { + if l.Goroutines { + go Send(r.Context(), l.send, msg, fields, l.KeyMap) + } else { + Send(r.Context(), l.send, msg, fields, l.KeyMap) + } + } + } +} + +func BuildMaskedResponseBody(ww WrapResponseWriter, c LogConfig, t1 time.Time, response string, fields map[string]interface{}, mask func(fieldName string, s interface{}) interface{}) map[string]interface{} { + if len(c.Response) > 0 { + fields[c.Response] = response + responseBody := response + responseMap := map[string]interface{}{} + json.Unmarshal([]byte(responseBody), &responseMap) + if len(responseMap) > 0 { + for key, v := range responseMap { + responseMap[key] = mask(key, v) + } + responseString, err := json.Marshal(responseMap) + if err != nil { + fmt.Printf("Error: %s", err.Error()) + } else { + fields[c.Response] = string(responseString) + } + } + } + if len(c.ResponseStatus) > 0 { + fields[c.ResponseStatus] = ww.Status() + } + if len(fieldConfig.Duration) > 0 { + t2 := time.Now() + duration := t2.Sub(t1) + fields[fieldConfig.Duration] = duration.Milliseconds() + } + if len(c.Size) > 0 { + fields[c.Size] = ww.BytesWritten() + } + return fields +} +func BuildMaskedRequestBody(r *http.Request, request string, fields map[string]interface{}, mask func(fieldName string, s interface{}) interface{}) map[string]interface{} { + if r.Body != nil { + buf := new(bytes.Buffer) + buf.ReadFrom(r.Body) + fields[request] = buf.String() + r.Body = ioutil.NopCloser(buf) + requestBody := fields[request].(string) + requestMap := map[string]interface{}{} + json.Unmarshal([]byte(requestBody), &requestMap) + if len(requestMap) > 0 { + for key, v := range requestMap { + requestMap[key] = mask(key, v) + } + requestString, err := json.Marshal(requestMap) + if err != nil { + fmt.Printf("Error: %s", err.Error()) + } else { + fields[request] = string(requestString) + } + } + } + return fields +} diff --git a/echo/v3/default_format_log.go b/echo/v3/structured_logger.go similarity index 100% rename from echo/v3/default_format_log.go rename to echo/v3/structured_logger.go diff --git a/gin/log.go b/gin/log.go index 1f9fa75..8745d3a 100644 --- a/gin/log.go +++ b/gin/log.go @@ -19,9 +19,8 @@ type GinLogger struct { Mask func(fieldName, s string) string } -func NewGinLogger(c LogConfig, logInfo func(ctx context.Context, msg string, fields map[string]interface{}), mask func(fieldName, s string) string) *GinLogger { - logger := NewLogger() - return &GinLogger{c, logInfo, logger, mask} +func NewGinLogger(c LogConfig, logInfo func(ctx context.Context, msg string, fields map[string]interface{}), f Formatter, mask func(fieldName, s string) string) *GinLogger { + return &GinLogger{c, logInfo, f, mask} } func (l *GinLogger) Logger() gin.HandlerFunc { diff --git a/gin/mask_logger.go b/gin/mask_logger.go new file mode 100644 index 0000000..fa282bf --- /dev/null +++ b/gin/mask_logger.go @@ -0,0 +1,122 @@ +package gin + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "strings" + "time" +) + +type MaskLogger struct { + send func(ctx context.Context, data []byte, attributes map[string]string) (string, error) + KeyMap map[string]string + Goroutines bool + MaskRequest func(fieldName string, v interface{}) interface{} + MaskResponse func(fieldName string, v interface{}) interface{} +} +func NewMaskLogger(maskRequest func(fieldName string, v interface{}) interface{}, maskResponse func(fieldName string, v interface{}) interface{}) *MaskLogger { + return &MaskLogger{MaskRequest: maskRequest, MaskResponse: maskResponse} +} +func NewMaskLoggerWithSending(maskRequest func(fieldName string, v interface{}) interface{}, maskResponse func(fieldName string, v interface{}) interface{}, send func(context.Context, []byte, map[string]string) (string, error), goroutines bool, options ...map[string]string) *MaskLogger { + var keyMap map[string]string + if len(options) >= 1 { + keyMap = options[0] + } + return &MaskLogger{MaskRequest: maskRequest, MaskResponse: maskResponse, send: send, Goroutines: goroutines, KeyMap: keyMap} +} + +func (l *MaskLogger) LogResponse(log func(context.Context, string, map[string]interface{}), r *http.Request, ww ResponseWriter, + c LogConfig, t1 time.Time, response string, fields map[string]interface{}, singleLog bool) { + fs := BuildMaskedResponseBody(ww, c, t1, response, fields, l.MaskResponse) + var msg string + if singleLog { + msg = r.Method + " " + r.RequestURI + } else { + msg = "Response " + r.Method + " " + r.RequestURI + } + log(r.Context(), msg, fs) + if l.send != nil { + if l.Goroutines { + go Send(r.Context(), l.send, msg, fields, l.KeyMap) + } else { + Send(r.Context(), l.send, msg, fields, l.KeyMap) + } + } +} +func (l *MaskLogger) LogRequest(log func(context.Context, string, map[string]interface{}), r *http.Request, c LogConfig, fields map[string]interface{}, singleLog bool) { + var fs map[string]interface{} + fs = fields + if len(c.Request) > 0 && r.Method != "GET" && r.Method != "DELETE" && !strings.Contains(r.Header.Get("Content-Type"), "multipart/form-data") { + fs = BuildMaskedRequestBody(r, c.Request, fields, l.MaskRequest) + } + if !singleLog { + msg := "Request " + r.Method + " " + r.RequestURI + log(r.Context(), msg, fs) + if l.send != nil { + if l.Goroutines { + go Send(r.Context(), l.send, msg, fields, l.KeyMap) + } else { + Send(r.Context(), l.send, msg, fields, l.KeyMap) + } + } + } +} + +func BuildMaskedResponseBody(ww ResponseWriter, c LogConfig, t1 time.Time, response string, fields map[string]interface{}, mask func(fieldName string, s interface{}) interface{}) map[string]interface{} { + if len(c.Response) > 0 { + fields[c.Response] = response + responseBody := response + responseMap := map[string]interface{}{} + json.Unmarshal([]byte(responseBody), &responseMap) + if len(responseMap) > 0 { + for key, v := range responseMap { + responseMap[key] = mask(key, v) + } + responseString, err := json.Marshal(responseMap) + if err != nil { + fmt.Printf("Error: %s", err.Error()) + } else { + fields[c.Response] = string(responseString) + } + } + } + if len(c.ResponseStatus) > 0 { + fields[c.ResponseStatus] = ww.Status() + } + if len(fieldConfig.Duration) > 0 { + t2 := time.Now() + duration := t2.Sub(t1) + fields[fieldConfig.Duration] = duration.Milliseconds() + } + if len(c.Size) > 0 { + fields[c.Size] = ww.Size() + } + return fields +} +func BuildMaskedRequestBody(r *http.Request, request string, fields map[string]interface{}, mask func(fieldName string, s interface{}) interface{}) map[string]interface{} { + if r.Body != nil { + buf := new(bytes.Buffer) + buf.ReadFrom(r.Body) + fields[request] = buf.String() + r.Body = ioutil.NopCloser(buf) + requestBody := fields[request].(string) + requestMap := map[string]interface{}{} + json.Unmarshal([]byte(requestBody), &requestMap) + if len(requestMap) > 0 { + for key, v := range requestMap { + requestMap[key] = mask(key, v) + } + requestString, err := json.Marshal(requestMap) + if err != nil { + fmt.Printf("Error: %s", err.Error()) + } else { + fields[request] = string(requestString) + } + } + } + return fields +} diff --git a/gin/default_format_log.go b/gin/structured_logger.go similarity index 100% rename from gin/default_format_log.go rename to gin/structured_logger.go diff --git a/mask_logger.go b/mask_logger.go new file mode 100644 index 0000000..1396b90 --- /dev/null +++ b/mask_logger.go @@ -0,0 +1,122 @@ +package middleware + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "strings" + "time" +) + +type MaskLogger struct { + send func(ctx context.Context, data []byte, attributes map[string]string) (string, error) + KeyMap map[string]string + Goroutines bool + MaskRequest func(fieldName string, v interface{}) interface{} + MaskResponse func(fieldName string, v interface{}) interface{} +} +func NewMaskLogger(maskRequest func(fieldName string, v interface{}) interface{}, maskResponse func(fieldName string, v interface{}) interface{}) *MaskLogger { + return &MaskLogger{MaskRequest: maskRequest, MaskResponse: maskResponse} +} +func NewMaskLoggerWithSending(maskRequest func(fieldName string, v interface{}) interface{}, maskResponse func(fieldName string, v interface{}) interface{}, send func(context.Context, []byte, map[string]string) (string, error), goroutines bool, options ...map[string]string) *MaskLogger { + var keyMap map[string]string + if len(options) >= 1 { + keyMap = options[0] + } + return &MaskLogger{MaskRequest: maskRequest, MaskResponse: maskResponse, send: send, Goroutines: goroutines, KeyMap: keyMap} +} + +func (l *MaskLogger) LogResponse(log func(context.Context, string, map[string]interface{}), r *http.Request, ww WrapResponseWriter, + c LogConfig, t1 time.Time, response string, fields map[string]interface{}, singleLog bool) { + fs := BuildMaskedResponseBody(ww, c, t1, response, fields, l.MaskResponse) + var msg string + if singleLog { + msg = r.Method + " " + r.RequestURI + } else { + msg = "Response " + r.Method + " " + r.RequestURI + } + log(r.Context(), msg, fs) + if l.send != nil { + if l.Goroutines { + go Send(r.Context(), l.send, msg, fields, l.KeyMap) + } else { + Send(r.Context(), l.send, msg, fields, l.KeyMap) + } + } +} +func (l *MaskLogger) LogRequest(log func(context.Context, string, map[string]interface{}), r *http.Request, c LogConfig, fields map[string]interface{}, singleLog bool) { + var fs map[string]interface{} + fs = fields + if len(c.Request) > 0 && r.Method != "GET" && r.Method != "DELETE" && !strings.Contains(r.Header.Get("Content-Type"), "multipart/form-data") { + fs = BuildMaskedRequestBody(r, c.Request, fields, l.MaskRequest) + } + if !singleLog { + msg := "Request " + r.Method + " " + r.RequestURI + log(r.Context(), msg, fs) + if l.send != nil { + if l.Goroutines { + go Send(r.Context(), l.send, msg, fields, l.KeyMap) + } else { + Send(r.Context(), l.send, msg, fields, l.KeyMap) + } + } + } +} + +func BuildMaskedResponseBody(ww WrapResponseWriter, c LogConfig, t1 time.Time, response string, fields map[string]interface{}, mask func(fieldName string, s interface{}) interface{}) map[string]interface{} { + if len(c.Response) > 0 { + fields[c.Response] = response + responseBody := response + responseMap := map[string]interface{}{} + json.Unmarshal([]byte(responseBody), &responseMap) + if len(responseMap) > 0 { + for key, v := range responseMap { + responseMap[key] = mask(key, v) + } + responseString, err := json.Marshal(responseMap) + if err != nil { + fmt.Printf("Error: %s", err.Error()) + } else { + fields[c.Response] = string(responseString) + } + } + } + if len(c.ResponseStatus) > 0 { + fields[c.ResponseStatus] = ww.Status() + } + if len(fieldConfig.Duration) > 0 { + t2 := time.Now() + duration := t2.Sub(t1) + fields[fieldConfig.Duration] = duration.Milliseconds() + } + if len(c.Size) > 0 { + fields[c.Size] = ww.BytesWritten() + } + return fields +} +func BuildMaskedRequestBody(r *http.Request, request string, fields map[string]interface{}, mask func(fieldName string, s interface{}) interface{}) map[string]interface{} { + if r.Body != nil { + buf := new(bytes.Buffer) + buf.ReadFrom(r.Body) + fields[request] = buf.String() + r.Body = ioutil.NopCloser(buf) + requestBody := fields[request].(string) + requestMap := map[string]interface{}{} + json.Unmarshal([]byte(requestBody), &requestMap) + if len(requestMap) > 0 { + for key, v := range requestMap { + requestMap[key] = mask(key, v) + } + requestString, err := json.Marshal(requestMap) + if err != nil { + fmt.Printf("Error: %s", err.Error()) + } else { + fields[request] = string(requestString) + } + } + } + return fields +} diff --git a/default_format_log.go b/structured_logger.go similarity index 99% rename from default_format_log.go rename to structured_logger.go index 4a717c8..872760b 100644 --- a/default_format_log.go +++ b/structured_logger.go @@ -32,6 +32,7 @@ func NewLoggerWithSending(send func(context.Context, []byte, map[string]string) } return &StructuredLogger{send: send, Goroutines: goroutines, KeyMap: keyMap} } + func (l *StructuredLogger) LogResponse(log func(context.Context, string, map[string]interface{}), r *http.Request, ww WrapResponseWriter, c LogConfig, t1 time.Time, response string, fields map[string]interface{}, singleLog bool) { fs := BuildResponseBody(ww, c, t1, response, fields) @@ -50,13 +51,6 @@ func (l *StructuredLogger) LogResponse(log func(context.Context, string, map[str } } } -func Send(ctx context.Context, send func(ctx context.Context, data []byte, attributes map[string]string) (string, error), msg string, fields map[string]interface{}, keyMap map[string]string) { - m2 := AddKeyFields(msg, fields, keyMap) - b, err := json.Marshal(m2) - if err == nil { - send(ctx, b, nil) - } -} func (l *StructuredLogger) LogRequest(log func(context.Context, string, map[string]interface{}), r *http.Request, c LogConfig, fields map[string]interface{}, singleLog bool) { var fs map[string]interface{} fs = fields @@ -102,6 +96,13 @@ func BuildRequestBody(r *http.Request, request string, fields map[string]interfa } return fields } +func Send(ctx context.Context, send func(ctx context.Context, data []byte, attributes map[string]string) (string, error), msg string, fields map[string]interface{}, keyMap map[string]string) { + m2 := AddKeyFields(msg, fields, keyMap) + b, err := json.Marshal(m2) + if err == nil { + send(ctx, b, nil) + } +} func AddKeyFields(message string, m map[string]interface{}, keys map[string]string) map[string]interface{} { level := "level" t := "time"