Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support configuring log levels and reqHeaders support #359

Merged
merged 2 commits into from
Dec 11, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions fiberzap/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ fiberzap.New(config ...Config) fiber.Handler
| Logger | `*zap.Logger` | Add custom zap logger. | `zap.NewDevelopment()` |
| Fields | `[]string` | Add fields what you want see. | `[]string{"latency", "status", "method", "url"}` |
| Messages | `[]string` | Custom response messages. | `[]string{"Server error", "Client error", "Success"}` |
| Levels | `[]zapcore.Level` | Custom response levels. | `[]zapcore.Level{zapcore.ErrorLevel, zapcore.WarnLevel, zapcore.InfoLevel}` |
| SkipURIs | `[]string` | Skip logging these URI. | `[]string{}` |

### Example
Expand Down
21 changes: 21 additions & 0 deletions fiberzap/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package fiberzap
import (
"github.com/gofiber/fiber/v2"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)

// Config defines the config for middleware.
Expand Down Expand Up @@ -38,9 +39,24 @@ type Config struct {
Fields []string

// Custom response messages.
// Response codes >= 500 will be logged with Messages[0].
// Response codes >= 400 will be logged with Messages[1].
// Other response codes will be logged with Messages[2].
// You can specify less, than 3 messages, but you must specify at least 1.
// Specifying more than 3 messages is useless.
//
// Optional. Default: {"Server error", "Client error", "Success"}
Messages []string

// Custom response levels.
// Response codes >= 500 will be logged with Levels[0].
// Response codes >= 400 will be logged with Levels[1].
// Other response codes will be logged with Levels[2].
// You can specify less, than 3 levels, but you must specify at least 1.
// Specifying more than 3 levels is useless.
//
// Optional. Default: {zapcore.ErrorLevel, zapcore.WarnLevel, zapcore.InfoLevel}
Levels []zapcore.Level
DoubleDi marked this conversation as resolved.
Show resolved Hide resolved
}

// Use zap.NewProduction() as default logging instance.
Expand All @@ -52,6 +68,7 @@ var ConfigDefault = Config{
Logger: logger,
Fields: []string{"latency", "status", "method", "url"},
Messages: []string{"Server error", "Client error", "Success"},
Levels: []zapcore.Level{zapcore.ErrorLevel, zapcore.WarnLevel, zapcore.InfoLevel},
}

// Helper function to set default values
Expand Down Expand Up @@ -81,5 +98,9 @@ func configDefault(config ...Config) Config {
cfg.Messages = ConfigDefault.Messages
}

if cfg.Levels == nil {
cfg.Levels = ConfigDefault.Levels
}

return cfg
}
45 changes: 34 additions & 11 deletions fiberzap/zap.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,36 @@ func New(config ...Config) fiber.Handler {
stop = time.Now()
}

// Check if the logger has the appropriate level
var (
s = c.Response().StatusCode()
index int
)
switch {
case s >= 500:
// error index is zero
case s >= 400:
index = 1
default:
index = 2
}
levelIndex := index
if levelIndex >= len(cfg.Levels) {
levelIndex = len(cfg.Levels) - 1
}
messageIndex := index
if messageIndex >= len(cfg.Messages) {
messageIndex = len(cfg.Messages) - 1
}

ce := cfg.Logger.Check(cfg.Levels[levelIndex], cfg.Messages[messageIndex])
if ce == nil {
efectn marked this conversation as resolved.
Show resolved Hide resolved
return nil
}

// Add fields
fields := make([]zap.Field, 0, len(cfg.Fields))
fields := make([]zap.Field, 0, len(cfg.Fields)+1)
fields = append(fields, zap.Error(err))

for _, field := range cfg.Fields {
switch field {
Expand Down Expand Up @@ -134,19 +162,14 @@ func New(config ...Config) fiber.Handler {
if chainErr != nil {
fields = append(fields, zap.String("error", chainErr.Error()))
}
case "reqHeaders":
c.Request().Header.VisitAll(func(k, v []byte) {
fields = append(fields, zap.ByteString(string(k), v))
})
}
}

// Return fields by status code
s := c.Response().StatusCode()
switch {
case s >= 500:
cfg.Logger.With(zap.Error(err)).Error(cfg.Messages[0], fields...)
case s >= 400:
cfg.Logger.With(zap.Error(err)).Warn(cfg.Messages[1], fields...)
default:
cfg.Logger.Info(cfg.Messages[2], fields...)
}
ce.Write(fields...)

return nil
}
Expand Down
118 changes: 117 additions & 1 deletion fiberzap/zap_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"github.com/gofiber/fiber/v2/utils"
"github.com/valyala/fasthttp"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"go.uber.org/zap/zaptest/observer"
)

