From 412d3442fa858f79f7382944988bd0064baa9456 Mon Sep 17 00:00:00 2001 From: Luis Pater Date: Mon, 25 May 2026 20:44:32 +0800 Subject: [PATCH] feat(logging): add `RequestID` support in home request logging - Included `RequestID` field in `homeRequestLogPayload` for better log categorization. - Updated `forwardRequestLogToHome` and related components to handle `RequestID`. - Added new test cases to validate `RequestID` propagation in streaming requests. --- internal/logging/request_logger.go | 11 ++-- internal/logging/request_logger_home_test.go | 57 ++++++++++++++++++++ 2 files changed, 65 insertions(+), 3 deletions(-) diff --git a/internal/logging/request_logger.go b/internal/logging/request_logger.go index 44b2c9526..26b2f42b3 100644 --- a/internal/logging/request_logger.go +++ b/internal/logging/request_logger.go @@ -166,6 +166,7 @@ type FileRequestLogger struct { type homeRequestLogPayload struct { Headers map[string][]string `json:"headers,omitempty"` + RequestID string `json:"request_id,omitempty"` RequestLog string `json:"request_log,omitempty"` } @@ -192,7 +193,7 @@ func cloneHeaders(headers map[string][]string) map[string][]string { return out } -func (l *FileRequestLogger) forwardRequestLogToHome(ctx context.Context, headers map[string][]string, logText string) error { +func (l *FileRequestLogger) forwardRequestLogToHome(ctx context.Context, headers map[string][]string, requestID string, logText string) error { if l == nil || !l.homeEnabled { return nil } @@ -202,6 +203,7 @@ func (l *FileRequestLogger) forwardRequestLogToHome(ctx context.Context, headers } payload := homeRequestLogPayload{ Headers: cloneHeaders(headers), + RequestID: strings.TrimSpace(requestID), RequestLog: logText, } raw, errMarshal := json.Marshal(&payload) @@ -334,7 +336,7 @@ func (l *FileRequestLogger) logRequest(url, method string, requestHeaders map[st if writeErr != nil { return fmt.Errorf("failed to build request log content: %w", writeErr) } - return l.forwardRequestLogToHome(context.Background(), requestHeaders, buf.String()) + return l.forwardRequestLogToHome(context.Background(), requestHeaders, requestID, buf.String()) } // Ensure logs directory exists @@ -1631,11 +1633,12 @@ type homeStreamingLogWriter struct { apiRequest []byte apiResponse []byte apiWebsocketTime []byte + requestID string apiResponseTS time.Time firstChunkTS time.Time } -func newHomeStreamingLogWriter(url, method string, headers map[string][]string, body []byte, _ string) *homeStreamingLogWriter { +func newHomeStreamingLogWriter(url, method string, headers map[string][]string, body []byte, requestID string) *homeStreamingLogWriter { requestHeaders := make(map[string][]string, len(headers)) for key, values := range headers { headerValues := make([]string, len(values)) @@ -1649,6 +1652,7 @@ func newHomeStreamingLogWriter(url, method string, headers map[string][]string, timestamp: time.Now(), requestHeaders: requestHeaders, requestBody: append([]byte(nil), body...), + requestID: strings.TrimSpace(requestID), chunkChan: make(chan []byte, 100), doneChan: make(chan struct{}), } @@ -1766,6 +1770,7 @@ func (w *homeStreamingLogWriter) Close() error { payload := homeRequestLogPayload{ Headers: cloneHeaders(w.requestHeaders), + RequestID: w.requestID, RequestLog: buf.String(), } raw, errMarshal := json.Marshal(&payload) diff --git a/internal/logging/request_logger_home_test.go b/internal/logging/request_logger_home_test.go index f8cdf1e45..4f66cacec 100644 --- a/internal/logging/request_logger_home_test.go +++ b/internal/logging/request_logger_home_test.go @@ -77,6 +77,7 @@ func TestFileRequestLogger_HomeEnabled_ForwardsWhenRequestLogEnabled(t *testing. var got struct { Headers map[string][]string `json:"headers"` + RequestID string `json:"request_id"` RequestLog string `json:"request_log"` } if errUnmarshal := json.Unmarshal(stub.pushed[0], &got); errUnmarshal != nil { @@ -88,6 +89,62 @@ func TestFileRequestLogger_HomeEnabled_ForwardsWhenRequestLogEnabled(t *testing. if got.Headers == nil || got.Headers["Authorization"][0] != "Bearer secret" { t.Fatalf("headers.authorization = %+v, want Bearer secret", got.Headers["Authorization"]) } + if got.RequestID != "req-1" { + t.Fatalf("request_id = %q, want req-1", got.RequestID) + } + if got.RequestLog == "" { + t.Fatalf("request_log empty, want non-empty") + } +} + +func TestFileRequestLogger_HomeEnabled_ForwardsStreamingRequestID(t *testing.T) { + original := currentHomeRequestLogClient + defer func() { + currentHomeRequestLogClient = original + }() + + stub := &stubHomeRequestLogClient{heartbeatOK: true} + currentHomeRequestLogClient = func() homeRequestLogClient { + return stub + } + + logsDir := t.TempDir() + logger := NewFileRequestLogger(true, logsDir, "", 0) + logger.SetHomeEnabled(true) + + writer, errLog := logger.LogStreamingRequest( + "/v1/responses", + http.MethodPost, + map[string][]string{"Content-Type": {"application/json"}}, + []byte(`{"input":"hello"}`), + "stream-req-1", + ) + if errLog != nil { + t.Fatalf("LogStreamingRequest error: %v", errLog) + } + + if errStatus := writer.WriteStatus(http.StatusOK, map[string][]string{"Content-Type": {"text/event-stream"}}); errStatus != nil { + t.Fatalf("WriteStatus error: %v", errStatus) + } + writer.WriteChunkAsync([]byte("data: ok\n\n")) + if errClose := writer.Close(); errClose != nil { + t.Fatalf("Close error: %v", errClose) + } + + if len(stub.pushed) != 1 { + t.Fatalf("home pushed records = %d, want 1", len(stub.pushed)) + } + + var got struct { + RequestID string `json:"request_id"` + RequestLog string `json:"request_log"` + } + if errUnmarshal := json.Unmarshal(stub.pushed[0], &got); errUnmarshal != nil { + t.Fatalf("unmarshal payload: %v payload=%s", errUnmarshal, string(stub.pushed[0])) + } + if got.RequestID != "stream-req-1" { + t.Fatalf("request_id = %q, want stream-req-1", got.RequestID) + } if got.RequestLog == "" { t.Fatalf("request_log empty, want non-empty") }