Skip to content

feat: add logging middleware#4

Merged
itsLeonB merged 2 commits into
mainfrom
dev
Sep 4, 2025
Merged

feat: add logging middleware#4
itsLeonB merged 2 commits into
mainfrom
dev

Conversation

@itsLeonB

@itsLeonB itsLeonB commented Sep 3, 2025

Copy link
Copy Markdown
Owner

Summary by CodeRabbit

  • New Features
    • Added request logging to improve visibility into HTTP traffic and errors.
  • Refactor
    • Internalized an error-handling entry point with no behavior changes.
  • Tests
    • Added comprehensive tests for logging across success, error, query, and OPTIONS scenarios.
  • Chores
    • CI now runs on push to main only (pull requests unchanged).
    • Upgraded and added dependencies for stability and security.

@coderabbitai

coderabbitai Bot commented Sep 3, 2025

Copy link
Copy Markdown

Walkthrough

Updates CI push triggers to only run for main, renames an exported error middleware method to unexported, adds a new HTTP logging middleware with unit tests, and updates several module dependencies.

Changes

Cohort / File(s) Summary
CI workflow triggers
.github/workflows/ci.yml
Restricts on.push.branches from ['main','dev'] to ['main']; pull_request trigger unchanged.
Error middleware symbol scope
error_middleware.go
Renames exported Handle → unexported handle and updates its internal call site; no logic change.
Logging middleware + tests
logging_middleware.go, test/logging_middleware_test.go
Adds MiddlewareProvider.NewLoggingMiddleware() returning a gin.HandlerFunc that skips OPTIONS, records timing, builds full path, calls ctx.Next(), and logs via Infof (<400) or Errorf (≥400) with optional error; adds table-driven tests covering success, client/server errors, query params, and OPTIONS skip.
Dependency updates
go.mod
Bumps github.com/itsLeonB/ezutil/v2v2.1.0; adds github.com/shopspring/decimal v1.4.0 (indirect); upgrades several golang.org/x/* and Google proto/protobuf indirect deps.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor Client
  participant Gin as Gin Router
  participant LM as LoggingMiddleware
  participant H as Handler
  participant L as Logger

  Client->>Gin: HTTP Request
  Gin->>LM: Invoke middleware
  alt Method == OPTIONS
    LM-->>Gin: Bypass logging
  else Proceed
    LM->>LM: Record start time & build full path
    LM->>H: ctx.Next()
    H-->>LM: Response (status, errors)
    LM->>LM: Compute duration, gather status, client IP
    alt status >= 400
      LM->>L: Errorf(method, path, status, duration, ip, optional err)
    else status < 400
      LM->>L: Infof(method, path, status, duration, ip)
    end
  end
  Gin-->>Client: HTTP Response
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • chore: bump ezutil to v2 #3 — Overlaps at go.mod ezutil v2 bump and touches error_middleware.go, indicating code-level relation to dependency and middleware changes.

Poem

A whisk of logs, a hop through routes,
I thump the ground as traffic flutes.
For 2xx skies I purr with cheer,
For 5xx storms I twitch an ear.
OPTIONS glide with silent grace—
I nose the trail, then leave no trace. 🐇✨

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch dev

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • 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.
  • 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 the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore or @coderabbit 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 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

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
go.mod (1)

3-3: Align go.mod directive with CI-tested Go versions
Go 1.21+ toolchains refuse to load a module whose go directive exceeds their version, so CI builds on Go 1.23/1.24 will fail with go 1.25.0. Lower the directive to the minimum CI version or raise the CI matrix to only test 1.25+.

-go 1.25.0
+go 1.23
🧹 Nitpick comments (8)
.github/workflows/ci.yml (1)

5-5: Push trigger now excludes dev — confirm policy is intentional

Limiting CI on push to main can delay feedback on dev. If unintentional, add dev back to push branches.

 on:
   push:
-    branches: [main]
+    branches: [main, dev]
error_middleware.go (2)

23-28: Fix doc comment/name mismatches after unexporting

Comments still reference exported names; align to identifiers to avoid lint drift.

-// NewErrorMiddleware creates an error handling middleware for Gin.
+// newErrorMiddleware creates an error handling middleware for Gin.
@@
-// Handle is the main middleware function that processes errors and panics
+// handle is the main middleware function that processes errors and panics

Also applies to: 35-36


23-33: API consistency: consider exporting NewErrorMiddleware

Other middleware constructors are exported (e.g., NewCorsMiddleware, NewAuthMiddleware). Consider exporting for consistency unless you explicitly want it internal-only.

Please confirm no external consumers rely on an exported constructor here; I can open a follow-up PR to rename safely.

logging_middleware.go (4)

12-15: Skip logging for OPTIONS — good call

Prevents noisy logs for CORS preflights. Consider also skipping HEAD or well-known health endpoints if desired.


21-25: Build full path more simply (preserve exact query/encoding)

Use RequestURI to avoid recomposing and to preserve original encoding and duplicate keys.

-		// Build full path with query string for logging
-		fullPath := path
-		if rawQuery := ctx.Request.URL.RawQuery; rawQuery != "" {
-			fullPath = path + "?" + rawQuery
-		}
+		// Build full path with query string for logging
+		fullPath := ctx.Request.RequestURI

35-61: Optional: include route pattern and keep errors concise

To reduce log cardinality, include ctx.FullPath() when available; also consider logging only the last error to avoid multi-line blobs.

-		if statusCode >= 400 {
-			errorMsg := ""
-			if len(ctx.Errors) > 0 {
-				errorMsg = ctx.Errors.String()
-			}
+		if statusCode >= 400 {
+			errorMsg := ""
+			if last := ctx.Errors.Last(); last != nil && last.Err != nil {
+				errorMsg = last.Error()
+			}
+			route := ctx.FullPath()
+			if route == "" {
+				route = method + " " + path
+			}
@@
-					method,
-					fullPath,
+					method,
+					fullPath, // consider adding route=%s as a separate field

10-73: Privacy note

Query strings can contain PII/secrets. If that’s a concern, add redaction or an allowlist for logged query params.

test/logging_middleware_test.go (1)

73-82: Strengthen OPTIONS test: assert no logging occurred

Optionally assert no Infof/Errorf calls were made to catch regressions.

 		{
 			name:       "OPTIONS request skipped",
@@
 			setupMock: func(m *MockLogger) {
-				// No logging expected for OPTIONS
+				// No expectations registered; we’ll assert none later
 			},
 		},

Add after ServeHTTP for this case:

if tt.method == "OPTIONS" {
	mockLogger.AssertNotCalled(t, "Infof", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything)
	mockLogger.AssertNotCalled(t, "Errorf", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything)
}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 6e3dae0 and b5a3a29.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (5)
  • .github/workflows/ci.yml (1 hunks)
  • error_middleware.go (1 hunks)
  • go.mod (2 hunks)
  • logging_middleware.go (1 hunks)
  • test/logging_middleware_test.go (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-08-27T09:32:11.080Z
Learnt from: itsLeonB
PR: itsLeonB/ginkgo#1
File: middlewares.go:39-44
Timestamp: 2025-08-27T09:32:11.080Z
Learning: In the ginkgo package, the NewAuthMiddleware function intentionally uses log.Fatalf for tokenCheckFunc validation as a fail-fast design choice, similar to the CORS middleware pattern.

Applied to files:

  • test/logging_middleware_test.go
  • logging_middleware.go
📚 Learning: 2025-08-27T09:29:56.699Z
Learnt from: itsLeonB
PR: itsLeonB/ginkgo#1
File: middlewares.go:23-28
Timestamp: 2025-08-27T09:29:56.699Z
Learning: In the ginkgo package, the NewCorsMiddleware function intentionally uses log.Fatalf for CORS configuration validation as a fail-fast design choice rather than degrading gracefully.

Applied to files:

  • test/logging_middleware_test.go
  • logging_middleware.go
🧬 Code graph analysis (2)
test/logging_middleware_test.go (2)
test/http_server_test.go (1)
  • MockLogger (15-17)
middlewares.go (1)
  • NewMiddlewareProvider (18-23)
logging_middleware.go (1)
middlewares.go (1)
  • MiddlewareProvider (14-16)
🪛 GitHub Check: Lint
test/logging_middleware_test.go

[failure] 108-108:
Error return value of c.Error is not checked (errcheck)

🪛 GitHub Actions: CI
test/logging_middleware_test.go

[error] 108-108: golangci-lint: test/logging_middleware_test.go:108:13: Error return value of c.Error is not checked (errcheck)

🔇 Additional comments (3)
.github/workflows/ci.yml (1)

21-21: Keep Go versions; 1.24 and 1.25 are official and supported
Go 1.24 was released on 11 February 2025(go.dev) and Go 1.25 on 12 August 2025(go.dev). actions/setup-go@v5 installs released Go versions from the actions/go-versions manifest (falling back to the official Go distribution when needed), so both are supported.(github.com)

error_middleware.go (1)

32-33: LGTM: returning the unexported handler

Switching to middleware.handle keeps the surface minimal without changing behavior.

go.mod (1)

9-9: Dependency bump: ezutil v2.1.0

Assuming no breaking changes in Logger interface. If there are, please confirm all call sites (including tests) compile and behave as expected.

Comment thread test/logging_middleware_test.go

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
test/logging_middleware_test.go (1)

106-109: Good fix for errcheck (c.Error) lint failure

Assigning the returned error to the blank identifier resolves golangci-lint’s errcheck.

🧹 Nitpick comments (4)
test/logging_middleware_test.go (4)

34-36: Make expectations strict: assert exactly one log per request

Guard against accidental double-logging by constraining each expected call to happen once.

-                m.On("Infof", mock.AnythingOfType("string"), mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything)
+                m.On("Infof", mock.AnythingOfType("string"), mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Once()
-                m.On("Infof", mock.AnythingOfType("string"), mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything)
+                m.On("Infof", mock.AnythingOfType("string"), mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Once()
-                m.On("Errorf", mock.AnythingOfType("string"), mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything)
+                m.On("Errorf", mock.AnythingOfType("string"), mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Once()
-                m.On("Errorf", mock.AnythingOfType("string"), mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything)
+                m.On("Errorf", mock.AnythingOfType("string"), mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Once()

Also applies to: 46-47, 57-58, 69-70


72-82: Explicitly assert no logging for OPTIONS

Strengthen the “skipped” scenario by asserting no Infof/Errorf calls occurred.

Add import:

 import (
 	"errors"
+	"net/http"
 	"net/http/httptest"
 	"testing"

Add assertions after ServeHTTP:

-			mockLogger.AssertExpectations(t)
+			if tt.method == http.MethodOptions {
+				mockLogger.AssertNotCalled(t, "Infof")
+				mockLogger.AssertNotCalled(t, "Errorf")
+			}
+			mockLogger.AssertExpectations(t)

Also applies to: 112-116


112-113: Also assert the HTTP status code

This ensures the handler behaved as intended alongside logging.

-			engine.ServeHTTP(w, ctx.Request)
+			engine.ServeHTTP(w, ctx.Request)
+			assert.Equal(t, tt.statusCode, w.Code)

94-101: Minor cleanup: build request without ctx

Not required, but you can drop CreateTestContext’s returned ctx and build req directly to reduce indirection:

Example:

router := gin.New()
router.Use(middleware)
router.Any("/*path", handler)

req := httptest.NewRequest(tt.method, url, nil)
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between b5a3a29 and 870cb13.

📒 Files selected for processing (1)
  • test/logging_middleware_test.go (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-08-27T09:32:11.080Z
Learnt from: itsLeonB
PR: itsLeonB/ginkgo#1
File: middlewares.go:39-44
Timestamp: 2025-08-27T09:32:11.080Z
Learning: In the ginkgo package, the NewAuthMiddleware function intentionally uses log.Fatalf for tokenCheckFunc validation as a fail-fast design choice, similar to the CORS middleware pattern.

Applied to files:

  • test/logging_middleware_test.go
📚 Learning: 2025-08-27T09:29:56.699Z
Learnt from: itsLeonB
PR: itsLeonB/ginkgo#1
File: middlewares.go:23-28
Timestamp: 2025-08-27T09:29:56.699Z
Learning: In the ginkgo package, the NewCorsMiddleware function intentionally uses log.Fatalf for CORS configuration validation as a fail-fast design choice rather than degrading gracefully.

Applied to files:

  • test/logging_middleware_test.go
🧬 Code graph analysis (1)
test/logging_middleware_test.go (2)
test/http_server_test.go (1)
  • MockLogger (15-17)
middlewares.go (1)
  • NewMiddlewareProvider (18-23)
🔇 Additional comments (2)
test/logging_middleware_test.go (2)

14-16: Solid test harness setup

Using gin.TestMode and a table-driven test is appropriate here.


34-70: Mock expectations correctly match middleware logging signatures. No argument-count mismatches detected.

@itsLeonB itsLeonB merged commit 0703b91 into main Sep 4, 2025
10 checks passed
@coderabbitai coderabbitai Bot mentioned this pull request Mar 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant