Repository: gofiber/fiber
Pull request #3333: 🔥 feat: Add Skip function to logger middleware
Author: login: JIeJaitt
Labels: name: ✏️ Feature
name: v3
Created: 2025-02-27T08:38:07Z | Merged: 2025-03-10T08:06:11Z

# Description

The `Filter` is a function that is called before the log string for a request is written to Output. If it returns true, the log will be written; otherwise, it will be skipped.

Fixes #3300


Linked issue #3300: Feature Request: Add a Pre-Write Callback to Logger Middleware
Currently, the `logger.New` middleware in Fiber writes logs to the specified `Output` file automatically.  While the `Done` callback allows for post-processing of the log string, there's no way to control *whether* a log entry is written to the file at all *before* it's written. This makes it difficult to implement fine-grained logging logic, such as selectively logging certain responses based on their content or other criteria.

**Problem:**

I have a use case where I want to log only specific errors (e.g., 4xx/5xx status codes) to an error log file, and potentially other requests (e.g., 2xx) to a separate debug or access log file.  The current `Done` callback is called *after* the log has already been written to the `Output` file.  This results in either:

1. **Double Logging:** If I use `Done` to filter logs and write to my error log file, the middleware also writes all logs to the `Output` file, leading to duplication.
2. **Inability to Selectively Log:** If I don't use `Done`, I can't filter which log entries are written to which file.

**Code from my use case**

```go
app.Use(logger.New())
app.Use(logger.New(logger.Config{
	TimeFormat: "2006-01-02 15:04:05",
	TimeZone:   "Local",
	Format:     "\n" + `{"timestamp":"${time}", "status":${status}, "latency":"${latency}", "method":"${method}", "path":"${path}", "error":"${error}", "response":[${resBody}]}`,
	Output:     errorLogFile,
	Done: func(c *fiber.Ctx, logString []byte) {
		if c.Response().StatusCode() >= 400 {
			fmt.Println("printing to error.log")
			errorLogFile.Write(logString)
			defer errorLogFile.Close()
		}
	},
}))
```
**Proposed Solution/Feature request:**

I propose adding a new callback function to the `logger.Config` called something like `BeforeWrite` or `ShouldLog`. This callback would be executed *before* the middleware writes the log entry to the `Output` file.  The callback would receive the `fiber.Ctx` and the log string as arguments and would return a boolean value: `true` if the log should be written, and `false` if it should be skipped.

**Example Usage:**

```go
app.Use(logger.New(logger.Config{
    TimeFormat: "2006-01-02 15:04:05",
    TimeZone:   "Local",
    Format:     `{"timestamp":"${time}", "status":${status}, ...}`, //Log format
    Output:     logFile, // General log file (optional)
    BeforeWrite: func(c *fiber.Ctx, logString []byte) bool {
        if c.Response().StatusCode() >= 400 {
            // Log errors to the error log file
            errorLogFile.Write(logString) // Example, we can write to different files here.
            return true // Write to the main log file as well (optional). We can return false if we only want to write to the error log.
        } else if c.Response().StatusCode() == 200 {
            // Log 200 OK requests to a separate access log
            accessLogFile.Write(logString)
            return true // Write to the main log file as well (optional). We can return false if we only want to write to the access log.
        }
        return false // Don't write other requests to the main log file.
    },
    Done: func(c *fiber.Ctx, logString []byte) {
        // Any post-processing 
    },
}))
Linked issue comments: user: login: welcome[bot]
created_at: 2025-02-06T14:25:21Z
body: Thanks for opening your first issue here! 🎉 Be sure to follow the issue template! If you need help or want to chat with us, join us on Discord https://gofiber.io/discord

author_association: NONE
user: login: gaby
created_at: 2025-02-06T16:05:08Z
body: I agree on adding this, got to check other logging framework to see how they name this kind of feature.
author_association: MEMBER
user: login: liaohongxing
created_at: 2025-02-07T06:26:33Z
body: I agree with adding this, but BeforeWrite should be renamed, according to other middlewares in gofiber, similar names are "Next" or "Filter"
author_association: CONTRIBUTOR
user: login: gaby
created_at: 2025-02-07T06:43:14Z
body: Agree with @liaohongxing this should be named `Filter`.
author_association: MEMBER
user: login: KernelDeimos
created_at: 2025-02-12T16:24:17Z
body: If I can throw my own two cents in here, this feels like a job for the decorator pattern
author_association: NONE
user: login: gaby
created_at: 2025-02-28T15:05:06Z
body: @bantawa04 Does #3333 cover your use-case? 

I don't think we need the "logstring" param, since the middleware will write to output after calling "filter()".
author_association: MEMBER
user: login: gaby
created_at: 2025-03-10T12:09:42Z
body: @bantawa04 This feature will be available in Fiber v3 as `Skip()`.

Example Usage:

```go
app := fiber.New()

app.Use(logger.New(logger.Config{
    Skip: func(c fiber.Ctx) bool {
        // Skip any code below 400
        return c.Response().StatusCode() < 400
    },
    Stream: ErrorLogStream
}))

app.Use(logger.New(logger.Config{
    Skip: func(c fiber.Ctx) bool {
        // Skip any code other than HTTP 200
        return c.Response().StatusCode() != fiber.StatusOK
    },
    Stream: AccessLogStream
}))
```
author_association: MEMBER

Diff:
diff --git a/docs/middleware/logger.md b/docs/middleware/logger.md
index 07ff07c4def..af16f384b9e 100644
--- a/docs/middleware/logger.md
+++ b/docs/middleware/logger.md
@@ -55,13 +55,13 @@ app.Use(logger.New(logger.Config{
 }))
 
 // Custom File Writer
-file, err := os.OpenFile("./123.log", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
+accessLog, err := os.OpenFile("./access.log", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
 if err != nil {
-    log.Fatalf("error opening file: %v", err)
+    log.Fatalf("error opening access.log file: %v", err)
 }
-defer file.Close()
+defer accessLog.Close()
 app.Use(logger.New(logger.Config{
-    Output: file,
+    Stream: accessLog,
 }))
 
 // Add Custom Tags
@@ -115,7 +115,7 @@ func main() {
 
     // Use the logger middleware with zerolog logger
     app.Use(logger.New(logger.Config{
-        Output: logger.LoggerToWriter(zap, log.LevelDebug),
+        Stream: logger.LoggerToWriter(zap, log.LevelDebug),
     }))
 
     // Define a route
@@ -129,7 +129,7 @@ func main() {
 ```
 
 :::tip
-Writing to os.File is goroutine-safe, but if you are using a custom Output that is not goroutine-safe, make sure to implement locking to properly serialize writes.
+Writing to os.File is goroutine-safe, but if you are using a custom Stream that is not goroutine-safe, make sure to implement locking to properly serialize writes.
 :::
 
 ## Config
@@ -138,31 +138,30 @@ Writing to os.File is goroutine-safe, but if you are using a custom Output that
 
 | Property         | Type                       | Description                                                                                                                      | Default                                                               |
 |:-----------------|:---------------------------|:---------------------------------------------------------------------------------------------------------------------------------|:----------------------------------------------------------------------|
-| Next             | `func(fiber.Ctx) bool`    | Next defines a function to skip this middleware when returned true.                                                              | `nil`                                                                 |
-| Done             | `func(fiber.Ctx, []byte)` | Done is a function that is called after the log string for a request is written to Output, and pass the log string as parameter. | `nil`                                                                 |
+| Next             | `func(fiber.Ctx) bool`     | Next defines a function to skip this middleware when returned true.                                                               | `nil`                                                                 |
+| Skip             | `func(fiber.Ctx) bool`     | Skip is a function to determine if logging is skipped or written to Stream.                                                       | `nil`                                                                 |
+| Done             | `func(fiber.Ctx, []byte)`  | Done is a function that is called after the log string for a request is written to Stream, and pass the log string as parameter.  | `nil`                                                                 |
 | CustomTags       | `map[string]LogFunc`       | tagFunctions defines the custom tag action.                                                                                      | `map[string]LogFunc`                                                  |
 | Format           | `string`                   | Format defines the logging tags.                                                                                                 | `[${time}] ${ip} ${status} - ${latency} ${method} ${path} ${error}\n` |
 | TimeFormat       | `string`                   | TimeFormat defines the time format for log timestamps.                                                                           | `15:04:05`                                                            |
 | TimeZone         | `string`                   | TimeZone can be specified, such as "UTC" and "America/New_York" and "Asia/Chongqing", etc                                        | `"Local"`                                                             |
 | TimeInterval     | `time.Duration`            | TimeInterval is the delay before the timestamp is updated.                                                                       | `500 * time.Millisecond`                                              |
-| Output           | `io.Writer`                | Output is a writer where logs are written.                                                                                       | `os.Stdout`                                                           |
+| Stream           | `io.Writer`                | Stream is a writer where logs are written.                                                                                       | `os.Stdout`                                                           |
 | LoggerFunc | `func(c fiber.Ctx, data *Data, cfg Config) error` | Custom logger function for integration with logging libraries (Zerolog, Zap, Logrus, etc). Defaults to Fiber's default logger if not defined. | `see default_logger.go defaultLoggerInstance` |
 | DisableColors    | `bool`                     | DisableColors defines if the logs output should be colorized.                                                                    | `false`                                                               |
