Skip to content

Commit

Permalink
Merge pull request #995 from roman-wb/zerolog_snake_case
Browse files Browse the repository at this point in the history
Option using fields in snake_case.
  • Loading branch information
ReneWerner87 authored Mar 7, 2024
2 parents 86655b7 + ed24b0f commit bb53713
Show file tree
Hide file tree
Showing 3 changed files with 124 additions and 24 deletions.
3 changes: 2 additions & 1 deletion fiberzerolog/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ 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.<br />If false: `{"method":"POST", "header-key":"header value"}`<br />If true: `{"method":"POST", "reqHeaders": {"header-key":"header value"}}` | `false` |
| WrapHeaders | bool | Wrap headers to dictionary.<br />If false: `{"method":"POST", "header-key":"header value"}`<br>If true: `{"method":"POST", "reqHeaders": {"header-key":"header value"}}` | `false` |
| FieldsSnakeCase | bool | Use snake case for fields: FieldResBody, FieldQueryParams, FieldBytesReceived, FieldBytesSent, FieldRequestId, FieldReqHeaders, FieldResHeaders.<br />If false: `{"method":"POST", "resBody":"v", "queryParams":"v"}`<br>If true: `{"method":"POST", "res_body":"v", "query_params":"v"}` | `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{}` |
Expand Down
82 changes: 59 additions & 23 deletions fiberzerolog/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,14 @@ const (
FieldError = "error"
FieldReqHeaders = "reqHeaders"
FieldResHeaders = "resHeaders"

fieldResBody_ = "res_body"
fieldQueryParams_ = "query_params"
fieldBytesReceived_ = "bytes_received"
fieldBytesSent_ = "bytes_sent"
fieldRequestID_ = "request_id"
fieldReqHeaders_ = "req_headers"
fieldResHeaders_ = "res_headers"
)

