From 4e8d5ce73a9c138adfba2cc2713babc43a65e59d Mon Sep 17 00:00:00 2001 From: Roman Dobynda Date: Wed, 21 Feb 2024 18:41:12 +0300 Subject: [PATCH] Improve Dict log for reqHeaders & resHeaders --- fiberzerolog/README.md | 4 +- fiberzerolog/config.go | 34 +++++++++- fiberzerolog/zerolog_test.go | 120 +++++++++++++++++++++++++++++++++++ 3 files changed, 153 insertions(+), 5 deletions(-) diff --git a/fiberzerolog/README.md b/fiberzerolog/README.md index 732dbd37..4524aeb1 100644 --- a/fiberzerolog/README.md +++ b/fiberzerolog/README.md @@ -38,11 +38,11 @@ fiberzerolog.New(config ...fiberzerolog.Config) fiber.Handler | Logger | `*zerolog.Logger` | Add custom zerolog logger. | `zerolog.New(os.Stderr).With().Timestamp().Logger()` | | GetLogger | `func(*fiber.Ctx) zerolog.Logger` | Get custom zerolog logger, if it's defined the returned logger will replace the `Logger` value. | `nil` | | Fields | `[]string` | Add fields what you want see. | `[]string{"latency", "status", "method", "url", "error"}` | +| WrapHeaders | bool | Wrap headers to dictionary.
If false: `{"method":"POST", "header-key":"header value"}`
If true: `{"method":"POST", "reqHeaders": {"header-key":"header value"}}` | `false` | | Messages | `[]string` | Custom response messages. | `[]string{"Server error", "Client error", "Success"}` | | Levels | `[]zerolog.Level` | Custom response levels. | `[]zerolog.Level{zerolog.ErrorLevel, zerolog.WarnLevel, zerolog.InfoLevel}` | | SkipURIs | `[]string` | Skip logging these URI. | `[]string{}` | -| GetResBody | func(c *fiber.Ctx) []byte | Define a function to get response body when return non-nil.
eg: When use compress middleware, resBody is unreadable. you can set GetResBody func to get readable resBody. | `nil` | - +| GetResBody | func(c *fiber.Ctx) []byte | Define a function to get response body when return non-nil.
eg: When use compress middleware, resBody is unreadable. you can set GetResBody func to get readable resBody. | `nil` | ## Example ```go diff --git a/fiberzerolog/config.go b/fiberzerolog/config.go index 9fa1c199..55fd5d0c 100644 --- a/fiberzerolog/config.go +++ b/fiberzerolog/config.go @@ -31,6 +31,7 @@ const ( FieldRequestID = "requestId" FieldError = "error" FieldReqHeaders = "reqHeaders" + FieldResHeaders = "resHeaders" ) // Config defines the config for middleware. @@ -79,6 +80,13 @@ type Config struct { // Optional. Default: {"ip", "latency", "status", "method", "url", "error"} Fields []string + // Wrap headers to dictionary. + // If false: {"method":"POST", "header-key":"header value"} + // If true: {"method":"POST", "reqHeaders": {"header-key":"header value"}} + // + // Optional. Default: false + WrapHeaders bool + // Custom response messages. // Response codes >= 500 will be logged with Messages[0]. // Response codes >= 400 will be logged with Messages[1]. @@ -166,9 +174,29 @@ func (c *Config) logger(fc *fiber.Ctx, latency time.Duration, err error) zerolog zc = zc.Err(err) } case FieldReqHeaders: - fc.Request().Header.VisitAll(func(k, v []byte) { - zc = zc.Bytes(string(k), v) - }) + if c.WrapHeaders { + dict := zerolog.Dict() + fc.Request().Header.VisitAll(func(k, v []byte) { + dict.Bytes(string(k), v) + }) + zc = zc.Dict(FieldReqHeaders, dict) + } else { + fc.Request().Header.VisitAll(func(k, v []byte) { + zc = zc.Bytes(string(k), v) + }) + } + case FieldResHeaders: + if c.WrapHeaders { + dict := zerolog.Dict() + fc.Response().Header.VisitAll(func(k, v []byte) { + dict.Bytes(string(k), v) + }) + zc = zc.Dict(FieldResHeaders, dict) + } else { + fc.Response().Header.VisitAll(func(k, v []byte) { + zc = zc.Bytes(string(k), v) + }) + } } } diff --git a/fiberzerolog/zerolog_test.go b/fiberzerolog/zerolog_test.go index 64348dc9..bb761c48 100644 --- a/fiberzerolog/zerolog_test.go +++ b/fiberzerolog/zerolog_test.go @@ -351,6 +351,126 @@ func Test_Req_Headers(t *testing.T) { utils.AssertEqual(t, expected, logs) } +func Test_Req_Headers_WrapHeaders(t *testing.T) { + t.Parallel() + + var buf bytes.Buffer + logger := zerolog.New(&buf) + + app := fiber.New() + app.Use(New(Config{ + Logger: &logger, + Fields: []string{FieldReqHeaders}, + WrapHeaders: true, + })) + + app.Get("/", func(c *fiber.Ctx) error { + return c.SendString("hello") + }) + + req := httptest.NewRequest("GET", "/", nil) + req.Header.Add("foo", "bar") + req.Header.Add("baz", "foo") + + resp, err := app.Test(req) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) + + expected := map[string]interface{}{ + "reqHeaders": map[string]interface{}{ + "Host": "example.com", + "Baz": "foo", + "Foo": "bar", + }, + "level": "info", + "message": "Success", + } + + var logs map[string]any + _ = json.Unmarshal(buf.Bytes(), &logs) + + utils.AssertEqual(t, expected, logs) +} + +func Test_Res_Headers(t *testing.T) { + t.Parallel() + + var buf bytes.Buffer + logger := zerolog.New(&buf) + + app := fiber.New() + app.Use(New(Config{ + Logger: &logger, + Fields: []string{FieldResHeaders}, + })) + + app.Get("/", func(c *fiber.Ctx) error { + c.Set("foo", "bar") + c.Set("baz", "foo") + return c.SendString("hello") + }) + + req := httptest.NewRequest("GET", "/", nil) + + resp, err := app.Test(req) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) + + expected := map[string]interface{}{ + "Content-Type": "text/plain; charset=utf-8", + "Baz": "foo", + "Foo": "bar", + "level": "info", + "message": "Success", + } + + var logs map[string]any + _ = json.Unmarshal(buf.Bytes(), &logs) + + utils.AssertEqual(t, expected, logs) +} + +func Test_Res_Headers_WrapHeaders(t *testing.T) { + t.Parallel() + + var buf bytes.Buffer + logger := zerolog.New(&buf) + + app := fiber.New() + app.Use(New(Config{ + Logger: &logger, + Fields: []string{FieldResHeaders}, + WrapHeaders: true, + })) + + app.Get("/", func(c *fiber.Ctx) error { + c.Set("foo", "bar") + c.Set("baz", "foo") + return c.SendString("hello") + }) + + req := httptest.NewRequest("GET", "/", nil) + + resp, err := app.Test(req) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) + + expected := map[string]interface{}{ + "resHeaders": map[string]interface{}{ + "Content-Type": "text/plain; charset=utf-8", + "Baz": "foo", + "Foo": "bar", + }, + "level": "info", + "message": "Success", + } + + var logs map[string]any + _ = json.Unmarshal(buf.Bytes(), &logs) + + utils.AssertEqual(t, expected, logs) +} + func Test_LoggerLevelsAndMessages(t *testing.T) { t.Parallel()