-| enableColors     | `bool`                     | Internal field for enabling colors in the log output. (This is not a user-configurable field)                                    | -                                                                     |
-| enableLatency    | `bool`                     | Internal field for enabling latency measurement in logs. (This is not a user-configurable field)                                 | -                                                                     |
-| timeZoneLocation | `*time.Location`           | Internal field for the time zone location. (This is not a user-configurable field)                                               | -                                                                     |
 
 ## Default Config
 
 ```go
 var ConfigDefault = Config{
     Next:          nil,
+    Skip           nil,
     Done:          nil,
     Format:        "[${time}] ${ip} ${status} - ${latency} ${method} ${path} ${error}\n",
     TimeFormat:    "15:04:05",
     TimeZone:      "Local",
     TimeInterval:  500 * time.Millisecond,
-    Output:        os.Stdout,
+    Stream:        os.Stdout,
     DisableColors: false,
     LoggerFunc:    defaultLoggerInstance,
 }
diff --git a/docs/whats_new.md b/docs/whats_new.md
index 5c3dd6ac9ae..8528dc41436 100644
--- a/docs/whats_new.md
+++ b/docs/whats_new.md
@@ -912,6 +912,31 @@ func main() {
 
 </details>
 
+The `Skip` is a function to determine if logging is skipped or written to `Stream`.
+
+<details>
+<summary>Example Usage</summary>
+
+```go
+app.Use(logger.New(logger.Config{
+    Skip: func(c fiber.Ctx) bool {
+        // Skip logging HTTP 200 requests
+        return c.Response().StatusCode() == fiber.StatusOK
+    },
+}))
+```
+
+```go
+app.Use(logger.New(logger.Config{
+    Skip: func(c fiber.Ctx) bool {
+        // Only log errors, similar to an error.log
+        return c.Response().StatusCode() < 400
+    },
+}))
+```
+
+</details>
+
 ### Filesystem
 
 We've decided to remove filesystem middleware to clear up the confusion between static and filesystem middleware.
diff --git a/middleware/logger/config.go b/middleware/logger/config.go
index 4826151e46c..2df814eb062 100644
--- a/middleware/logger/config.go
+++ b/middleware/logger/config.go
@@ -10,16 +10,21 @@ import (
 
 // Config defines the config for middleware.
 type Config struct {
-	// Output is a writer where logs are written
+	// Stream is a writer where logs are written
 	//
 	// Default: os.Stdout
-	Output io.Writer
+	Stream io.Writer
 
 	// Next defines a function to skip this middleware when returned true.
 	//
 	// Optional. Default: nil
 	Next func(c fiber.Ctx) bool
 
+	// Skip is a function to determine if logging is skipped or written to Stream.
+	//
+	// Optional. Default: nil
+	Skip func(c fiber.Ctx) bool
+
 	// Done is a function that is called after the log string for a request is written to Output,
 	// and pass the log string as parameter.
 	//
@@ -98,12 +103,13 @@ type LogFunc func(output Buffer, c fiber.Ctx, data *Data, extraParam string) (in
 // ConfigDefault is the default config
 var ConfigDefault = Config{
 	Next:              nil,
+	Skip:              nil,
 	Done:              nil,
 	Format:            defaultFormat,
 	TimeFormat:        "15:04:05",
 	TimeZone:          "Local",
 	TimeInterval:      500 * time.Millisecond,
-	Output:            os.Stdout,
+	Stream:            os.Stdout,
 	BeforeHandlerFunc: beforeHandlerFunc,
 	LoggerFunc:        defaultLoggerInstance,
 	enableColors:      true,
@@ -126,6 +132,9 @@ func configDefault(config ...Config) Config {
 	if cfg.Next == nil {
 		cfg.Next = ConfigDefault.Next
 	}
+	if cfg.Skip == nil {
+		cfg.Skip = ConfigDefault.Skip
+	}
 	if cfg.Done == nil {
 		cfg.Done = ConfigDefault.Done
 	}
@@ -141,8 +150,8 @@ func configDefault(config ...Config) Config {
 	if int(cfg.TimeInterval) <= 0 {
 		cfg.TimeInterval = ConfigDefault.TimeInterval
 	}
-	if cfg.Output == nil {
-		cfg.Output = ConfigDefault.Output
+	if cfg.Stream == nil {
+		cfg.Stream = ConfigDefault.Stream
 	}
 
 	if cfg.BeforeHandlerFunc == nil {
@@ -154,7 +163,7 @@ func configDefault(config ...Config) Config {
 	}
 
 	// Enable colors if no custom format or output is given
-	if !cfg.DisableColors && cfg.Output == ConfigDefault.Output {
+	if !cfg.DisableColors && cfg.Stream == ConfigDefault.Stream {
 		cfg.enableColors = true
 	}
 
diff --git a/middleware/logger/default_logger.go b/middleware/logger/default_logger.go
index 369b2c85801..e4de79bf943 100644
--- a/middleware/logger/default_logger.go
+++ b/middleware/logger/default_logger.go
@@ -15,6 +15,12 @@ import (
 
 // default logger for fiber
 func defaultLoggerInstance(c fiber.Ctx, data *Data, cfg Config) error {
+	// Check if Skip is defined and call it.
+	// Now, if Skip(c) == true, we SKIP logging:
+	if cfg.Skip != nil && cfg.Skip(c) {
+		return nil // Skip logging if Skip returns true
+	}
+
 	// Alias colors
 	colors := c.App().Config().ColorScheme
 
@@ -91,7 +97,7 @@ func defaultLoggerInstance(c fiber.Ctx, data *Data, cfg Config) error {
 		}
 
 		// Write buffer to output
-		writeLog(cfg.Output, buf.Bytes())
+		writeLog(cfg.Stream, buf.Bytes())
 
 		if cfg.Done != nil {
 			cfg.Done(c, buf.Bytes())
@@ -125,7 +131,7 @@ func defaultLoggerInstance(c fiber.Ctx, data *Data, cfg Config) error {
 		buf.WriteString(err.Error())
 	}
 
-	writeLog(cfg.Output, buf.Bytes())
+	writeLog(cfg.Stream, buf.Bytes())
 
 	if cfg.Done != nil {
 		cfg.Done(c, buf.Bytes())
@@ -141,9 +147,9 @@ func defaultLoggerInstance(c fiber.Ctx, data *Data, cfg Config) error {
 func beforeHandlerFunc(cfg Config) {
 	// If colors are enabled, check terminal compatibility
 	if cfg.enableColors {
-		cfg.Output = colorable.NewColorableStdout()
+		cfg.Stream = colorable.NewColorableStdout()
 		if os.Getenv("TERM") == "dumb" || os.Getenv("NO_COLOR") == "1" || (!isatty.IsTerminal(os.Stdout.Fd()) && !isatty.IsCygwinTerminal(os.Stdout.Fd())) {
-			cfg.Output = colorable.NewNonColorable(os.Stdout)
+			cfg.Stream = colorable.NewNonColorable(os.Stdout)
 		}
 	}
 }
diff --git a/middleware/logger/logger_test.go b/middleware/logger/logger_test.go
index d459f22c591..011a0ead455 100644
--- a/middleware/logger/logger_test.go
+++ b/middleware/logger/logger_test.go
@@ -71,7 +71,7 @@ func Test_Logger(t *testing.T) {
 
 	app.Use(New(Config{
 		Format: "${error}",
-		Output: buf,
+		Stream: buf,
 	}))
 
 	app.Get("/", func(_ fiber.Ctx) error {
@@ -94,7 +94,7 @@ func Test_Logger_locals(t *testing.T) {
 
 	app.Use(New(Config{
 		Format: "${locals:demo}",
-		Output: buf,
+		Stream: buf,
 	}))
 
 	app.Get("/", func(c fiber.Ctx) error {
@@ -171,6 +171,147 @@ func Test_Logger_Done(t *testing.T) {
 	require.Positive(t, buf.Len(), 0)
 }
 
+// Test_Logger_Filter tests the Filter functionality of the logger middleware.
+// It verifies that logs are written or skipped based on the filter condition.
+func Test_Logger_Filter(t *testing.T) {
+	t.Parallel()
+
+	t.Run("Test Not Found", func(t *testing.T) {
+		t.Parallel()
+		app := fiber.New()
+
+		logOutput := bytes.Buffer{}
+
+		// Return true to skip logging for all requests != 404
+		app.Use(New(Config{
+			Skip: func(c fiber.Ctx) bool {
+				return c.Response().StatusCode() != fiber.StatusNotFound
+			},
+			Stream: &logOutput,
+		}))
+
+		resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/nonexistent", nil))
+		require.NoError(t, err)
+		require.Equal(t, fiber.StatusNotFound, resp.StatusCode)
+
+		// Expect logs for the 404 request
+		require.Contains(t, logOutput.String(), "404")
+	})
+
+	t.Run("Test OK", func(t *testing.T) {
+		t.Parallel()
+		app := fiber.New()
+
+		logOutput := bytes.Buffer{}
+
+		// Return true to skip logging for all requests == 200
+		app.Use(New(Config{
+			Skip: func(c fiber.Ctx) bool {
+				return c.Response().StatusCode() == fiber.StatusOK
+			},
+			Stream: &logOutput,
+		}))
+
+		app.Get("/", func(c fiber.Ctx) error {
+			return c.SendStatus(fiber.StatusOK)
+		})
+
+		resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil))
+		require.NoError(t, err)
+		require.Equal(t, fiber.StatusOK, resp.StatusCode)
+
+		// We skip logging for status == 200, so "200" should not appear
+		require.NotContains(t, logOutput.String(), "200")
+	})
+
+	t.Run("Always Skip", func(t *testing.T) {
+		t.Parallel()
+		app := fiber.New()
+
+		logOutput := bytes.Buffer{}
+
+		// Filter always returns true => skip all logs
+		app.Use(New(Config{
+			Skip: func(_ fiber.Ctx) bool {
+				return true // always skip
+			},
+			Stream: &logOutput,
+		}))
+
+		app.Get("/something", func(c fiber.Ctx) error {
+			return c.Status(fiber.StatusTeapot).SendString("I'm a teapot")
+		})
+
+		_, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/something", nil))
+		require.NoError(t, err)
+
+		// Expect NO logs
+		require.Empty(t, logOutput.String())
+	})
+
+	t.Run("Never Skip", func(t *testing.T) {
+		t.Parallel()
+		app := fiber.New()
+
+		logOutput := bytes.Buffer{}
+
+		// Filter always returns false => never skip logs
+		app.Use(New(Config{
+			Skip: func(_ fiber.Ctx) bool {
+				return false // never skip
+			},
+			Stream: &logOutput,
+		}))
+
+		app.Get("/always", func(c fiber.Ctx) error {
+			return c.Status(fiber.StatusTeapot).SendString("Teapot again")
+		})
+
+		_, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/always", nil))
+		require.NoError(t, err)
+
+		// Expect some logging - check any substring
+		require.Contains(t, logOutput.String(), strconv.Itoa(fiber.StatusTeapot))
+	})
+
+	t.Run("Skip /healthz", func(t *testing.T) {
+		t.Parallel()
+		app := fiber.New()
+
+		logOutput := bytes.Buffer{}
+
+		// Filter returns true (skip logs) if the request path is /healthz
+		app.Use(New(Config{
+			Skip: func(c fiber.Ctx) bool {
+				return c.Path() == "/healthz"
+			},
+			Stream: &logOutput,
+		}))
+
+		// Normal route
+		app.Get("/", func(c fiber.Ctx) error {
+			return c.SendString("Hello World!")
+		})
+		// Health route
+		app.Get("/healthz", func(c fiber.Ctx) error {
+			return c.SendString("OK")
+		})
+
+		// Request to "/" -> should be logged
+		_, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil))
+		require.NoError(t, err)
+		require.Contains(t, logOutput.String(), "200")
+
+		// Reset output buffer
+		logOutput.Reset()
+
+		// Request to "/healthz" -> should be skipped
+		_, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/healthz", nil))
+		require.NoError(t, err)
+		require.Empty(t, logOutput.String())
+	})
+}
+
 // go test -run Test_Logger_ErrorTimeZone
 func Test_Logger_ErrorTimeZone(t *testing.T) {
 	t.Parallel()
@@ -234,7 +375,7 @@ func Test_Logger_LoggerToWriter(t *testing.T) {
 
 			app.Use("/"+level, New(Config{
 				Format: "${error}",
-				Output: LoggerToWriter(logger, tc.
+				Stream: LoggerToWriter(logger, tc.
 					level),
 			}))
 
@@ -276,7 +417,7 @@ func Test_Logger_ErrorOutput_WithoutColor(t *testing.T) {
 	app := fiber.New()
 
 	app.Use(New(Config{
-		Output:        o,
+		Stream:        o,
 		DisableColors: true,
 	}))
 
@@ -293,7 +434,7 @@ func Test_Logger_ErrorOutput(t *testing.T) {
 	app := fiber.New()
 
 	app.Use(New(Config{
-		Output: o,
+		Stream: o,
 	}))
 
 	resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil))
@@ -312,7 +453,7 @@ func Test_Logger_All(t *testing.T) {
 
 	app.Use(New(Config{
 		Format: "${pid}${reqHeaders}${referer}${scheme}${protocol}${ip}${ips}${host}${url}${ua}${body}${route}${black}${red}${green}${yellow}${blue}${magenta}${cyan}${white}${reset}${error}${reqHeader:test}${query:test}${form:test}${cookie:test}${non}",
-		Output: buf,
+		Stream: buf,
 	}))
 
 	// Alias colors
@@ -358,7 +499,7 @@ func Test_Logger_WithLatency(t *testing.T) {
 	app := fiber.New()
 
 	logger := New(Config{
-		Output: buff,
+		Stream: buff,
 		Format: "${latency}",
 	})
 	app.Use(logger)
@@ -403,7 +544,7 @@ func Test_Logger_WithLatency_DefaultFormat(t *testing.T) {
 	app := fiber.New()
 
 	logger := New(Config{
-		Output: buff,
+		Stream: buff,
 	})
 	app.Use(logger)
 
@@ -453,7 +594,7 @@ func Test_Query_Params(t *testing.T) {
 
 	app.Use(New(Config{
 		Format: "${queryParams}",
-		Output: buf,
+		Stream: buf,
 	}))
 
 	resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/?foo=bar&baz=moz", nil))
@@ -474,7 +615,7 @@ func Test_Response_Body(t *testing.T) {
 
 	app.Use(New(Config{
 		Format: "${resBody}",
-		Output: buf,
+		Stream: buf,
 	}))
 
 	app.Get("/", func(c fiber.Ctx) error {
@@ -508,7 +649,7 @@ func Test_Request_Body(t *testing.T) {
 
 	app.Use(New(Config{
 		Format: "${bytesReceived} ${bytesSent} ${status}",
-		Output: buf,
+		Stream: buf,
 	}))
 
 	app.Post("/", func(c fiber.Ctx) error {
@@ -536,7 +677,7 @@ func Test_Logger_AppendUint(t *testing.T) {
 
 	app.Use(New(Config{
 		Format: "${bytesReceived} ${bytesSent} ${status}",
-		Output: buf,
+		Stream: buf,
 	}))
 
 	app.Get("/", func(c fiber.Ctx) error {
@@ -611,7 +752,7 @@ func Test_Response_Header(t *testing.T) {
 	}))
 	app.Use(New(Config{
 		Format: "${respHeader:X-Request-ID}",
-		Output: buf,
+		Stream: buf,
 	}))
 	app.Get("/", func(c fiber.Ctx) error {
 		return c.SendString("Hello fiber!")
@@ -634,7 +775,7 @@ func Test_Req_Header(t *testing.T) {
 
 	app.Use(New(Config{
 		Format: "${reqHeader:test}",
-		Output: buf,
+		Stream: buf,
 	}))
 	app.Get("/", func(c fiber.Ctx) error {
 		return c.SendString("Hello fiber!")
@@ -658,7 +799,7 @@ func Test_ReqHeader_Header(t *testing.T) {
 
 	app.Use(New(Config{
 		Format: "${reqHeader:test}",
-		Output: buf,
+		Stream: buf,
 	}))
 	app.Get("/", func(c fiber.Ctx) error {
 		return c.SendString("Hello fiber!")
@@ -689,7 +830,7 @@ func Test_CustomTags(t *testing.T) {
 				return output.WriteString(customTag)
 			},
 		},
-		Output: buf,
+		Stream: buf,
 	}))
 	app.Get("/", func(c fiber.Ctx) error {
 		return c.SendString("Hello fiber!")
@@ -713,7 +854,7 @@ func Test_Logger_ByteSent_Streaming(t *testing.T) {
 
 	app.Use(New(Config{
 		Format: "${bytesReceived} ${bytesSent} ${status}",
-		Output: buf,
+		Stream: buf,
 	}))
 
 	app.Get("/", func(c fiber.Ctx) error {
@@ -759,7 +900,7 @@ func Test_Logger_EnableColors(t *testing.T) {
 	app := fiber.New()
 
 	app.Use(New(Config{
-		Output: o,
+		Stream: o,
 	}))
 
 	resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil))
@@ -782,7 +923,7 @@ func Benchmark_Logger(b *testing.B) {
 		app := fiber.New()
 		app.Use(New(Config{
 			Format: "${bytesReceived} ${bytesSent} ${status}",
-			Output: io.Discard,
+			Stream: io.Discard,
 		}))
 		app.Get("/", func(c fiber.Ctx) error {
 			c.Set("test", "test")
@@ -794,7 +935,7 @@ func Benchmark_Logger(b *testing.B) {
 	b.Run("DefaultFormat", func(bb *testing.B) {
 		app := fiber.New()
 		app.Use(New(Config{
-			Output: io.Discard,
+			Stream: io.Discard,
 		}))
 		app.Get("/", func(c fiber.Ctx) error {
 			return c.SendString("Hello, World!")
@@ -805,7 +946,7 @@ func Benchmark_Logger(b *testing.B) {
 	b.Run("DefaultFormatDisableColors", func(bb *testing.B) {
 		app := fiber.New()
 		app.Use(New(Config{
-			Output:        io.Discard,
+			Stream:        io.Discard,
 			DisableColors: true,
 		}))
 		app.Get("/", func(c fiber.Ctx) error {
@@ -819,7 +960,7 @@ func Benchmark_Logger(b *testing.B) {
 		logger := fiberlog.DefaultLogger()
 		logger.SetOutput(io.Discard)
 		app.Use(New(Config{
-			Output: LoggerToWriter(logger, fiberlog.LevelDebug),
+			Stream: LoggerToWriter(logger, fiberlog.LevelDebug),
 		}))
 		app.Get("/", func(c fiber.Ctx) error {
 			return c.SendString("Hello, World!")
@@ -831,7 +972,7 @@ func Benchmark_Logger(b *testing.B) {
 		app := fiber.New()
 		app.Use(New(Config{
 			Format: "${bytesReceived} ${bytesSent} ${status} ${reqHeader:test}",
-			Output: io.Discard,
+			Stream: io.Discard,
 		}))
 		app.Get("/", func(c fiber.Ctx) error {
 			c.Set("test", "test")
@@ -844,7 +985,7 @@ func Benchmark_Logger(b *testing.B) {
 		app := fiber.New()
 		app.Use(New(Config{
 			Format: "${locals:demo}",
-			Output: io.Discard,
+			Stream: io.Discard,
 		}))
 		app.Get("/", func(c fiber.Ctx) error {
 			c.Locals("demo", "johndoe")
@@ -857,7 +998,7 @@ func Benchmark_Logger(b *testing.B) {
 		app := fiber.New()
 		app.Use(New(Config{
 			Format: "${locals:demo}",
-			Output: io.Discard,
+			Stream: io.Discard,
 		}))
 		app.Get("/int", func(c fiber.Ctx) error {
 			c.Locals("demo", 55)
@@ -874,7 +1015,7 @@ func Benchmark_Logger(b *testing.B) {
 					io.Discard.Write(logString) //nolint:errcheck // ignore error
 				}
 			},
-			Output: io.Discard,
+			Stream: io.Discard,
 		}))
 		app.Get("/logging", func(ctx fiber.Ctx) error {
 			return ctx.SendStatus(fiber.StatusOK)
@@ -886,7 +1027,7 @@ func Benchmark_Logger(b *testing.B) {
 		app := fiber.New()
 		app.Use(New(Config{
 			Format: "${pid}${reqHeaders}${referer}${scheme}${protocol}${ip}${ips}${host}${url}${ua}${body}${route}${black}${red}${green}${yellow}${blue}${magenta}${cyan}${white}${reset}${error}${reqHeader:test}${query:test}${form:test}${cookie:test}${non}",
-			Output: io.Discard,
+			Stream: io.Discard,
 		}))
 		app.Get("/", func(c fiber.Ctx) error {
 			return c.SendString("Hello, World!")
@@ -898,7 +1039,7 @@ func Benchmark_Logger(b *testing.B) {
 		app := fiber.New()
 		app.Use(New(Config{
 			Format: "${bytesReceived} ${bytesSent} ${status}",
-			Output: io.Discard,
+			Stream: io.Discard,
 		}))
 		app.Get("/", func(c fiber.Ctx) error {
 			c.Set("Connection", "keep-alive")
@@ -927,7 +1068,7 @@ func Benchmark_Logger(b *testing.B) {
 		app := fiber.New()
 		app.Use(New(Config{
 			Format: "${resBody}",
-			Output: io.Discard,
+			Stream: io.Discard,
 		}))
 		app.Get("/", func(c fiber.Ctx) error {
 			return c.SendString("Sample response body")
@@ -950,7 +1091,7 @@ func Benchmark_Logger_Parallel(b *testing.B) {
 		app := fiber.New()
 		app.Use(New(Config{
 			Format: "${bytesReceived} ${bytesSent} ${status}",
-			Output: io.Discard,
+			Stream: io.Discard,
 		}))
 		app.Get("/", func(c fiber.Ctx) error {
 			c.Set("test", "test")
@@ -962,7 +1103,7 @@ func Benchmark_Logger_Parallel(b *testing.B) {
 	b.Run("DefaultFormat", func(bb *testing.B) {
 		app := fiber.New()
 		app.Use(New(Config{
-			Output: io.Discard,
+			Stream: io.Discard,
 		}))
 		app.Get("/", func(c fiber.Ctx) error {
 			return c.SendString("Hello, World!")
@@ -975,7 +1116,7 @@ func Benchmark_Logger_Parallel(b *testing.B) {
 		logger := fiberlog.DefaultLogger()
 		logger.SetOutput(io.Discard)
 		app.Use(New(Config{
-			Output: LoggerToWriter(logger, fiberlog.LevelDebug),
+			Stream: LoggerToWriter(logger, fiberlog.LevelDebug),
 		}))
 		app.Get("/", func(c fiber.Ctx) error {
 			return c.SendString("Hello, World!")
@@ -986,7 +1127,7 @@ func Benchmark_Logger_Parallel(b *testing.B) {
 	b.Run("DefaultFormatDisableColors", func(bb *testing.B) {
 		app := fiber.New()
 		app.Use(New(Config{
-			Output:        io.Discard,
+			Stream:        io.Discard,
 			DisableColors: true,
 		}))
 		app.Get("/", func(c fiber.Ctx) error {
@@ -999,7 +1140,7 @@ func Benchmark_Logger_Parallel(b *testing.B) {
 		app := fiber.New()
 		app.Use(New(Config{
 			Format: "${bytesReceived} ${bytesSent} ${status} ${reqHeader:test}",
-			Output: io.Discard,
+			Stream: io.Discard,
 		}))
 		app.Get("/", func(c fiber.Ctx) error {
 			c.Set("test", "test")
@@ -1012,7 +1153,7 @@ func Benchmark_Logger_Parallel(b *testing.B) {
 		app := fiber.New()
 		app.Use(New(Config{
 			Format: "${locals:demo}",
-			Output: io.Discard,
+			Stream: io.Discard,
 		}))
 		app.Get("/", func(c fiber.Ctx) error {
 			c.Locals("demo", "johndoe")
@@ -1025,7 +1166,7 @@ func Benchmark_Logger_Parallel(b *testing.B) {
 		app := fiber.New()
 		app.Use(New(Config{
 			Format: "${locals:demo}",
-			Output: io.Discard,
+			Stream: io.Discard,
 		}))
 		app.Get("/int", func(c fiber.Ctx) error {
 			c.Locals("demo", 55)
@@ -1042,7 +1183,7 @@ func Benchmark_Logger_Parallel(b *testing.B) {
 					io.Discard.Write(logString) //nolint:errcheck // ignore error
 				}
 			},
-			Output: io.Discard,
+			Stream: io.Discard,
 		}))
 		app.Get("/logging", func(ctx fiber.Ctx) error {
 			return ctx.SendStatus(fiber.StatusOK)
@@ -1054,7 +1195,7 @@ func Benchmark_Logger_Parallel(b *testing.B) {
 		app := fiber.New()
 		app.Use(New(Config{
 			Format: "${pid}${reqHeaders}${referer}${scheme}${protocol}${ip}${ips}${host}${url}${ua}${body}${route}${black}${red}${green}${yellow}${blue}${magenta}${cyan}${white}${reset}${error}${reqHeader:test}${query:test}${form:test}${cookie:test}${non}",
-			Output: io.Discard,
+			Stream: io.Discard,
 		}))
 		app.Get("/", func(c fiber.Ctx) error {
 			return c.SendString("Hello, World!")
@@ -1066,7 +1207,7 @@ func Benchmark_Logger_Parallel(b *testing.B) {
 		app := fiber.New()
 		app.Use(New(Config{
 			Format: "${bytesReceived} ${bytesSent} ${status}",
-			Output: io.Discard,
+			Stream: io.Discard,
 		}))
 		app.Get("/", func(c fiber.Ctx) error {
 			c.Set("Connection", "keep-alive")
@@ -1095,7 +1236,7 @@ func Benchmark_Logger_Parallel(b *testing.B) {
 		app := fiber.New()
 		app.Use(New(Config{
 			Format: "${resBody}",
-			Output: io.Discard,
+			Stream: io.Discard,
 		}))
 		app.Get("/", func(c fiber.Ctx) error {
 			return c.SendString("Sample response body")


Reviews:
user: login: ReneWerner87
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-02-27T10:18:06Z
user: login: gaby
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-02-27T12:48:15Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 1**

<details>
<summary>🧹 Nitpick comments (3)</summary><blockquote>

<details>
<summary>docs/middleware/logger.md (1)</summary><blockquote>

`161-161`: **Fix missing colon in the default configuration.**

There's a syntax issue in the ConfigDefault example - the colon is missing after Filter.


```diff
-    Filter         nil,
+    Filter:        nil,
```

</blockquote></details>
<details>
<summary>middleware/logger/logger_test.go (1)</summary><blockquote>

`183-187`: **Consider adding more diverse test cases for the Filter functionality.**

The current tests only verify filtering based on status codes. Consider adding tests for other scenarios, such as filtering based on request path, method, or headers.

</blockquote></details>
<details>
<summary>middleware/logger/config.go (1)</summary><blockquote>

`23-27`: **Documentation for the Filter property is clear, but has a minor inaccuracy.**

The comment describes the filter function as returning true to write the log, but looking at the implementation in default_logger.go, the condition is `!cfg.Filter(c)` meaning it skips logging if the filter returns false.

To make this clearer and consistent with the implementation:


```diff
-	// Filter is a function that is called before writing the log string.
-	// If it returns true, the log will be written; otherwise, it will be skipped.
+	// Filter is a function that is called before writing the log string.
+	// If it returns false, the log will be skipped; otherwise, it will be written.
```

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 86cf80630b3ee6275c189592b0594ab8c23d5a00 and 572233ea21206e6792b19a68440093b1b2b929f3.

</details>

<details>
<summary>📒 Files selected for processing (4)</summary>

* `docs/middleware/logger.md` (2 hunks)
* `middleware/logger/config.go` (3 hunks)
* `middleware/logger/default_logger.go` (1 hunks)
* `middleware/logger/logger_test.go` (1 hunks)

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (2)</summary>

* GitHub Check: repeated
* GitHub Check: Compare

</details>

<details>
<summary>🔇 Additional comments (4)</summary><blockquote>

<details>
<summary>middleware/logger/default_logger.go (1)</summary>

`93-96`: **Conditional logging filter implementation looks good.**

The filtering logic is correctly implemented with proper nil check and logical flow. This allows for selective logging based on custom criteria, which is a useful feature to reduce noise in logs.

</details>
<details>
<summary>docs/middleware/logger.md (1)</summary>

`142-142`: **Documentation for the new Filter property is clear and well-formatted.**

The new Filter property is properly documented with a clear description of its purpose and behavior.

</details>
<details>
<summary>middleware/logger/config.go (2)</summary>

`107-107`: **ConfigDefault correctly includes the new Filter field.**

The default setting of nil is appropriate for this optional functionality.

---

`136-138`: **Proper initialization of Filter in configDefault function.**

The Filter property is correctly initialized with the default value if not provided.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-02-27T13:30:56Z
user: login: JIeJaitt
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-02-27T13:31:12Z
user: login: gaby
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-02-27T13:38:43Z
user: login: coderabbitai[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-02-27T13:39:13Z
user: login: gaby
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-02-27T13:39:29Z
user: login: JIeJaitt
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-02-28T05:30:56Z
user: login: ReneWerner87
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-02-28T07:43:29Z
user: login: gaby
state: CHANGES_REQUESTED
author_association: MEMBER
submitted_at: 2025-02-28T15:11:37Z
user: login: JIeJaitt
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-02T05:45:15Z
user: login: gaby
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-02T05:46:32Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 1**

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 2154baf7513a53ecdf7631af4c329bc1b4d6142b and 6528b9f02f30258e45efbece1dd0fa936c1fe088.

</details>

<details>
<summary>📒 Files selected for processing (1)</summary>

* `docs/whats_new.md` (1 hunks)

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (8)</summary>

* GitHub Check: unit (1.24.x, macos-13)
* GitHub Check: unit (1.24.x, macos-latest)
* GitHub Check: unit (1.24.x, windows-latest)
* GitHub Check: unit (1.23.x, macos-13)
* GitHub Check: unit (1.23.x, windows-latest)
* GitHub Check: Analyse
* GitHub Check: Compare
* GitHub Check: repeated

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-03T01:48:29Z
user: login: gaby
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-03T12:44:48Z
user: login: gaby
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-03T12:48:45Z
user: login: JIeJaitt
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-03T12:52:04Z
user: login: gaby
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-03T13:08:11Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 0**

<details>
<summary>♻️ Duplicate comments (1)</summary><blockquote>

<details>
<summary>docs/whats_new.md (1)</summary><blockquote>

`943-961`: _⚠️ Potential issue_

**Critical issue: Inconsistent filter example logic.**

The documentation states that when the Filter function returns **true** the log will be written, and when it returns **false** the log will be skipped. In the provided examples, the second example (logging only for status 200) is consistent with this behavior. However, in the first example the comment indicates that logging should be skipped for 404 requests, but the code returns:

```go
return c.Response().StatusCode() == fiber.StatusNotFound
```

This condition returns **true** when the status code is 404, meaning a 404 response would be logged rather than skipped. To align the logic with the intended behavior (i.e., *skip logging for 404 responses*), consider inverting the condition. For example:

```diff
-        return c.Response().StatusCode() == fiber.StatusNotFound
+        return c.Response().StatusCode() != fiber.StatusNotFound
```

This change would ensure that the log is written only when the status code is not 404, thereby *skipping* 404 responses as the comment suggests.

</blockquote></details>

</blockquote></details>

<details>
<summary>🧹 Nitpick comments (1)</summary><blockquote>

<details>
<summary>docs/middleware/logger.md (1)</summary><blockquote>

`142-142`: **New Filter Property Documentation Addition**  
The configuration table now includes the `Filter` property with the function signature `func(fiber.Ctx) bool` and a clear description. This documentation clearly explains that the function is invoked before writing the log output and will skip logging if it returns false. Consider adding a brief usage example in the "Examples" section to illustrate how to leverage this filter in a real scenario.

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 9e6f4fd408aa1c93dff654392614995a5c73748f and 75976afbc447f140857167e14dc37b4314898b18.

</details>

<details>
<summary>📒 Files selected for processing (5)</summary>

* `docs/middleware/logger.md` (2 hunks)
* `docs/whats_new.md` (1 hunks)
* `middleware/logger/config.go` (3 hunks)
* `middleware/logger/default_logger.go` (1 hunks)
* `middleware/logger/logger_test.go` (1 hunks)

</details>

<details>
<summary>🔇 Additional comments (7)</summary><blockquote>

<details>
<summary>middleware/logger/default_logger.go (1)</summary>

`18-21`: **Filter implementation looks correct.**

This implementation adds a filter capability to the logger, allowing conditional logging based on request context. The placement at the beginning of the function is efficient as it avoids unnecessary processing for requests that should be skipped.

</details>
<details>
<summary>middleware/logger/logger_test.go (1)</summary>

`174-224`: **Test structure looks good and effectively validates the filter functionality.**

The test implementation properly verifies the filter functionality with two distinct test cases:
1. Verifying that logs are written for 404 status codes when filtered to only log those
2. Verifying that logs are not written for 200 status codes with the same filter

The tests correctly use a single middleware instance with both the filter and output capture, which accurately tests the filter's behavior.

</details>
<details>
<summary>middleware/logger/config.go (4)</summary>

`23-27`: **Well-defined and properly documented feature addition.**

The new `Filter` function field is well-designed and appropriately documented. It provides a clean extension point for conditionally logging requests based on custom logic, which addresses issue #3300 as mentioned in the PR objectives.

---

`107-107`: **Appropriate default value for the new Filter field.**

Setting the default value to `nil` is appropriate as it:
1. Maintains backward compatibility
2. Makes the feature optional
3. Follows the pattern of other optional function fields in the Config struct

---

`136-138`: **Proper implementation of default value handling.**

The implementation correctly follows the established pattern for handling default values in the `configDefault` function. This ensures consistent behavior when users don't explicitly provide a filter function.

---

`23-27`: <details>
<summary>✅ Verification successful</summary>

**Verify usage examples are provided in documentation.**

The implementation looks good, but users might benefit from usage examples in the documentation showing how to implement filter functions for common scenarios (e.g., filtering by status code, path, etc.).

---

🏁 Script executed:

```shell
#!/bin/bash
# Check if examples are provided in the logger middleware documentation

