From 60979b12eb53d9700add5992022efde51e17d1a5 Mon Sep 17 00:00:00 2001 From: pugnack Date: Fri, 17 Oct 2025 12:56:06 +0500 Subject: [PATCH] [v4] improve logs (#165) * improve logs --- client_helpers.go | 36 +++++++++++++++++++++++++++++---- client_helpers_test.go | 46 ++++++++++++++++++++++++++++++++++++++++++ client_unary_call.go | 28 +++++++++++++++++++------ 3 files changed, 100 insertions(+), 10 deletions(-) diff --git a/client_helpers.go b/client_helpers.go index 0f8c3e8..f616e15 100644 --- a/client_helpers.go +++ b/client_helpers.go @@ -119,10 +119,23 @@ func buildHTTPRequest( } if log.V(logger.DebugLevel) { - log.Debug( - ctx, - fmt.Sprintf("request %s to %s with headers %v body %s", method, u.String(), hreq.Header, body), - ) + if shouldLogBody(ct) { + log.Debug( + ctx, + fmt.Sprintf( + "micro.client http request: method=%s url=%s headers=%v body=%s", + method, u.String(), hreq.Header, body, + ), + ) + } else { + log.Debug( + ctx, + fmt.Sprintf( + "micro.client http request: method=%s url=%s headers=%v", + method, u.String(), hreq.Header, + ), + ) + } } return hreq, nil @@ -261,3 +274,18 @@ func validateHeadersAndCookies(r *http.Request, parameters map[string]map[string return nil } + +func shouldLogBody(contentType string) bool { + ct := strings.ToLower(strings.Split(contentType, ";")[0]) + switch { + case strings.HasPrefix(ct, "text/"): // => text/html, text/plain, text/csv etc. + return true + case ct == "application/json", + ct == "application/xml", + ct == "application/x-www-form-urlencoded", + ct == "application/yaml": + return true + default: + return false + } +} diff --git a/client_helpers_test.go b/client_helpers_test.go index 8ae7c90..bf95984 100644 --- a/client_helpers_test.go +++ b/client_helpers_test.go @@ -399,3 +399,49 @@ func TestValidateHeadersAndCookies(t *testing.T) { }) } } + +func TestShouldLogBody(t *testing.T) { + tests := []struct { + name string + contentType string + want bool + }{ + // --- text/* + {"plain text", "text/plain", true}, + {"html", "text/html", true}, + {"csv", "text/csv", true}, + {"yaml text", "text/yaml", true}, + + // --- application/* + {"json", "application/json", true}, + {"xml", "application/xml", true}, + {"form-urlencoded", "application/x-www-form-urlencoded", true}, + {"yaml", "application/yaml", true}, + + // --- with parameters + {"json with charset", "application/json; charset=utf-8", true}, + {"binary with charset", "application/octet-stream; charset=utf-8", false}, + + // --- binary + {"multipart form", "multipart/form-data", false}, + {"binary stream", "application/octet-stream", false}, + {"pdf", "application/pdf", false}, + {"protobuf", "application/protobuf", false}, + {"image", "image/png", false}, + + // --- edge cases + {"upper case type", "APPLICATION/JSON", true}, + {"mixed case type", "Text/HTML", true}, + {"unknown text prefix", "TEXT/FOO", true}, + {"weird semicolon only", ";", false}, + {"spaces only", " ", false}, + {"empty content-type", "", false}, + {"missing main type", "/plain", false}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + require.Equal(t, tt.want, shouldLogBody(tt.contentType)) + }) + } +} diff --git a/client_unary_call.go b/client_unary_call.go index 2e79855..de3391a 100644 --- a/client_unary_call.go +++ b/client_unary_call.go @@ -207,8 +207,6 @@ func (c *Client) parseRsp(ctx context.Context, hrsp *http.Response, rsp any, opt default: } - var buf []byte - if opts.ResponseMetadata != nil { for k, v := range hrsp.Header { opts.ResponseMetadata.Append(k, v...) @@ -224,6 +222,8 @@ func (c *Client) parseRsp(ctx context.Context, hrsp *http.Response, rsp any, opt ct = htype } + var buf []byte + if hrsp.Body != nil { var err error buf, err = io.ReadAll(hrsp.Body) @@ -232,15 +232,31 @@ func (c *Client) parseRsp(ctx context.Context, hrsp *http.Response, rsp any, opt } } + if log.V(logger.DebugLevel) { + if shouldLogBody(ct) { + log.Debug( + ctx, + fmt.Sprintf( + "micro.client http response: status=%s headers=%v body=%s", + hrsp.Status, hrsp.Header, buf, + ), + ) + } else { + log.Debug( + ctx, + fmt.Sprintf( + "micro.client http response: status=%s headers=%v", + hrsp.Status, hrsp.Header, + ), + ) + } + } + cf, err := c.newCodec(ct) if err != nil { return errors.InternalServerError("go.micro.client", "unknown content-type %s: %v", ct, err) } - if log.V(logger.DebugLevel) { - log.Debug(ctx, fmt.Sprintf("response with headers: %v and body: %s", hrsp.Header, buf)) - } - if hrsp.StatusCode < http.StatusBadRequest { if err = cf.Unmarshal(buf, rsp); err != nil { return errors.InternalServerError("go.micro.client", "unmarshal response: %v", err)