@@ -3,15 +3,15 @@ package util
33import (
44 "bytes"
55 "encoding/json"
6+ "io"
67 "log/slog"
78 "net"
89 "net/http"
910 "net/http/httptest"
11+ "os"
1012 "strings"
1113 "testing"
1214 "time"
13-
14- "github.com/felixge/httpsnoop"
1515)
1616
1717func TestLogHTTPReqInfo (t * testing.T ) {
@@ -60,7 +60,7 @@ func TestLogHTTPReqInfo(t *testing.T) {
6060 }
6161 }
6262
63- // Check that duration field exists and is a number (slog duration format in nanoseconds )
63+ // Check that duration field exists and is a number (milliseconds )
6464 if duration , ok := logData ["duration" ]; ! ok {
6565 t .Errorf ("Expected log to contain 'duration' field, got: %s" , logged )
6666 } else if _ , ok := duration .(float64 ); ! ok {
@@ -135,37 +135,15 @@ func TestLogRequestHandler(t *testing.T) {
135135 // Capture log output to a buffer instead of stdout
136136 var buf bytes.Buffer
137137
138- // Create the logging handler with a custom logger that writes to our buffer
139138 dummyHandler := http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
140139 w .WriteHeader (http .StatusOK )
141140 w .Write ([]byte ("test response" ))
142141 })
143142
144- // Create a custom version of LogRequestHandler that uses our buffer for logging
145- var logger * slog.Logger
146- if tt .pretty {
147- logger = slog .New (slog .NewTextHandler (& buf , nil ))
148- } else {
149- logger = slog .New (slog .NewJSONHandler (& buf , nil ))
150- }
151-
152- // Create a modified version of LogRequestHandler that uses our logger
153- fn := func (w http.ResponseWriter , r * http.Request ) {
154- // runs handler and captures information about HTTP request
155- mtr := httpsnoop .CaptureMetrics (dummyHandler , w , r )
156-
157- logHTTPReqInfo (logger , & HTTPReqInfo {
158- method : r .Method ,
159- path : r .URL .String (),
160- code : mtr .Code ,
161- size : mtr .Written ,
162- duration : mtr .Duration ,
163- ipAddress : requestGetRemoteAddress (r ),
164- userAgent : r .Header .Get ("User-Agent" ),
165- referer : r .Header .Get ("Referer" ),
166- })
167- }
168- handler := http .HandlerFunc (fn )
143+ handler := LogRequestHandler (dummyHandler , & LogRequestHandlerOptions {
144+ Pretty : tt .pretty ,
145+ Writer : & buf ,
146+ })
169147
170148 // Create test request
171149 req := httptest .NewRequest (tt .method , tt .path , nil )
@@ -271,7 +249,6 @@ func TestLogRequestHandlerWithDifferentStatusCodes(t *testing.T) {
271249 t .Run (tt .name , func (t * testing.T ) {
272250 // Capture log output
273251 var buf bytes.Buffer
274- logger := slog .New (slog .NewJSONHandler (& buf , nil ))
275252
276253 // Create a dummy handler that returns the specified status code
277254 dummyHandler := http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
@@ -281,22 +258,9 @@ func TestLogRequestHandlerWithDifferentStatusCodes(t *testing.T) {
281258 }
282259 })
283260
284- // Create a custom handler that uses our logger
285- fn := func (w http.ResponseWriter , r * http.Request ) {
286- mtr := httpsnoop .CaptureMetrics (dummyHandler , w , r )
287-
288- logHTTPReqInfo (logger , & HTTPReqInfo {
289- method : r .Method ,
290- path : r .URL .String (),
291- code : mtr .Code ,
292- size : mtr .Written ,
293- duration : mtr .Duration ,
294- ipAddress : requestGetRemoteAddress (r ),
295- userAgent : r .Header .Get ("User-Agent" ),
296- referer : r .Header .Get ("Referer" ),
297- })
298- }
299- handler := http .HandlerFunc (fn )
261+ handler := LogRequestHandler (dummyHandler , & LogRequestHandlerOptions {
262+ Writer : & buf ,
263+ })
300264
301265 // Create test request
302266 req := httptest .NewRequest ("GET" , "/test" , nil )
@@ -338,29 +302,16 @@ func TestLogRequestHandlerWithDifferentStatusCodes(t *testing.T) {
338302func TestLogRequestHandlerPrettyLogging (t * testing.T ) {
339303 // Test that the pretty option works without errors
340304 var buf bytes.Buffer
341- logger := slog .New (slog .NewTextHandler (& buf , nil ))
342305
343306 dummyHandler := http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
344307 w .WriteHeader (http .StatusOK )
345308 w .Write ([]byte ("ok" ))
346309 })
347310
348- // Create a custom handler with pretty logging
349- fn := func (w http.ResponseWriter , r * http.Request ) {
350- mtr := httpsnoop .CaptureMetrics (dummyHandler , w , r )
351-
352- logHTTPReqInfo (logger , & HTTPReqInfo {
353- method : r .Method ,
354- path : r .URL .String (),
355- code : mtr .Code ,
356- size : mtr .Written ,
357- duration : mtr .Duration ,
358- ipAddress : requestGetRemoteAddress (r ),
359- userAgent : r .Header .Get ("User-Agent" ),
360- referer : r .Header .Get ("Referer" ),
361- })
362- }
363- handler := http .HandlerFunc (fn )
311+ handler := LogRequestHandler (dummyHandler , & LogRequestHandlerOptions {
312+ Pretty : true ,
313+ Writer : & buf ,
314+ })
364315
365316 req := httptest .NewRequest ("GET" , "/" , nil )
366317 req .RemoteAddr = "127.0.0.1:8080"
@@ -390,3 +341,35 @@ func TestLogRequestHandlerPrettyLogging(t *testing.T) {
390341 t .Errorf ("Expected log to contain message 'HTTP Request', got: %s" , logged )
391342 }
392343}
344+
345+ func TestLogRequestHandlerNilOptions (t * testing.T ) {
346+ // Ensure nil options do not panic and still log to stdout.
347+ dummyHandler := http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
348+ w .WriteHeader (http .StatusOK )
349+ })
350+
351+ origStdout := os .Stdout
352+ r , w , err := os .Pipe ()
353+ if err != nil {
354+ t .Fatalf ("failed to create pipe: %v" , err )
355+ }
356+ os .Stdout = w
357+ defer func () {
358+ os .Stdout = origStdout
359+ }()
360+
361+ handler := LogRequestHandler (dummyHandler , nil )
362+ req := httptest .NewRequest ("GET" , "/nil-options" , nil )
363+ req .RemoteAddr = "127.0.0.1:8080"
364+ wr := httptest .NewRecorder ()
365+ handler .ServeHTTP (wr , req )
366+
367+ w .Close ()
368+ out , err := io .ReadAll (r )
369+ if err != nil {
370+ t .Fatalf ("failed to read stdout: %v" , err )
371+ }
372+ if len (out ) == 0 {
373+ t .Errorf ("expected log output, got empty string" )
374+ }
375+ }
0 commit comments