# Look for Filter examples in docs
echo "Checking for Filter usage examples in documentation..."
grep -A 10 "Filter" docs/middleware/logger.md || echo "No Filter examples found in logger documentation"

# Check if the new feature is mentioned in what's new document
echo "Checking for mention in what's new document..."
grep -A 5 "Filter" docs/whats_new.md || echo "No mention of Filter in what's new document"
```

Length of output: 3951

---



</details>

**Documentation Examples Verified**

The Filter usage examples are clearly present in both `docs/middleware/logger.md` and `docs/whats_new.md`, demonstrating common scenarios like filtering based on status codes. No further action is required.

</details>
<details>
<summary>docs/middleware/logger.md (1)</summary>

`161-161`: **Default Config Update: 'Filter' Initialization**  
The default configuration now initializes the `Filter` property to `nil`, which preserves the existing logging behavior when no filter is provided. This is consistent with the documented default in the configuration table.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-03T13:20:29Z
user: login: gaby
body: One last thing
state: CHANGES_REQUESTED
author_association: MEMBER
submitted_at: 2025-03-03T13:29:21Z
user: login: gaby
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-03T14:37:23Z
user: login: gaby
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-03T14:39:57Z
user: login: JIeJaitt
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-03T14:49:44Z
user: login: gaby
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-03T14:52:24Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 0**

<details>
<summary>🧹 Nitpick comments (2)</summary><blockquote>

<details>
<summary>docs/whats_new.md (1)</summary><blockquote>

`921-925`: **Fix inconsistency between comment and code.**

The comment states "log status code >= 400" but the code actually checks for status codes >= 404 (StatusNotFound). For accurate documentation, you should either update the comment or modify the condition.

```diff
- // log status code >= 400
+ // log status code >= 404
  return c.Response().StatusCode() >= fiber.StatusNotFound