Expand Down Expand Up @@ -280,7 +281,7 @@ func Test_Request_Id(t *testing.T) {
resp, err := app.Test(httptest.NewRequest("GET", "/", nil))
utils.AssertEqual(t, nil, err)
utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode)
utils.AssertEqual(t, "bf985e8e-6a32-42ec-8e50-05a21db8f0e4", logs.All()[0].Context[0].String)
utils.AssertEqual(t, "bf985e8e-6a32-42ec-8e50-05a21db8f0e4", logs.All()[0].Context[1].String)
}

// go test -run Test_Skip_URIs
Expand All @@ -302,3 +303,118 @@ func Test_Skip_URIs(t *testing.T) {
utils.AssertEqual(t, fiber.StatusInternalServerError, resp.StatusCode)
utils.AssertEqual(t, 0, len(logs.All()))
}

// go test -run Test_Req_Headers
func Test_Req_Headers(t *testing.T) {
app := fiber.New()
logger, logs := setupLogsCapture()

app.Use(New(Config{
Logger: logger,
Fields: []string{"reqHeaders"},
}))

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

expected := map[string]interface{}{
"Host": "example.com",
"Baz": "foo",
"Foo": "bar",
}

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)
utils.AssertEqual(t, expected, logs.All()[0].ContextMap())
}

// go test -run Test_LoggerLevelsAndMessages
func Test_LoggerLevelsAndMessages(t *testing.T) {
app := fiber.New()
logger, logs := setupLogsCapture()

levels := []zapcore.Level{zapcore.ErrorLevel, zapcore.WarnLevel, zapcore.InfoLevel}
messages := []string{"server error", "client error", "success"}
app.Use(New(Config{
Logger: logger,
Messages: messages,
Levels: levels,
}))

app.Get("/200", func(c *fiber.Ctx) error {
c.Status(fiber.StatusOK)
return nil
})
app.Get("/400", func(c *fiber.Ctx) error {
c.Status(fiber.StatusBadRequest)
return nil
})
app.Get("/500", func(c *fiber.Ctx) error {
c.Status(fiber.StatusInternalServerError)
return nil
})

resp, err := app.Test(httptest.NewRequest("GET", "/500", nil))
utils.AssertEqual(t, nil, err)
utils.AssertEqual(t, fiber.StatusInternalServerError, resp.StatusCode)
utils.AssertEqual(t, levels[0], logs.All()[0].Level)
utils.AssertEqual(t, messages[0], logs.All()[0].Message)
resp, err = app.Test(httptest.NewRequest("GET", "/400", nil))
utils.AssertEqual(t, nil, err)
utils.AssertEqual(t, fiber.StatusBadRequest, resp.StatusCode)
utils.AssertEqual(t, levels[1], logs.All()[1].Level)
utils.AssertEqual(t, messages[1], logs.All()[1].Message)
resp, err = app.Test(httptest.NewRequest("GET", "/200", nil))
utils.AssertEqual(t, nil, err)
utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode)
utils.AssertEqual(t, levels[2], logs.All()[2].Level)
utils.AssertEqual(t, messages[2], logs.All()[2].Message)
}

// go test -run Test_LoggerLevelsAndMessagesSingle
func Test_LoggerLevelsAndMessagesSingle(t *testing.T) {
app := fiber.New()
logger, logs := setupLogsCapture()

levels := []zapcore.Level{zapcore.ErrorLevel}
messages := []string{"server error"}
app.Use(New(Config{
Logger: logger,
Messages: messages,
Levels: levels,
}))

app.Get("/200", func(c *fiber.Ctx) error {
c.Status(fiber.StatusOK)
return nil
})
app.Get("/400", func(c *fiber.Ctx) error {
c.Status(fiber.StatusBadRequest)
return nil
})
app.Get("/500", func(c *fiber.Ctx) error {
c.Status(fiber.StatusInternalServerError)
return nil
})

resp, err := app.Test(httptest.NewRequest("GET", "/500", nil))
utils.AssertEqual(t, nil, err)
utils.AssertEqual(t, fiber.StatusInternalServerError, resp.StatusCode)
utils.AssertEqual(t, levels[0], logs.All()[0].Level)
utils.AssertEqual(t, messages[0], logs.All()[0].Message)
resp, err = app.Test(httptest.NewRequest("GET", "/400", nil))
utils.AssertEqual(t, nil, err)
utils.AssertEqual(t, fiber.StatusBadRequest, resp.StatusCode)
utils.AssertEqual(t, levels[0], logs.All()[1].Level)
utils.AssertEqual(t, messages[0], logs.All()[1].Message)
resp, err = app.Test(httptest.NewRequest("GET", "/200", nil))
utils.AssertEqual(t, nil, err)
utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode)
utils.AssertEqual(t, levels[0], logs.All()[2].Level)
utils.AssertEqual(t, messages[0], logs.All()[2].Message)
}