// Config defines the config for middleware.
Expand Down Expand Up @@ -87,6 +95,13 @@ type Config struct {
// Optional. Default: false
WrapHeaders bool

// Use snake case for fields: FieldResBody, FieldQueryParams, FieldBytesReceived, FieldBytesSent, FieldRequestId, FieldReqHeaders, FieldResHeaders.
// If false: {"method":"POST", "resBody":"v", "queryParams":"v"}
// If true: {"method":"POST", "res_body":"v", "query_params":"v"}
//
// Optional. Default: false
FieldsSnakeCase bool

// Custom response messages.
// Response codes >= 500 will be logged with Messages[0].
// Response codes >= 400 will be logged with Messages[1].
Expand Down Expand Up @@ -122,76 +137,97 @@ func (c *Config) logger(fc *fiber.Ctx, latency time.Duration, err error) zerolog
for _, field := range c.Fields {
switch field {
case FieldReferer:
zc = zc.Str(FieldReferer, fc.Get(fiber.HeaderReferer))
zc = zc.Str(field, fc.Get(fiber.HeaderReferer))
case FieldProtocol:
zc = zc.Str(FieldProtocol, fc.Protocol())
zc = zc.Str(field, fc.Protocol())
case FieldPID:
zc = zc.Int(FieldPID, os.Getpid())
zc = zc.Int(field, os.Getpid())
case FieldPort:
zc = zc.Str(FieldPort, fc.Port())
zc = zc.Str(field, fc.Port())
case FieldIP:
zc = zc.Str(FieldIP, fc.IP())
zc = zc.Str(field, fc.IP())
case FieldIPs:
zc = zc.Str(FieldIPs, fc.Get(fiber.HeaderXForwardedFor))
zc = zc.Str(field, fc.Get(fiber.HeaderXForwardedFor))
case FieldHost:
zc = zc.Str(FieldHost, fc.Hostname())
zc = zc.Str(field, fc.Hostname())
case FieldPath:
zc = zc.Str(FieldPath, fc.Path())
zc = zc.Str(field, fc.Path())
case FieldURL:
zc = zc.Str(FieldURL, fc.OriginalURL())
zc = zc.Str(field, fc.OriginalURL())
case FieldUserAgent:
zc = zc.Str(FieldUserAgent, fc.Get(fiber.HeaderUserAgent))
zc = zc.Str(field, fc.Get(fiber.HeaderUserAgent))
case FieldLatency:
zc = zc.Str(FieldLatency, latency.String())
zc = zc.Str(field, latency.String())
case FieldStatus:
zc = zc.Int(FieldStatus, fc.Response().StatusCode())
zc = zc.Int(field, fc.Response().StatusCode())
case FieldResBody:
if c.FieldsSnakeCase {
field = fieldResBody_
}
if c.SkipResBody == nil || !c.SkipResBody(fc) {
if c.GetResBody == nil {
zc = zc.Bytes(FieldResBody, fc.Response().Body())
zc = zc.Bytes(field, fc.Response().Body())
} else {
zc = zc.Bytes(FieldResBody, c.GetResBody(fc))
zc = zc.Bytes(field, c.GetResBody(fc))
}
}
case FieldQueryParams:
zc = zc.Stringer(FieldQueryParams, fc.Request().URI().QueryArgs())
if c.FieldsSnakeCase {
field = fieldQueryParams_
}
zc = zc.Stringer(field, fc.Request().URI().QueryArgs())
case FieldBody:
if c.SkipBody == nil || !c.SkipBody(fc) {
zc = zc.Bytes(FieldBody, fc.Body())
zc = zc.Bytes(field, fc.Body())
}
case FieldBytesReceived:
zc = zc.Int(FieldBytesReceived, len(fc.Request().Body()))
if c.FieldsSnakeCase {
field = fieldBytesReceived_
}
zc = zc.Int(field, len(fc.Request().Body()))
case FieldBytesSent:
zc = zc.Int(FieldBytesSent, len(fc.Response().Body()))
if c.FieldsSnakeCase {
field = fieldBytesSent_
}
zc = zc.Int(field, len(fc.Response().Body()))
case FieldRoute:
zc = zc.Str(FieldRoute, fc.Route().Path)
zc = zc.Str(field, fc.Route().Path)
case FieldMethod:
zc = zc.Str(FieldMethod, fc.Method())
zc = zc.Str(field, fc.Method())
case FieldRequestID:
zc = zc.Str(FieldRequestID, fc.GetRespHeader(fiber.HeaderXRequestID))
if c.FieldsSnakeCase {
field = fieldRequestID_
}
zc = zc.Str(field, fc.GetRespHeader(fiber.HeaderXRequestID))
case FieldError:
if err != nil {
zc = zc.Err(err)
}
case FieldReqHeaders:
if c.FieldsSnakeCase {
field = fieldReqHeaders_
}
if c.WrapHeaders {
dict := zerolog.Dict()
fc.Request().Header.VisitAll(func(k, v []byte) {
dict.Bytes(string(k), v)
})
zc = zc.Dict(FieldReqHeaders, dict)
zc = zc.Dict(field, dict)
} else {
fc.Request().Header.VisitAll(func(k, v []byte) {
zc = zc.Bytes(string(k), v)
})
}
case FieldResHeaders:
if c.FieldsSnakeCase {
field = 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)
zc = zc.Dict(field, dict)
} else {
fc.Response().Header.VisitAll(func(k, v []byte) {
zc = zc.Bytes(string(k), v)
Expand Down
63 changes: 63 additions & 0 deletions fiberzerolog/zerolog_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"time"

"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/requestid"
"github.com/gofiber/fiber/v2/utils"
"github.com/rs/zerolog"
)
Expand Down Expand Up @@ -471,6 +472,68 @@ func Test_Res_Headers_WrapHeaders(t *testing.T) {
utils.AssertEqual(t, expected, logs)
}

func Test_FieldsSnakeCase(t *testing.T) {
t.Parallel()

var buf bytes.Buffer
logger := zerolog.New(&buf)

app := fiber.New()
app.Use(requestid.New())
app.Use(New(Config{
Logger: &logger,
Fields: []string{
FieldResBody,
FieldQueryParams,
FieldBytesReceived,
FieldBytesSent,
FieldRequestID,
FieldResHeaders,
FieldReqHeaders,
},
FieldsSnakeCase: true,
WrapHeaders: true,
}))

app.Get("/", func(c *fiber.Ctx) error {
c.Set("Foo", "bar")
return c.SendString("hello")
})

req := httptest.NewRequest("GET", "/?param=value", nil)
req.Header.Add("X-Request-ID", "uuid")
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{}{
"bytes_received": float64(0),
"bytes_sent": float64(5),
"query_params": "param=value",
"req_headers": map[string]interface{}{
"Host": "example.com",
"Baz": "foo",
"X-Request-Id": "uuid",
},
"res_headers": map[string]interface{}{
"Content-Type": "text/plain; charset=utf-8",
"Foo": "bar",
"X-Request-Id": "uuid",
},
"request_id": "uuid",
"res_body": "hello",
"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()

Expand Down

0 comments on commit bb53713

Please sign in to comment.