```

Alternatively, to match the comment:
```diff
  // log status code >= 400
- return c.Response().StatusCode() >= fiber.StatusNotFound
+ return c.Response().StatusCode() >= fiber.StatusBadRequest
```

</blockquote></details>
<details>
<summary>middleware/logger/logger_test.go (1)</summary><blockquote>

`174-176`: **Test function documentation could be improved.**

Consider adding a documentation comment to explain the purpose of this test function.

```diff
+// Test_Logger_Filter tests the Filter functionality of the logger middleware.
+// It verifies that logs are written or skipped based on the filter condition.
 func Test_Logger_Filter(t *testing.T) {
 	t.Parallel()
```

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 288edb2611c64976c9e54eaf79de82d2caefee65 and e2295e51073d01c84fdc9a65c0d7309ef967ecca.

</details>

<details>
<summary>📒 Files selected for processing (3)</summary>

* `docs/middleware/logger.md` (2 hunks)
* `docs/whats_new.md` (1 hunks)
* `middleware/logger/logger_test.go` (1 hunks)

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (5)</summary>

* GitHub Check: unit (1.24.x, macos-13)
* GitHub Check: unit (1.24.x, windows-latest)
* GitHub Check: unit (1.23.x, windows-latest)
* GitHub Check: Compare
* GitHub Check: repeated

</details>

<details>
<summary>🔇 Additional comments (7)</summary><blockquote>

<details>
<summary>docs/whats_new.md (3)</summary>

`915-916`: **Filter function documentation looks good.**

The description clearly explains the purpose of the Filter function: it acts as a condition to determine whether a request should be logged.

---

`935-940`: **Ensure this filter works as expected.**

The comment and implementation align correctly. When the filter returns true (status != 200), the log will be written, which means logs will be skipped for 200 status codes.

---

`928-932`: <details>
<summary>❓ Verification inconclusive</summary>

**Inconsistent filtering logic.**

This example has a potential issue with the filter logic. The comment says "log status code == 404", but the filter is returning true when the status is 404. The documentation at line 915-916 states that when the filter returns true, the log will be written. However, there seems to be confusion in past review comments about how this interacts with the defaultLogger.

Let's verify the implementation to ensure this works as expected:

---

🏁 Script executed:

```shell
#!/bin/bash
# Check how the Filter is used in the logger middleware implementation

# Search for places where the Filter is checked
rg -A 3 "if c\.Filter" middleware/logger/
```

Length of output: 43

---



</details>

**Action Required: Verify Logger Filter Usage Consistency**

The documentation snippet in `docs/whats_new.md` (lines 928–932) shows a logger configuration with a `Filter` function that returns true when the response status is equal to `fiber.StatusNotFound`. According to the docs (lines 915–916), returning true should trigger logging. However, our initial search for usage of `c.Filter` in the logger middleware (using `rg -A 3 "if c\.Filter" middleware/logger/`) did not produce any output.

Please manually verify the logger middleware implementation—especially within the `logger.New` configuration in the `middleware/logger/` directory—to ensure that the Filter logic is applied as expected and interacts correctly with the default logger behavior.

</details>
<details>
<summary>middleware/logger/logger_test.go (2)</summary>

`177-198`: **Test implementation for 404 filters looks good.**

The test correctly sets up a logger middleware with a filter that only logs 404 responses, and then verifies that the output contains "404". This confirms the filter behavior works as expected.

---

`200-225`: **Test implementation for 200 filter looks good.**

The test correctly sets up a filter that logs everything except 200 responses, and verifies that the output does not contain "200". This confirms that the filter successfully skips logging OK responses.

</details>
<details>
<summary>docs/middleware/logger.md (2)</summary>

`142-142`: **Filter property documentation looks good.**

The description clearly explains what the Filter function does and how it affects logging behavior.

---

`161-161`: _⚠️ Potential issue_

**Fix syntax error in ConfigDefault.**

There's a missing comma after `nil` in the ConfigDefault declaration, which would cause a compilation error.

```diff
- Filter         nil,
+ Filter:         nil,
```



> Likely an incorrect or invalid review comment.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-04T03:14:07Z
user: login: ReneWerner87
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-06T08:08:04Z
user: login: ReneWerner87
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-06T08:08:34Z
user: login: JIeJaitt
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-06T08:42:50Z
user: login: ReneWerner87
state: APPROVED
author_association: MEMBER
submitted_at: 2025-03-07T07:24:18Z
user: login: gaby
body: 👍 LGTM
state: APPROVED
author_association: MEMBER
submitted_at: 2025-03-09T15:59:44Z
user: login: ReneWerner87
state: APPROVED
author_association: MEMBER
submitted_at: 2025-03-10T07:58:04Z
user: login: ReneWerner87
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-10T08:00:46Z

Review comments:
diff_hunk: @@ -20,6 +20,12 @@ type Config struct {
 	// Optional. Default: nil
 	Next func(c fiber.Ctx) bool
 
+	// Filter is a function that is called before writing the log string.
+	// If it returns true, the log will be written; otherwise, it will be skipped.
+	//
+	// Optional. Default: nil
+	Filter func(c fiber.Ctx) bool
path: middleware/logger/config.go
user: login: ReneWerner87
body: would say that we also need the logstring here, as perhaps filtering should happen based on it
created_at: 2025-02-27T10:18:06Z
author_association: MEMBER
diff_hunk: @@ -20,6 +20,12 @@ type Config struct {
 	// Optional. Default: nil
 	Next func(c fiber.Ctx) bool
 
+	// Filter is a function that is called before writing the log string.
+	// If it returns true, the log will be written; otherwise, it will be skipped.
+	//
+	// Optional. Default: nil
+	Filter func(c fiber.Ctx) bool
path: middleware/logger/config.go
user: login: gaby
body: @ReneWerner87 What do you mean by logstring? 
created_at: 2025-02-27T12:48:15Z
author_association: MEMBER
diff_hunk: @@ -171,6 +171,68 @@ func Test_Logger_Done(t *testing.T) {
 	require.Positive(t, buf.Len(), 0)
 }
 
+func Test_Logger_Filter(t *testing.T) {
+	t.Parallel()
+
+	// Log only 404 status codes
+	t.Run("Test Not Found", func(t *testing.T) {
+		t.Parallel()
+		app := fiber.New()
+
+		// Create a logging middleware that filters logs to only include 404 status codes
+		app.Use(New(Config{
+			Filter: func(c fiber.Ctx) bool {
+				return c.Response().StatusCode() == fiber.StatusNotFound
+			},
+		}))
+
+		logOutput := bytes.Buffer{}
+
+		// Redirect log output to the logOutput buffer
+		app.Use(New(Config{
+			Output: &logOutput,
+		}))
+
+		resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/nonexistent", nil))
+		require.NoError(t, err)
+		require.Equal(t, fiber.StatusNotFound, resp.StatusCode)
+
+		// Verify the log output contains the "404" message
+		require.Contains(t, logOutput.String(), "404") // Check if the log contains "404"
+	})
+
+	// Subtest for 200 status code
+	t.Run("Test OK", func(t *testing.T) {
+		t.Parallel()
+		app := fiber.New()
+
+		// Create a logging middleware that filters logs to only include 404 status codes
+		app.Use(New(Config{
+			Filter: func(c fiber.Ctx) bool {
+				return c.Response().StatusCode() == fiber.StatusNotFound
+			},
+		}))
+
+		logOutput := bytes.Buffer{}
+
+		app.Get("/", func(c fiber.Ctx) error {
+			return c.SendStatus(fiber.StatusOK)
+		})
+
+		// Redirect log output to the logOutput buffer
+		app.Use(New(Config{
+			Output: &logOutput,
+		}))
+
+		resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil))
+		require.NoError(t, err)
+		require.Equal(t, fiber.StatusOK, resp.StatusCode)
+
+		// Verify the log output does not contain the "200" message
+		require.NotContains(t, logOutput.String(), "200") // Check if the log does not contain "200"
+	})
+}
path: middleware/logger/logger_test.go
user: login: coderabbitai[bot]
body: _:hammer_and_wrench: Refactor suggestion_

**Test structure for Logger_Filter has potential issues.**

The test implementation has a few issues:

1. In both subtests, you're registering two separate logger middlewares - one with the filter and another with the output capture. This means you're not actually testing if the filter works, but rather if the second middleware gets executed.

2. The middleware registration order might affect test results since in Fiber the order matters.


I recommend restructuring the tests to use a single logger middleware with both filter and output capture:

```diff
func Test_Logger_Filter(t *testing.T) {
	t.Parallel()

	t.Run("Test Not Found", func(t *testing.T) {
		t.Parallel()
		app := fiber.New()
		
		logOutput := bytes.Buffer{}

-		// Create a logging middleware that filters logs to only include 404 status codes
-		app.Use(New(Config{
-			Filter: func(c fiber.Ctx) bool {
-				return c.Response().StatusCode() == fiber.StatusNotFound
-			},
-		}))
-
-		// Redirect log output to the logOutput buffer
-		app.Use(New(Config{
-			Output: &logOutput,
-		}))
+		// Create a single logging middleware with both filter and output capture
+		app.Use(New(Config{
+			Filter: func(c fiber.Ctx) bool {
+				return c.Response().StatusCode() == fiber.StatusNotFound
+			},
+			Output: &logOutput,
+		}))

		resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/nonexistent", nil))
		require.NoError(t, err)
		require.Equal(t, fiber.StatusNotFound, resp.StatusCode)

		// Verify the log output contains the "404" message
		require.Contains(t, logOutput.String(), "404")
	})

	// Similar changes for the second subtest...
}
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
func Test_Logger_Filter(t *testing.T) {
	t.Parallel()

	t.Run("Test Not Found", func(t *testing.T) {
		t.Parallel()
		app := fiber.New()

		logOutput := bytes.Buffer{}

		// Create a single logging middleware with both filter and output capture
		app.Use(New(Config{
			Filter: func(c fiber.Ctx) bool {
				return c.Response().StatusCode() == fiber.StatusNotFound
			},
			Output: &logOutput,
		}))

		resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/nonexistent", nil))
		require.NoError(t, err)
		require.Equal(t, fiber.StatusNotFound, resp.StatusCode)

		// Verify the log output contains the "404" message
		require.Contains(t, logOutput.String(), "404")
	})

	t.Run("Test OK", func(t *testing.T) {
		t.Parallel()
		app := fiber.New()

		logOutput := bytes.Buffer{}

		// Create a single logging middleware with both filter and output capture
		app.Use(New(Config{
			Filter: func(c fiber.Ctx) bool {
				return c.Response().StatusCode() == fiber.StatusNotFound
			},
			Output: &logOutput,
		}))

		app.Get("/", func(c fiber.Ctx) error {
			return c.SendStatus(fiber.StatusOK)
		})

		resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil))
		require.NoError(t, err)
		require.Equal(t, fiber.StatusOK, resp.StatusCode)

		// Verify the log output does not contain the "200" message
		require.NotContains(t, logOutput.String(), "200")
	})
}
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-02-27T13:30:56Z
author_association: CONTRIBUTOR
diff_hunk: @@ -20,6 +20,12 @@ type Config struct {
 	// Optional. Default: nil
 	Next func(c fiber.Ctx) bool
 
+	// Filter is a function that is called before writing the log string.
+	// If it returns true, the log will be written; otherwise, it will be skipped.
+	//
+	// Optional. Default: nil
+	Filter func(c fiber.Ctx) bool
path: middleware/logger/config.go
user: login: JIeJaitt
body: > @ReneWerner87 What do you mean by logstring?

may be like this: https://github.com/gofiber/fiber/blob/86cf80630b3ee6275c189592b0594ab8c23d5a00/middleware/logger/config.go#L27
created_at: 2025-02-27T13:31:12Z
author_association: CONTRIBUTOR
diff_hunk: @@ -171,6 +171,68 @@ func Test_Logger_Done(t *testing.T) {
 	require.Positive(t, buf.Len(), 0)
 }
 
+func Test_Logger_Filter(t *testing.T) {
+	t.Parallel()
+
+	// Log only 404 status codes
+	t.Run("Test Not Found", func(t *testing.T) {
+		t.Parallel()
+		app := fiber.New()
+
+		// Create a logging middleware that filters logs to only include 404 status codes
+		app.Use(New(Config{
+			Filter: func(c fiber.Ctx) bool {
+				return c.Response().StatusCode() == fiber.StatusNotFound
+			},
+		}))
+
+		logOutput := bytes.Buffer{}
+
+		// Redirect log output to the logOutput buffer
+		app.Use(New(Config{
+			Output: &logOutput,
+		}))
+
+		resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/nonexistent", nil))
+		require.NoError(t, err)
+		require.Equal(t, fiber.StatusNotFound, resp.StatusCode)
+
+		// Verify the log output contains the "404" message
+		require.Contains(t, logOutput.String(), "404") // Check if the log contains "404"
+	})
+
+	// Subtest for 200 status code
+	t.Run("Test OK", func(t *testing.T) {
+		t.Parallel()
+		app := fiber.New()
+
+		// Create a logging middleware that filters logs to only include 404 status codes
+		app.Use(New(Config{
+			Filter: func(c fiber.Ctx) bool {
+				return c.Response().StatusCode() == fiber.StatusNotFound
+			},
+		}))
+
+		logOutput := bytes.Buffer{}
+
+		app.Get("/", func(c fiber.Ctx) error {
+			return c.SendStatus(fiber.StatusOK)
+		})
+
+		// Redirect log output to the logOutput buffer
+		app.Use(New(Config{
+			Output: &logOutput,
+		}))
+
+		resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil))
+		require.NoError(t, err)
+		require.Equal(t, fiber.StatusOK, resp.StatusCode)
+
+		// Verify the log output does not contain the "200" message
+		require.NotContains(t, logOutput.String(), "200") // Check if the log does not contain "200"
+	})
+}
path: middleware/logger/logger_test.go
user: login: gaby
body: Are you sure?
created_at: 2025-02-27T13:38:43Z
author_association: MEMBER
diff_hunk: @@ -171,6 +171,68 @@ func Test_Logger_Done(t *testing.T) {
 	require.Positive(t, buf.Len(), 0)
 }
 
