Skip to content

Commit 5f7f283

Browse files
fix-formatting-remote-debug-log2
Fix formatting of remote debug logs
2 parents 77e61db + 074cc71 commit 5f7f283

File tree

2 files changed

+100
-9
lines changed

2 files changed

+100
-9
lines changed

pkg/gofr/logging/remotelogger/dynamic_level_logger.go

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,34 @@ const (
2121
colorRed = 202 // For server errors (5xx)
2222
)
2323

24+
// httpDebugMsg represents a structured HTTP debug log entry.
25+
// It implements PrettyPrint for colored output and json.Marshaler for JSON logs.
26+
type httpDebugMsg struct {
27+
CorrelationID string `json:"correlation_id"`
28+
ResponseCode int `json:"response_code"`
29+
ResponseTime int64 `json:"response_time_us"`
30+
HTTPMethod string `json:"http_method"`
31+
URI string `json:"uri"`
32+
}
33+
34+
func (m httpDebugMsg) PrettyPrint(w io.Writer) {
35+
colorCode := colorForResponseCode(m.ResponseCode)
36+
fmt.Fprintf(w,
37+
"\u001B[38;5;8m%s \u001B[38;5;%dm%-6d\u001B[0m %8dμs\u001B[0m %s %s\n",
38+
m.CorrelationID,
39+
colorCode,
40+
m.ResponseCode,
41+
m.ResponseTime,
42+
m.HTTPMethod,
43+
m.URI,
44+
)
45+
}
46+
47+
func (m httpDebugMsg) MarshalJSON() ([]byte, error) {
48+
type alias httpDebugMsg
49+
return json.Marshal(alias(m))
50+
}
51+
2452
// httpLogFilter filters HTTP logs from remote logger to reduce noise.
2553
type httpLogFilter struct {
2654
logging.Logger
@@ -77,16 +105,14 @@ func (f *httpLogFilter) handleHTTPLog(httpLog *service.Log, args []any) {
77105

78106
// Subsequent successful hits - log at DEBUG level with consistent format
79107
case isSuccessful:
80-
if debugLogger, ok := f.Logger.(interface{ Debugf(string, ...any) }); ok {
81-
colorCode := colorForResponseCode(httpLog.ResponseCode)
82-
debugLogger.Debugf("\u001B[38;5;8m%s \u001B[38;5;%dm%-6d\u001B[0m %8d\u001B[38;5;8mµs\u001B[0m %s %s",
83-
httpLog.CorrelationID,
84-
colorCode,
85-
httpLog.ResponseCode,
86-
httpLog.ResponseTime,
87-
httpLog.HTTPMethod,
88-
httpLog.URI)
108+
msg := httpDebugMsg{
109+
CorrelationID: httpLog.CorrelationID,
110+
ResponseCode: httpLog.ResponseCode,
111+
ResponseTime: httpLog.ResponseTime,
112+
HTTPMethod: httpLog.HTTPMethod,
113+
URI: httpLog.URI,
89114
}
115+
f.Logger.Debug(msg)
90116

91117
// Error responses - pass through to original logger
92118
default:

pkg/gofr/logging/remotelogger/dynamic_level_logger_test.go

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package remotelogger
22

33
import (
4+
"bytes"
45
"fmt"
56
"net/http"
67
"net/http/httptest"
@@ -357,3 +358,67 @@ func TestLogLevelChangeToFatal_NoExit(t *testing.T) {
357358
// Verify the log contains a warning about the level change
358359
assert.Contains(t, log, "LOG_LEVEL updated from INFO to FATAL")
359360
}
361+
362+
func TestHTTPDebugMsg_PrettyPrint(t *testing.T) {
363+
cases := []struct {
364+
name string
365+
msg httpDebugMsg
366+
wantColorSeq string
367+
}{
368+
{
369+
name: "2xx uses blue",
370+
msg: httpDebugMsg{
371+
CorrelationID: "corr-200",
372+
ResponseCode: 200,
373+
ResponseTime: 123,
374+
HTTPMethod: "GET",
375+
URI: "/ok",
376+
},
377+
wantColorSeq: fmt.Sprintf("\u001B[38;5;%dm", colorBlue),
378+
},
379+
{
380+
name: "4xx uses yellow",
381+
msg: httpDebugMsg{
382+
CorrelationID: "corr-404",
383+
ResponseCode: 404,
384+
ResponseTime: 456,
385+
HTTPMethod: "POST",
386+
URI: "/not-found",
387+
},
388+
wantColorSeq: fmt.Sprintf("\u001B[38;5;%dm", colorYellow),
389+
},
390+
{
391+
name: "5xx uses red",
392+
msg: httpDebugMsg{
393+
CorrelationID: "corr-500",
394+
ResponseCode: 500,
395+
ResponseTime: 789,
396+
HTTPMethod: "PUT",
397+
URI: "/err",
398+
},
399+
wantColorSeq: fmt.Sprintf("\u001B[38;5;%dm", colorRed),
400+
},
401+
}
402+
403+
for _, tc := range cases {
404+
t.Run(tc.name, func(t *testing.T) {
405+
var buf bytes.Buffer
406+
tc.msg.PrettyPrint(&buf)
407+
out := buf.String()
408+
409+
// basic content checks
410+
assert.Contains(t, out, tc.msg.CorrelationID)
411+
assert.Contains(t, out, tc.msg.HTTPMethod)
412+
assert.Contains(t, out, tc.msg.URI)
413+
assert.Contains(t, out, fmt.Sprintf("%d", tc.msg.ResponseCode))
414+
// response time should include the microsecond suffix
415+
assert.Contains(t, out, fmt.Sprintf("%dμs", tc.msg.ResponseTime))
416+
417+
// color sequence must be present
418+
assert.Contains(t, out, tc.wantColorSeq, "expected color sequence %q in output: %q", tc.wantColorSeq, out)
419+
420+
// ensure reset code present
421+
assert.Contains(t, out, "\u001B[0m")
422+
})
423+
}
424+
}

0 commit comments

Comments
 (0)