-
Notifications
You must be signed in to change notification settings - Fork 4
feat(reqlog): added a middleware to logging requests #45
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Reviewer's Guide by SourceryThis pull request introduces a new request logging middleware 'reqlog' along with comprehensive documentation and tests. The changes implement a middleware that logs each incoming request using configurable formatting options, enhance the response writer to track body bytes sent, and add tests to ensure correct behavior. Sequence diagram for logging middleware request flowsequenceDiagram
actor Client
participant Middleware as RequestLogMiddleware
participant Handler as NextHandler
participant Logger as LogInstance
Client->>Middleware: HTTP Request starts
Middleware->>Handler: Invoke next(c)
Handler-->>Middleware: Return response
Middleware->>Logger: Log request details (Combined format)
Middleware->>Client: Send Response
Class diagram for request logging middlewareclassDiagram
%% Define the Options structure
class Options {
+*log.Logger Logger
+func(*xun.Context) string GetVisitor
+func(*xun.Context) string GetUser
+Format Format
}
%% Define reqlog middleware and functions
class ReqlogMiddleware {
+New(...Option) xun.Middleware
}
%% Option functions
class WithLogger {
+WithLogger(l *log.Logger) Option
}
class WithVisitor {
+WithVisitor(func(c *xun.Context) string) Option
}
class WithUser {
+WithUser(func(c *xun.Context) string) Option
}
class WithFormat {
+WithFormat(f Format) Option
}
%% Format functions
class Combined {
+Combined(c *xun.Context, options *Options, starts time.Time)
}
class Common {
+Common(c *xun.Context, options *Options, starts time.Time)
}
%% Relationships
ReqlogMiddleware ..> Options : uses
Options <|-- WithLogger
Options <|-- WithVisitor
Options <|-- WithUser
Options <|-- WithFormat
ReqlogMiddleware ..> Combined : logs using
ReqlogMiddleware ..> Common : alternative format
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey @cnlangzi - I've reviewed your changes - here's some feedback:
Overall Comments:
- Consider adding a benchmark to evaluate the performance impact of the logging middleware.
- It might be helpful to provide an example of how to use the middleware with a custom log format.
Here's what I looked at during the review
- 🟡 General issues: 2 issues found
- 🟢 Security: all looks good
- 🟡 Testing: 1 issue found
- 🟢 Complexity: all looks good
- 🟢 Documentation: all looks good
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
// Combined log request with Combined Log Format (XLF/ELF) | ||
func Combined(c *xun.Context, options *Options, starts time.Time) { | ||
requestLine := fmt.Sprintf(`"%s %s %s"`, c.Request.Method, c.Request.URL.Path, c.Request.Proto) | ||
host, _, _ := net.SplitHostPort(c.Request.RemoteAddr) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion (bug_risk): Consider error handling in host extraction.
While it's likely that RemoteAddr is well-formed, ignoring the error from net.SplitHostPort might lead to unexpected values in logs. A brief comment or error handling may improve robustness.
host, _, _ := net.SplitHostPort(c.Request.RemoteAddr) | |
host, _, err := net.SplitHostPort(c.Request.RemoteAddr) | |
if err != nil { | |
// fallback to full remote address if unable to split host and port | |
host = c.Request.RemoteAddr | |
} |
@@ -0,0 +1,86 @@ | |||
package reqlog |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion (testing): Test cases for error scenarios are missing.
It would be beneficial to add tests to cover cases where the logger fails to write, or when the request context is missing expected values. This ensures the middleware handles errors gracefully and doesn't cause unexpected behavior.
Suggested implementation:
import (
"bytes"
"errors"
"io"
"log"
"net/http"
"net/http/httptest"
"strings"
"testing"
"github.com/stretchr/testify/require"
"github.com/yaitoo/xun"
)
type errorWriter struct{}
func (ew errorWriter) Write(p []byte) (int, error) {
return 0, errors.New("write error")
}
func TestLoggerWriteError(t *testing.T) {
// Create a logger that uses a writer which always returns an error.
ew := errorWriter{}
logger := log.New(ew, "", 0)
req := httptest.NewRequest("GET", "http://example.com", nil)
recorder := httptest.NewRecorder()
// Dummy handler to simulate the next middleware/handler.
next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
})
// Assuming RequestLoggingHandler is our middleware function that accepts a logger.
handler := RequestLoggingHandler(next, logger)
handler.ServeHTTP(recorder, req)
// Even if the logger write fails, the middleware should allow the request to complete.
require.Equal(t, http.StatusOK, recorder.Code)
}
func TestMissingExpectedRequestContext(t *testing.T) {
// Create a request without the expected context values.
req := httptest.NewRequest("GET", "http://example.com", nil)
recorder := httptest.NewRecorder()
// Dummy handler for the next stage.
next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
})
// Using the standard logger.
handler := RequestLoggingHandler(next, log.Default())
handler.ServeHTTP(recorder, req)
// The middleware should handle the missing context gracefully and proceed.
require.Equal(t, http.StatusOK, recorder.Code)
}
Make sure that the middleware function RequestLoggingHandler(next http.Handler, logger *log.Logger) is defined in your codebase so that these tests can be compiled and executed. You might need to adjust the function signature or its usage to match your project's actual implementation details.
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #45 +/- ##
==========================================
+ Coverage 92.47% 92.69% +0.21%
==========================================
Files 43 46 +3
Lines 1675 1766 +91
==========================================
+ Hits 1549 1637 +88
- Misses 93 96 +3
Partials 33 33
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. |
Here's the code health analysis summary for commits Analysis Summary
|
Changed
Fixed
Added
Tests
Tasks to complete before merging PR:
make unit-test
to check for any regressions 📋make lint
to check for any issuesSummary by Sourcery
Add request logging middleware to log incoming requests, and instructions for using GoAccess to generate and serve real-time analysis reports.
New Features:
reqlog
middleware to log incoming requests with customizable format (default: Combined Log Format).Tests:
reqlog
middleware.