+func Test_Logger_Filter(t *testing.T) {
+	t.Parallel()
+
+	// Log only 404 status codes
+	t.Run("Test Not Found", func(t *testing.T) {
+		t.Parallel()
+		app := fiber.New()
+
+		// Create a logging middleware that filters logs to only include 404 status codes
+		app.Use(New(Config{
+			Filter: func(c fiber.Ctx) bool {
+				return c.Response().StatusCode() == fiber.StatusNotFound
+			},
+		}))
+
+		logOutput := bytes.Buffer{}
+
+		// Redirect log output to the logOutput buffer
+		app.Use(New(Config{
+			Output: &logOutput,
+		}))
+
+		resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/nonexistent", nil))
+		require.NoError(t, err)
+		require.Equal(t, fiber.StatusNotFound, resp.StatusCode)
+
+		// Verify the log output contains the "404" message
+		require.Contains(t, logOutput.String(), "404") // Check if the log contains "404"
+	})
+
+	// Subtest for 200 status code
+	t.Run("Test OK", func(t *testing.T) {
+		t.Parallel()
+		app := fiber.New()
+
+		// Create a logging middleware that filters logs to only include 404 status codes
+		app.Use(New(Config{
+			Filter: func(c fiber.Ctx) bool {
+				return c.Response().StatusCode() == fiber.StatusNotFound
+			},
+		}))
+
+		logOutput := bytes.Buffer{}
+
+		app.Get("/", func(c fiber.Ctx) error {
+			return c.SendStatus(fiber.StatusOK)
+		})
+
+		// Redirect log output to the logOutput buffer
+		app.Use(New(Config{
+			Output: &logOutput,
+		}))
+
+		resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil))
+		require.NoError(t, err)
+		require.Equal(t, fiber.StatusOK, resp.StatusCode)
+
+		// Verify the log output does not contain the "200" message
+		require.NotContains(t, logOutput.String(), "200") // Check if the log does not contain "200"
+	})
+}
path: middleware/logger/logger_test.go
user: login: coderabbitai[bot]
body: @gaby Yes, I am sure about the suggestion. The current test structure has a fundamental issue that prevents it from properly testing the Filter functionality:

In the current test setup, you have two separate logger middleware instances:
1. First middleware with the Filter (but no output capture)
2. Second middleware with the Output capture (but no filter)

With this setup, the Filter in the first middleware doesn't actually affect what gets captured by the second middleware. The second middleware will always log requests regardless of what the first one does.

For a valid test of the Filter functionality, you need to configure a single middleware instance that has both the Filter and Output, so you can verify that the Filter properly controls whether logging happens.

The suggested change ensures that the test actually validates what it's intended to test - that the Filter function correctly determines whether logging occurs.

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-02-27T13:39:13Z
author_association: CONTRIBUTOR
diff_hunk: @@ -20,6 +20,12 @@ type Config struct {
 	// Optional. Default: nil
 	Next func(c fiber.Ctx) bool
 
+	// Filter is a function that is called before writing the log string.
+	// If it returns true, the log will be written; otherwise, it will be skipped.
+	//
+	// Optional. Default: nil
+	Filter func(c fiber.Ctx) bool
path: middleware/logger/config.go
user: login: gaby
body: I dont think we need it since this is only defined during the config?

The user can't call this function. So the logfilter or whatever the filter uses will be inside that function. Right?
created_at: 2025-02-27T13:39:29Z
author_association: MEMBER
diff_hunk: @@ -20,6 +20,12 @@ type Config struct {
 	// Optional. Default: nil
 	Next func(c fiber.Ctx) bool
 
+	// Filter is a function that is called before writing the log string.
+	// If it returns true, the log will be written; otherwise, it will be skipped.
+	//
+	// Optional. Default: nil
+	Filter func(c fiber.Ctx) bool
path: middleware/logger/config.go
user: login: JIeJaitt
body: > I dont think we need it since this is only defined during the config?
> 
> The user can't call this function. So the logfilter or whatever the filter uses will be inside that function. Right?

I agree with you.
created_at: 2025-02-28T05:30:56Z
author_association: CONTRIBUTOR
diff_hunk: @@ -20,6 +20,12 @@ type Config struct {
 	// Optional. Default: nil
 	Next func(c fiber.Ctx) bool
 
+	// Filter is a function that is called before writing the log string.
+	// If it returns true, the log will be written; otherwise, it will be skipped.
+	//
+	// Optional. Default: nil
+	Filter func(c fiber.Ctx) bool
path: middleware/logger/config.go
user: login: ReneWerner87
body: ![image](https://github.com/user-attachments/assets/1e3c2c24-3805-4ef6-be0c-f5b0bc096730)
from the feature request https://github.com/gofiber/fiber/issues/3300

@gaby @JIeJaitt 

created_at: 2025-02-28T07:43:29Z
author_association: MEMBER
diff_hunk: @@ -90,6 +90,11 @@ func defaultLoggerInstance(c fiber.Ctx, data *Data, cfg Config) error {
 			buf.WriteString("\n")
 		}
 
+		// Check if Filter is defined and call it
+		if cfg.Filter != nil && !cfg.Filter(c) {
path: middleware/logger/default_logger.go
user: login: gaby
body: @JIeJaitt This can be moved to the beginning of the function. There's no point is running all that only to have the filter return True and skip 😂
created_at: 2025-02-28T15:11:32Z
author_association: MEMBER
diff_hunk: @@ -20,6 +20,12 @@ type Config struct {
 	// Optional. Default: nil
 	Next func(c fiber.Ctx) bool
 
+	// Filter is a function that is called before writing the log string.
+	// If it returns true, the log will be written; otherwise, it will be skipped.
+	//
+	// Optional. Default: nil
+	Filter func(c fiber.Ctx) bool
path: middleware/logger/config.go
user: login: JIeJaitt
body: > ![image](https://private-user-images.githubusercontent.com/7063188/417929998-1e3c2c24-3805-4ef6-be0c-f5b0bc096730.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3NDA4OTQ1NDMsIm5iZiI6MTc0MDg5NDI0MywicGF0aCI6Ii83MDYzMTg4LzQxNzkyOTk5OC0xZTNjMmMyNC0zODA1LTRlZjYtYmUwYy1mNWIwYmMwOTY3MzAucG5nP1gtQW16LUFsZ29yaXRobT1BV1M0LUhNQUMtU0hBMjU2JlgtQW16LUNyZWRlbnRpYWw9QUtJQVZDT0RZTFNBNTNQUUs0WkElMkYyMDI1MDMwMiUyRnVzLWVhc3QtMSUyRnMzJTJGYXdzNF9yZXF1ZXN0JlgtQW16LURhdGU9MjAyNTAzMDJUMDU0NDAzWiZYLUFtei1FeHBpcmVzPTMwMCZYLUFtei1TaWduYXR1cmU9NTZlZDg0NWY4MGY5MTQ0NWQyNGZmMGQzMzEyYTVjNmJjMjk0YWRjZWE4ZjliYzI4NjY5Zjc5ZWU2YWE4OWJjMSZYLUFtei1TaWduZWRIZWFkZXJzPWhvc3QifQ.PHjqqUQmP3EE8x-L-nEIGTB_Dv8tWgIinHgXKLGrGFM) from the feature request #3300
> 
> @gaby @JIeJaitt

No problem, I'll add it
created_at: 2025-03-02T05:45:15Z
author_association: CONTRIBUTOR
diff_hunk: @@ -20,6 +20,12 @@ type Config struct {
 	// Optional. Default: nil
 	Next func(c fiber.Ctx) bool
 
+	// Filter is a function that is called before writing the log string.
+	// If it returns true, the log will be written; otherwise, it will be skipped.
+	//
+	// Optional. Default: nil
+	Filter func(c fiber.Ctx) bool
path: middleware/logger/config.go
user: login: gaby
body: @JIeJaitt We don't need it.
created_at: 2025-03-02T05:46:32Z
author_association: MEMBER
diff_hunk: @@ -940,6 +940,26 @@ The Healthcheck middleware has been enhanced to support more than two routes, wi
 
 Refer to the [healthcheck middleware migration guide](./middleware/healthcheck.md) or the [general migration guide](#-migration-guide) to review the changes.
 
+### Filter
+
+The `Filter` is a function that is called before the log string for a request is written to Output. If it returns true, the log will be written; otherwise, it will be skipped.
+
+```go
+app.Use(logger.New(logger.Config{
+    Filter: func(c fiber.Ctx) bool {
+        // Skip logging for 404 requests
+        return c.Response().StatusCode() == fiber.StatusNotFound
+    },
+}))
+
+app.Use(logger.New(logger.Config{
+    Filter: func(c fiber.Ctx) bool {
+        // Only log requests with status code 200
+        return c.Response().StatusCode() == fiber.StatusOK
+    },
+}))
+```
path: docs/whats_new.md
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

_⚠️ Potential issue_

**Critical Issue: Inconsistent Filter Example Logic**

The documentation states that when the Filter function returns **true** the log will be written, and when it returns **false** the log will be skipped. In the provided examples, the second example (logging only for status 200) is consistent with this behavior. However, in the first example the comment indicates that logging should be skipped for 404 requests, but the code returns:

```go
return c.Response().StatusCode() == fiber.StatusNotFound
```

This condition returns **true** when the status code is 404, meaning a 404 response would be logged rather than skipped. To align the logic with the intended behavior (i.e., *skip logging for 404 responses*), consider inverting the condition. For example:

```diff
-        return c.Response().StatusCode() == fiber.StatusNotFound
+        return c.Response().StatusCode() != fiber.StatusNotFound
```

This change would ensure that the log is written only when the status code is not 404, thereby *skipping* 404 responses as the comment suggests.

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-03-03T01:48:29Z
author_association: CONTRIBUTOR
diff_hunk: @@ -15,6 +15,11 @@ import (
 
 // default logger for fiber
 func defaultLoggerInstance(c fiber.Ctx, data *Data, cfg Config) error {
+	// Check if Filter is defined and call it
+	if cfg.Filter != nil && !cfg.Filter(c) {
path: middleware/logger/default_logger.go
user: login: gaby
body: @JIeJaitt I think this is wrong. It should be:

```go
if cfg.Filter != nil && cfg.Filter(c)
```
In the current implementation it's using a negated approach which is not user friendly.
created_at: 2025-03-03T12:44:48Z
author_association: MEMBER
diff_hunk: @@ -15,6 +15,11 @@ import (
 
 // default logger for fiber
 func defaultLoggerInstance(c fiber.Ctx, data *Data, cfg Config) error {
+	// Check if Filter is defined and call it
+	if cfg.Filter != nil && !cfg.Filter(c) {
path: middleware/logger/default_logger.go
user: login: gaby
body: Nevermind. ChatGPT agreed.

> Return true if the log entry should pass the filter (be logged) and false otherwise. To make it more user-friendly, clearly document it or rename it (e.g., ShouldLog()) so the meaning of returning true is obvious.
created_at: 2025-03-03T12:48:45Z
author_association: MEMBER
diff_hunk: @@ -15,6 +15,11 @@ import (
 
 // default logger for fiber
 func defaultLoggerInstance(c fiber.Ctx, data *Data, cfg Config) error {
+	// Check if Filter is defined and call it
+	if cfg.Filter != nil && !cfg.Filter(c) {
path: middleware/logger/default_logger.go
user: login: JIeJaitt
body: > @JIeJaitt I think this is wrong. It should be:
> 
> ```go
> if cfg.Filter != nil && cfg.Filter(c)
> ```
> 
> In the current implementation it's using a negated approach which is not user friendly.

I do not think so, Filter is the meaning of filtering, the function returns true, that is, filtering success.
created_at: 2025-03-03T12:52:03Z
author_association: CONTRIBUTOR
diff_hunk: @@ -15,6 +15,11 @@ import (
 
 // default logger for fiber
 func defaultLoggerInstance(c fiber.Ctx, data *Data, cfg Config) error {
+	// Check if Filter is defined and call it
+	if cfg.Filter != nil && !cfg.Filter(c) {
path: middleware/logger/default_logger.go
user: login: gaby
body: We can leave as initially implemented. All good
created_at: 2025-03-03T13:08:11Z
author_association: MEMBER
diff_hunk: @@ -940,6 +940,26 @@ The Healthcheck middleware has been enhanced to support more than two routes, wi
 
 Refer to the [healthcheck middleware migration guide](./middleware/healthcheck.md) or the [general migration guide](#-migration-guide) to review the changes.
 
+### Filter
path: docs/whats_new.md
user: login: gaby
body: @JIeJaitt This should be under the Logger section in line 872
created_at: 2025-03-03T13:29:09Z
author_association: MEMBER
diff_hunk: @@ -912,6 +912,29 @@ func main() {
 
 </details>
 
+The `Filter` is a function that is called before the log string for a request is written to Output. If it returns true, the log will be written; otherwise, it will be skipped.
+
+<details>
+<summary>Example</summary>
+
+```go
+app.Use(logger.New(logger.Config{
+    Filter: func(c fiber.Ctx) bool {
+        // Skip logging for 404 requests
+        return c.Response().StatusCode() == fiber.StatusNotFound
path: docs/whats_new.md
user: login: gaby
body: Shouldn't this be `!=` ?

If its 404 it returns True, which the defaultLogger negates into False and doesn't filter
created_at: 2025-03-03T14:37:15Z
author_association: MEMBER
diff_hunk: @@ -171,6 +171,58 @@ func Test_Logger_Done(t *testing.T) {
 	require.Positive(t, buf.Len(), 0)
 }
 
+func Test_Logger_Filter(t *testing.T) {
path: middleware/logger/logger_test.go
user: login: gaby
body: I'm confused about how this is working.

If the condition is True, it will become False in defaultLogger. But we dont check for that in the test?
created_at: 2025-03-03T14:39:54Z
author_association: MEMBER
diff_hunk: @@ -171,6 +171,58 @@ func Test_Logger_Done(t *testing.T) {
 	require.Positive(t, buf.Len(), 0)
 }
 
+func Test_Logger_Filter(t *testing.T) {
path: middleware/logger/logger_test.go
user: login: JIeJaitt
body: @gaby Thanks for the heads up. I've just checked and found a few things that need to be refined, it's almost midnight here, I'll submit a more complete unit test and documentation note tomorrow.
created_at: 2025-03-03T14:49:44Z
author_association: CONTRIBUTOR
diff_hunk: @@ -171,6 +171,58 @@ func Test_Logger_Done(t *testing.T) {
 	require.Positive(t, buf.Len(), 0)
 }
 
+func Test_Logger_Filter(t *testing.T) {
path: middleware/logger/logger_test.go
user: login: gaby
body: Sounds good. Thanks! 
created_at: 2025-03-03T14:52:24Z
author_association: MEMBER
diff_hunk: @@ -912,6 +912,29 @@ func main() {
 
 </details>
 
+The `Filter` is a function that is called before the log string for a request is written to Output. If it returns true, the log will be written; otherwise, it will be skipped.
+
+<details>
+<summary>Example</summary>
+
+```go
+app.Use(logger.New(logger.Config{
+    Filter: func(c fiber.Ctx) bool {
+        // Skip logging for 404 requests
+        return c.Response().StatusCode() == fiber.StatusNotFound
path: docs/whats_new.md
user: login: ReneWerner87
body: well, if he only wants to log every 404, then that should be fine
depends on what he wants to do

it is always logged if the condition is true
-> if of course he wants everything else to be logged except 404, then an unequal should be used
created_at: 2025-03-06T08:08:04Z
author_association: MEMBER
diff_hunk: @@ -912,6 +912,29 @@ func main() {
 
 </details>
 
+The `Filter` is a function that is called before the log string for a request is written to Output. If it returns true, the log will be written; otherwise, it will be skipped.
+
+<details>
+<summary>Example</summary>
+
+```go
+app.Use(logger.New(logger.Config{
+    Filter: func(c fiber.Ctx) bool {
+        // Skip logging for 404 requests
+        return c.Response().StatusCode() == fiber.StatusNotFound
path: docs/whats_new.md
user: login: ReneWerner87
body: @JIeJaitt 
created_at: 2025-03-06T08:08:34Z
author_association: MEMBER
diff_hunk: @@ -912,6 +912,29 @@ func main() {
 
 </details>
 
+The `Filter` is a function that is called before the log string for a request is written to Output. If it returns true, the log will be written; otherwise, it will be skipped.
+
+<details>
+<summary>Example</summary>
+
+```go
+app.Use(logger.New(logger.Config{
+    Filter: func(c fiber.Ctx) bool {
+        // Skip logging for 404 requests
+        return c.Response().StatusCode() == fiber.StatusNotFound
path: docs/whats_new.md
user: login: JIeJaitt
body: @ReneWerner87 I've added new examples in the documentation, you can take a look at the

```go
app.Use(logger.New(logger.Config{
    Filter: func(c fiber.Ctx) bool {
        // log status code >= 400
        return c.Response().StatusCode() >= fiber.StatusBadRequest
    },
}))

app.Use(logger.New(logger.Config{
    Filter: func(c fiber.Ctx) bool {
        // log status code == 404
        return c.Response().StatusCode() == fiber.StatusNotFound
    },
}))

app.Use(logger.New(logger.Config{
    Filter: func(c fiber.Ctx) bool {
        // log status code != 200
        return c.Response().StatusCode() != fiber.StatusOK
    },
}))
```
created_at: 2025-03-06T08:42:50Z
author_association: CONTRIBUTOR
diff_hunk: @@ -15,6 +15,12 @@ import (
 
 // default logger for fiber
 func defaultLoggerInstance(c fiber.Ctx, data *Data, cfg Config) error {
+	// Check if Filter is defined and call it.
+	// Now, if Filter(c) == true, we SKIP logging:
+	if cfg.Skip != nil && cfg.Skip(c) {
+		return nil // Skip logging if Filter returns true
+	}
path: middleware/logger/default_logger.go
user: login: ReneWerner87
body: ```suggestion
	// Check if Skip is defined and call it.
	// Now, if Skip(c) == true, we SKIP logging:
	if cfg.Skip != nil && cfg.Skip(c) {
		return nil // Skip logging if Skip returns true
	}
```
created_at: 2025-03-10T08:00:46Z
author_association: MEMBER

PR comments:
user: login: coderabbitai[bot]
created_at: 2025-02-27T08:38:14Z
body: <!-- This is an auto-generated comment: summarize by coderabbit.ai -->
<!-- walkthrough_start -->

## Walkthrough
The pull request modifies the logger middleware configuration in the Fiber framework by renaming the `Output` property to `Stream` and introducing a new `Skip` property that determines if logging should be skipped based on a condition. The default configuration is updated accordingly. The changes also include updates to the documentation, tests, and the logger's control flow to accommodate these modifications.

## Changes
| File(s)                                        | Change Summary                                                                                                                                      |
|------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------|
| docs/middleware/logger.md<br>middleware/logger/config.go | Renamed `Output` to `Stream`, added `Skip` property, and updated default configuration and related documentation.                                   |
| middleware/logger/default_logger.go            | Introduced a conditional check for `cfg.Skip` in the logging process to skip logging if the condition is met, and updated output references to `Stream`. |
| middleware/logger/logger_test.go               | Added `Test_Logger_Filter` with subtests to validate logging behavior based on HTTP response statuses; updated references from `Output` to `Stream`. |
| docs/whats_new.md                             | Added a new section describing the `Skip` function with examples demonstrating its usage in the logging middleware.                                  |

## Poem
> In the meadow of code where logs do play,  
> A rabbit hops forth to brighten the day.  
> With `Stream` in the mix and `Skip` by my side,  
> I’ll log only the tales that I take in stride.  
> Hooray for the changes that help us convey!  
> 🐇✨

<!-- walkthrough_end -->


---

<details>
<summary>📜 Recent review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Free**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between b479e966b650be3acde6e6f540fc73c37206cb9a and 903c2acb18b964179cbaa61801b8218cd6c88dce.

</details>

<details>
<summary>📒 Files selected for processing (1)</summary>

* `middleware/logger/default_logger.go` (4 hunks)

</details>

<details>
<summary>🚧 Files skipped from review as they are similar to previous changes (1)</summary>

* middleware/logger/default_logger.go

</details>

</details>
<!-- internal state start -->


<!-- DwQgtGAEAqAWCWBnSTIEMB26CuAXA9mAOYCmGJATmriQCaQDG+Ats2bgFyQAOFk+AIwBWJBrngA3EsgEBPRvlqU0AgfFwA6NPEgQAfACgjoCEYDEZyAAUASpADK2VmgqyjcEj2wAbb5AokAI7Y0riQ4rjedJAARIC8G4Cku5AAZiTUXACCtPQAYvDeNHz43OL4WASQ3vhEpHzM8NlRAO4uJDGQLciI2AL1uDT0cpDYiJSQAFIAkiTj2v3oGPSoDezwyfDRaMi2kBhOAmMAzMeH/FgA4uoAEj0aMLCevPDMLvLc2BTc+KP8yeEI2zsqAq8AwuAoimwDE8aBS+UK/BK8DK4Xw/08VRqY3qjRILQCABp0NkAohEKCiCgySFIGZjgAGel3DyQJSIBgUeBIlH4PDeUHSf7UdGQAAGeQKlFFKWwGDEyKwqAYaF80QOyXwARFmMgiHBFJSmvQ/iCIT1VI6nP6ZFRIt5uHemkgkz+uAeMrlpSwAVwHwwyHBISJbox1Q6+T8B0t6hoGAA3PwQxQmkgSET1OHfJAo4gANZc7h0DTmSwAYRYbDByEczlc7ndTFY7C6oi9vxFO1S1A+gthtCQDBG5JRTxenO88gOuCaJBtUxmczCmHoNjIJAA6pRyBQABwAdiJKbdCjBnIEeAVyGSEOYkCIKlkd3nsxj/m0o2QsKYcq36AE9uNWUMxoc03WFChZS6KFoTJZIfAnM50XgPgqmVPwXgYBByGzPAUm0bxkFBEVLlwG4BEgDJ5TKZBS0mSAyAkZCykrJ1V3ITcKG3fcTUQL5FhkeQtnzDBKTWCZphfeYqHKd0QNwZBUJVBCj1gEVABwCMAqGhQBcAhSbw0CIQ8EEwi0RmiColBoMQ32hE9+y9QiLjRXhqioZhEGLAxzgfSAhHwUEAwbMopAoRBqAVIk9XCkShTCEN5GkkU5IU/A0IQkYDVFF5c08OTpRUgCyCYWVCgNTB5GSfCew8yArnwGcQqJbA+P8WUMEy7LQxEvKiXvIZv3WCh3NikUwrYGzPG/eyFX4ARRgoKQlhkzxaNZfBBVSwcKDubyhl40QfGoQUwLi90kG6TwXknTwAn0gZbRDE0zPbR6xs8UU0G4bhpQC3BMFstAOW+ZBmB8cRuCicJQkc8FIWhJaOxsIz4BM1pWowdqYqI7gXCUkg/GXdBrWYJEYoqAIiCQBFXrQcaITwEhPIAVQKZ4jonXqfO/BhvGwNkRseoC4uh9AtRxskDQwer00WFHospE6RXOmkrsgWA0CkbNZ29aR8G8RbmSCjAQrCtsECIWB+Qt+SRSYXwVE1cLNZIZINQoOK0VhnookQWB8HwJdFh4vXNbkg1lcFW6jvoCpHvIJpGHVkTBVBWHaChaIiMe2wdp8lVEDRbosT1QLhWfRdiXoWFRioxUsFFWhUsQAB6Jp1fkgB9eONGYWhRRl/tlXEMn28R3U/Z8QYbrSWh5AqNgKFIdBkmpgFK/UBUnwkivAdzKWmiiWhSEC1Ai+Pr0iSl4eFdHx7RXWSUKAACgASmlODPRm5UsA1QdkBRJQDAkAKjANlA0mIUbhjdABcgdADTz0wAZTw+0gHrAYDwCEMFyQxUsvhf+rp3Q6j1JyESnl9DGHAFAMg9B8B/DQHgQgpBtzRwUE2MEXBeCzREPKKQAkFBKCoKodQWgdDkJMFAOAyxPxYHoQQYga4qD3UbCxLgVAE7dFrJOeQTABEqDUJobQugwCGAoaYAwjcGAtxxLQZorRm6YlqD3WgHADAxDcQYCwFFJjyOYfdDRY55C0MTpgY+9ZJpJ2PpAZgig1hz0IdUWoUSGg2LxGjAa8AiAfHCiiLO7o8gHD4NeWmeJNS5mzAlMgtMypB2arQeWGDiiUHEJHfGLCKgOINPaR0ht3oAHk8COmlC5Qs7t5DqxkNrE0GBikxzRKKew4I0jMH7h0YyqlUD7xFGyDJ0z7rDKaRVI0KDYkGjbpQUMRBPxaiaFaWMdxywUFJHxfsIkOa20UJ4EgAAPWmENBTq01gcG0tS2logCMkKI1k3RKgiWmFAcpeYvJvtPaZ9QYpBMehIFw8AVCQ2vCwMUD8SDSgqB9BgWCAAy1RpSE0elsAuQCWE6iJbsYpKQbxigAIwACZDgaExCSuZgMsH8upT0+ijyjRsDJEg+F2owyNMxpSD+tc1ZbHQARNEQKsAgvuhUY5yQ4meHjpUMMLLUWMwotkDeZQlKyCJLCE1+zRmsrYPQeZ+ZvpqomTaVOEJ07w2RqjXw9VkBmVCraSylA0WeDEh0mKvteTeCnrqT1hZBhbGiCiL8ZRpplHFaKAAImUYlDSRm4DGeq7VwxuB1L1WiUEg8jojQzEqPGVcV5jEekQ/UWNkA3JjDaUlCyAi0x+v6GgaAaF/FFP0h0eBRTiosU4dg2SsDjK1jaKdQgRj1pNBC1s6IfiYRCSnZaJ51iZMUTNP6AhIaE2+b8n2nlPEZEfmugMnt3SAP0te6i7ZvlfHdlmvg7w72QNWM0xARgoBWAhOWwS2Q6BcA9VyaUGLZCFkJbKBgT91gFI0KWXAXyX7Zn9t4cdupAzWVFOWDAl7KMNybs3axtiAj2ISZQJxooDCwfgwcmtdbkNijnYM4JydZlihHUs9DrpMPvWRBodcVopRyuIVCMItGygMblUxyxLHklsZIBxrE21e48agIWl29CCiQCxbzTwurhOzoGQu8TpBJPzMWWO+FG8VTwAAF7mTmd8DQCzG5uaIqKLFfA6OXqs5VMGjGLFWMM6k9jDiuPmdce43jZjWPpeM5ligzd0lEA0EQfALi3ExA8ZYDI3imHKD8U4AJ7YT3J2gwYFkHXInRP7IakUWn6MZOlOp6yuT3oFfxEVzjJWysVfwO/fInghgBFRfA90Ln524GW/jTz0mx1BtUvNXh6BPzRgRG7U1ly7hZDzdM3w9rjQmvWPt110RUNeuWKeOGdBjsWlhCqtsiso1DQFP2h4SYbuUwTRPZNWtU0FjVJmmhiobY+j9HZlUIQ7gACEA6qS86O5ZCx3X2E9TSrUY4cpV3/tyR7UDVJKES7Z+zZp2yilC+F+0NKg6inahRokTzWySHxo+SAYSxRxYyQlmzu3seclxbGsNtbQX7shadaQ4TT2IBln5/kAXMoU7Q7aAX+Q+f0ACBDQGmVRNuZUlJ7zyyHWat1CQD2Ypue4Ai7tgtZW5dJY9Kq1A/W1gbEk6MLXUnKdwne7Hb91mwbY4cygP4rbkBXwaYxJQCNHplayV6cVED0HXcwqIYSyKnck72wj1ATnJNkG6FqDMLP5fICYI81sCExJZ4NRHzyvSQpKWDA8Y9sLkBkCTrZfP2mMmF5msUByhoUIJINEMJS9UDTXdhNErUB6vnwDvRc2HlJiFLLJyKFQ+R1BzzRFNG1j2EJ5i5DDikL76vvuXwnzwP7cbL6CUA01HuiNDA35HQUgw2C6ygDyHe3WxmRQ3tzCEU2U3UFUyInG00xlyIEY2mzsWK1Kzn3K0q2lEACTCavC/FAlTCgSjTA6XIg3AtLGbEzWoQgkbYgpbXjSAWAhHKdXPFDE3bgYPJ+UvI/LjIjEjMjPWWg6jLAhg3TPAjLObNgy9RbCzSAAANWxWV0E2jhQ2wMDwKEYNxGYIIIWxINtFBB5j5nekEK4EF0twoNpi4G9193UJyBwzbAbxQwDyTwKBEKIMgA0GCOwNI2wOMJSVMOUPMKW1tCjzFAYGSHK0EMgAAF5IADC/DNBBCfoZ1EjkjY8Nl8guCPDP4URvCEiiDDDcAAj2CgiQiiCwj5CotFDZtTMVCMk1DbRy8GAylRR8iwtndx09QZ5OcBikCeMasYN8smD8DlC28wYO5itFtqt3FX1GsFEWF/FXh2sJ8pdesz004M4Xs8Q7JH8VRE4K85U74FiCgqVTNJgJ1/pS1gcb00QwcY009jR41lV4Qxgn5+ikiwtKdSNUAWcBQq4g4SB2cjpP1wgIJPAzksA0pohHd88Ph1swhvwaAvlNMGBF1nQCElRc1H8LQ2BcBR9PBXiURMdOJJ8XA3kXZUgeFxckcvpNsT8DQXIRUrUHs7VKT+BXMwg2Rh410V838YoN1q0G92UCVATysJizcBjDtSciJQZWY/lTUh5LwZZrCkV5VKQB1r53da5pEY4goqhBFIYulcIo9mpritt1RNQSArhlwogKBSj8TDYYVT0okYk3tApJoyhYY/AIV6pykNUqgUwYoy8SSvQLifjsxUdEJHoH5CgABySfGEtsQmFUQoQMwUnbKjEnYYUYega7H4j/CiL/S8B6RPHmf/WswAr5IDEA0Db2CDMEDeaQGDSAAAOTRDzOa1rJ/1ZFEF/Q/QAxbOAJAy8HAwgK7Kgw6HOSiSnVjRkjBLWGSGLCmLyzACMFaJYMoCPIoA7jkhWJy1q3WJ8Wa2iG2NcF2N1ylwG3T1+wDV7F2FOLkmDzbFFGgFCA7nuNqA7glEKEozvmKzPNCC6KJQBysMRUFFGGHz8G6AEGSltHswaGbVTL+JIWVU8IVH8wrReniVMySRMNaHFVQvQvgpsNiH/PNAHLCByF5EWAJHaDbhRlUh6NzFLjCExCuU8Ca0UWiGuwABZ6QxKqNuwO8Pk9dL8YgGKwhekABpdi1ZLiuzSgcPY6UeAS0WY1AOO8TY+6a7blRkaS30WStkO7a1eMlCnoGihFOimIN9FoWQasT1dSzikydJIaPiyMm7QStk9NIkGIPs6EsYQQ7ytZeif0aqEafStGFUdy5AAITvXPB1IOGIFI5uB4PM2AALGKzSnigKpKrUF/L6USo0auQsVBSBfKgobih4XouK2gL4VOcVA4z8N3UECQEORzNXY02fdghfFEfFW8bbMTYdIYuVdU8GSGG3GCf4emC2ACR6b82C+FJtY06uBAFeOVP2BOHUG05A5AJOFJWgKst9QoSc0cv/P9f0Kc1s2csAzsiIKA3siKhOTagi7NJDZxbDOUGAACoCygEC3Cp+MIAAKjDlIWgDfjlRZSylmKUPaMgvPJIN7IAFkPc/Z6ByQiBdkexdCBgUMVV+y8Qn5QjKNkbDyCDliLDyD74cNKamhqbGjaaVsxR6blDGbYin55quRIZfpnjIcVyprIs0qSAlrUT1AicVTRQX4ca8bFBdQdluwtQKiWbgalLAK5sobIBYbQh38EaubIYUaKK0bWCMboKmagb0E9awbn4Ya4byszakbubLbIi5j0a5soK9QujBawZhb1yoofxxatRJaFdUBrd9J4YmcnDlllauDcboECaNbfQtahrnMKanb/bFICJDbja9RTbEaiI6bUa2ibb/bMbYjma87QaC7Up85i63aNAPaK6vbea/bTMA7NALDg6NSRaniI7VkJbFTY6Zb465bjxicZMU6oA078b1aibNbBqhNAadbHam6+7i1yA26Tb4by6f5u6q6TyTz+6uiG7Wb8696S1D7S7j7zaptz6Gba67aBahbNTRax6zko7J7pbZb6BHd56x1F7IBl61bCbibs7N7ybb7d7gKABRSVCgaAZ4EgAALQftdqPvdpPrhAtp7prr7rrrIIdpBr1H1r7tQYhHQcwZwYPrwafoIZfp5rfr5o/sDsHu/pHvDuWv/r6SFJ+iAZnpAflqTqVpVvTtXtgY3r0MobvuAudugHwFQMKEfuvg7sIcrqturuPNtp4frqUaQfBtUfUeoK0bLvYe9qMwvqMYHq/pDp/tHsEYeAAZEYtDjsBlnoVqGIgagYzrXqzoUbJtMeoedo7joc1CQI7nXHlvtHLEtOsefs9uIc4d7uAvIcgBvt1rMdPJiYoDiYSegTwGSc1FSbYfSdfv0Yce4acelCHoWrDr+j/o8eEZ21EZNGAcTrAeTpkZXpgfXtJtzsQcif9qKaQKqZ0dsZIcMYaevoidwBoZQbQemZYe0c7tPoybqffrIc/qab4dabFvHs8a6e8ent8YkbnsVsCdVuCfkdGa3sbomb7rfW8Bme2aIdqZ9utoWYOeMYoe3qoZWaiY+a+d0bPr2a4cBcacgGadDvhQEcFCEZEy8ant6dAbucGegczpJu1tebBf9tKdgApSOjlFkEhbmcydIeycOdyeWdWfBtJfJdjAYCpc2ZsZqY4ZhayfBpycRdcZRcjs6bE0xfEb6ZxdToebkZGcJfGeJb7tZYpY5Y7mqJYqGmoGpZ5bscK3qbhaWZBeUZZflrZaKlkHVayM1ZeBqK5bSa7t2b+YMZK0caDuOeRbafcYnoxbEeualYCdxceflZzpecVY7gAEUQhXAO4rBcZ3IdXHXfn7H9n6WgXGXjWAKo3KBLW423JEBE2dnk39XU2BWGWhX+GvXUWOn0WLmJX/XsXA2ZXZHhnQnnmEH8nqHVxeJqISAO4CdZ5C2fneXnWDW034W8md6u3pA+JRh+3FBOWjb27vm9HR3S3TzBWPXf7vXznxW/WE7G2a97mW38W4HFHM3p3ggAKB3F2S6tmoWnWU3YXx2jWiWO5Vwr3qGb2h3V2n3+WN3y2t23Hq2fW6392/GpHj2hnT2wmxnO2lXgKMgqrFgmZU4f3oW13n2y303J3QXmXTykPCwUO0P7Xqmk2R2/26XsP4WK2Tn2nQO92enJXD2F6g25W22FX4P32Z3e2O4rgZ5KB0PH2S2sOAOcOmXu3Z2+3+O1yXal38HZndX5nXXFneGXHK3Tm0Xo7umfGD3JH+npHm3oOQmCXQ2O2p2VmP2+OBO5O73uXyO9Woj/2r77aL3LOghrPZOhPi2nOqOxOaOgORWzmxWpamOG39PpWl7ZXW3TP4GJOggZOBFPOBFvOKORPnOcncO9aP3EvwbcvbPl2H2fPfa/OXPnHh66Od2QuY7wObn/Gj22OYuz3wm3OO5Sxd0WBoADIC3SPFOHPlPL7MumX2u9ROvuvUvHOSuAWX21OKvPXNOa3tPLmsWIum2ouT2TPmu4OLP8P+3ZAaB7B2AO4VSKQJuBu3XXO32om8d9uSBDuwRjvndTveuV2MPKPpvqP3X1PKuQPd3QvdOIODOoO8XNvYOw2uOonkHplj8KnQozvaWPv/PX3w3IfoeohYeev5PWG+ui20vfPEeyujnvv5v6O/uauwu9Pbm1vIHouYP23KG8cipYAad8On5yI7PSE8ciu8epuVPDXLvWbGe5RmeXBcxWf2f26ueaW+XSvN3ift3fvqudOrnKf6vWOjOQennOP0EhfMIWeom828ZPmJeFOpelOEe+eZuTGQXdeReKAxeDfcZVRjesftGzf+uLfBvAP5fgPRXa3GOAe6vIPLzpj9zzFmM25qBEAu48QnFVirz6sNjfE7zWsdiglurnytz4UjjlrHVTia42xJsxQUtW525o/u5zNh3QdpAOQxCCyvt34/qsAVIi/EzWjvSrwm+xzCgY1xbodYQdR2AHzE1J5EdKr01+A+AjTYw6zPBTriylk7gXQRRqSdYs6nrAw4Vu0wwh/5ANkjL0rNRc8EwxJKoCI4UM8YciwYAmg0QRhZVH0SYfYDLs8VhAaS0RpKqUoaht8jQrhoBoAVgSAOZXpA8QpOZpBYITi7SjwygCEfSk8l7b9pJGUUKyvwkFA+xS4WACSkyHuCoA+CpJKfKcxGqXoskagfkMRQxSkVT8USUQEnCQC3gN8IaaMpSCUBSAqgIyOEgam0RxlaysoARBpRMiJlNoHwDyCWGrK3Vv8X6X/OOUbL/pmyL1GhO2XnJxUPqPZLgt9RSBpA22fBZzDkR/IzRW+a+GKK0QvTz5HqumEvpH07gV8+4IfPLCYnERtV2wsiRhCZWiDKJ2AqiNAOolT4Pl+oHyQRPohERGJDAjg5ROoA7gNBo+AQRiHiDoAdxrws4DuFFHdghDTEUAWgIcHpBZCBAnKAAKycpjg0IblAADYSAe4ekAIAYACA9wKgekGgDEolDMhYlPcJyhIBZDuUaAHcLCDESUJIAO4BgAAE5DghQ2gEMJKHcpuUqgVQCQEGGpASAYlNAIcGSBNDaAVQg4E0NaElCdwO4blGkMcECAWhgw2YSUJKECASheQyoe0MBhKAyhJQ5IHkIkrJAGAe4Q4AwEOB7hzKJQ6oYMJ6HGIDAjgwYdkIYCdDqhnKHcAIEGElCxKnKPcIMOqFoA0AJQ8EfSE5QCBdh4IhgLQG+E7CxhngchACL6HhCVmUQjuDEI2AzhaACQgIH22oQhCgAA== -->

<!-- internal state end -->
<!-- tips_start -->

---

> [!NOTE]
> <details>
> <summary>🎁 Summarized by CodeRabbit Free</summary>
> 
> Your organization has reached its limit of developer seats under the Pro Plan. For new users, CodeRabbit will generate a high-level summary and a walkthrough for each pull request. For a comprehensive line-by-line review, please add seats to your subscription by visiting <https://app.coderabbit.ai/login>.If you believe this is a mistake and have available seats, please assign one to the pull request author through the subscription management page using the link above.
> 
> </details>

<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=gofiber/fiber&utm_content=3333):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Generate unit testing code for this file.`
  - `Open a follow-up GitHub issue for this discussion.`
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples:
  - `@coderabbitai generate unit testing code for this file.`
  -	`@coderabbitai modularize this function.`
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
  - `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.`
  - `@coderabbitai read src/utils.ts and generate unit testing code.`
  - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`
  - `@coderabbitai help me debug CodeRabbit configuration file.`

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (Invoked using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically.

### CodeRabbit Configuration File (`.coderabbit.yaml`)

- You can programmatically configure CodeRabbit by adding a `.coderabbit.yaml` file to the root of your repository.
- Please see the [configuration documentation](https://docs.coderabbit.ai/guides/configure-coderabbit) for more information.
- If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: `# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json`

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->
author_association: CONTRIBUTOR
user: login: codecov[bot]
created_at: 2025-02-27T08:40:51Z
body: ## [Codecov](https://app.codecov.io/gh/gofiber/fiber/pull/3333?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=gofiber) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 83.67%. Comparing base [(`600ebd9`)](https://app.codecov.io/gh/gofiber/fiber/commit/600ebd95ce741474724de93ea4ca920678f60e54?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=gofiber) to head [(`903c2ac`)](https://app.codecov.io/gh/gofiber/fiber/commit/903c2acb18b964179cbaa61801b8218cd6c88dce?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=gofiber).
> Report is 2 commits behind head on main.

<details><summary>Additional details and impacted files</summary>


```diff
@@           Coverage Diff           @@
##             main    #3333   +/-   ##
=======================================
  Coverage   83.66%   83.67%           
=======================================
  Files         118      118           
  Lines       11711    11718    +7     
=======================================
+ Hits         9798     9805    +7     
  Misses       1485     1485           
  Partials      428      428           
```

| [Flag](https://app.codecov.io/gh/gofiber/fiber/pull/3333/flags?src=pr&el=flags&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=gofiber) | Coverage Δ | |
|---|---|---|
| [unittests](https://app.codecov.io/gh/gofiber/fiber/pull/3333/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=gofiber) | `83.67% <100.00%> (+<0.01%)` | :arrow_up: |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=gofiber#carryforward-flags-in-the-pull-request-comment) to find out more.

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/gofiber/fiber/pull/3333?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=gofiber).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=gofiber).

<details><summary>🚀 New features to boost your workflow: </summary>

- ❄ [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
</details>
author_association: NONE
user: login: JIeJaitt
created_at: 2025-02-27T09:58:23Z
body: @ReneWerner87 hi, there
Strange, this unit test runs locally for me without any problem, but reports an error in GitHub Action
author_association: CONTRIBUTOR
user: login: ReneWerner87
created_at: 2025-02-27T10:19:25Z
body: > @ReneWerner87 hi, there Strange, this unit test runs locally for me without any problem, but reports an error in GitHub Action

did you also run this local with the “-race” flag ?
author_association: MEMBER
user: login: gaby
created_at: 2025-02-27T12:51:30Z
body: > > @ReneWerner87 hi, there Strange, this unit test runs locally for me without any problem, but reports an error in GitHub Action
> 
> did you also run this local with the “-race” flag ?

I just ran them locally with `make test` and it doesn't fail.
author_association: MEMBER
user: login: gaby
created_at: 2025-02-27T12:54:58Z
body: > > > @ReneWerner87 hi, there Strange, this unit test runs locally for me without any problem, but reports an error in GitHub Action
> > 
> > 
> > did you also run this local with the “-race” flag ?
> 
> I just ran them locally with `make test` and it doesn't fail.

Using `make longtest` does trigger the same race condition we are seeing in the CI
author_association: MEMBER
user: login: gaby
created_at: 2025-02-27T13:07:58Z
body: > > > > @ReneWerner87 hi, there Strange, this unit test runs locally for me without any problem, but reports an error in GitHub Action
> > > 
> > > 
> > > did you also run this local with the “-race” flag ?
> > 
> > 
> > I just ran them locally with `make test` and it doesn't fail.
> 
> Using `make longtest` does trigger the same race condition we are seeing in the CI

Pretty sure this is related to re-using the same `app` across all the new tests introduced in this PR. They are both running in parallel and registering `/`.
author_association: MEMBER
user: login: gaby
created_at: 2025-02-27T13:24:13Z
body: ok, unit-tests are passing now.
author_association: MEMBER
user: login: gaby
created_at: 2025-03-02T05:47:26Z
body: @JIeJaitt Add a section in docs/whats_new.md should be ready to merge after that.
author_association: MEMBER
user: login: JIeJaitt
created_at: 2025-03-02T07:10:33Z
body: @gaby I thought about it and you are right. The filter() function can focus on the decision-making logic without having to care about the specific processing details of the log string.
author_association: CONTRIBUTOR
user: login: gaby
created_at: 2025-03-03T13:16:45Z
body: @coderabbitai full review
author_association: MEMBER
user: login: coderabbitai[bot]
created_at: 2025-03-03T13:16:50Z
body: <!-- This is an auto-generated reply by CodeRabbit -->
<details>
<summary>:white_check_mark: Actions performed</summary>

Full review triggered.

</details>
author_association: CONTRIBUTOR
user: login: gaby
created_at: 2025-03-09T15:55:27Z
body: @ksw2000 @efectn Can you take a look at this PR? Thanks
author_association: MEMBER
user: login: gaby
created_at: 2025-03-09T15:56:08Z
body: @JIeJaitt I updated the logic per recommendation from `OpenAI o1` and added more unit tests
author_association: MEMBER
user: login: gaby
created_at: 2025-03-10T03:17:43Z
body: Renamed:
- Function `Filter()` -> `Skip()`
- Field `Output` -> `Stream`

These now match the official expressjs middleware https://github.com/expressjs/morgan
author_association: MEMBER
user: login: JIeJaitt
created_at: 2025-03-10T08:00:41Z
body: 👍 LGTM
author_association: CONTRIBUTOR