Skip to content

Commit 765eb3d

Browse files
authored
chore: extend network trace level debug logs to include request and response bodies (#332)
1 parent 4e8fc50 commit 765eb3d

File tree

4 files changed

+417
-91
lines changed

4 files changed

+417
-91
lines changed

pkg/networking/logging.go

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
package networking
2+
3+
import (
4+
"bytes"
5+
"compress/gzip"
6+
"fmt"
7+
"io"
8+
"net/http"
9+
10+
"github.com/pkg/errors"
11+
"github.com/rs/zerolog"
12+
)
13+
14+
const defaultNetworkLogLevel = zerolog.DebugLevel
15+
const extendedNetworkLogLevel = zerolog.TraceLevel
16+
17+
func shouldNotLog(currentLevel zerolog.Level, levelToLogAt zerolog.Level) bool {
18+
// Don't log if logger level is above the threshold
19+
return currentLevel > levelToLogAt
20+
}
21+
22+
func getResponseBody(response *http.Response) io.ReadCloser {
23+
if response.Body != nil {
24+
bodyBytes, bodyErr := io.ReadAll(response.Body)
25+
if bodyErr == nil {
26+
response.Body.Close()
27+
bodyReader := io.NopCloser(bytes.NewBuffer(bodyBytes))
28+
response.Body = io.NopCloser(bytes.NewBuffer(bodyBytes))
29+
return bodyReader
30+
}
31+
}
32+
return nil
33+
}
34+
35+
func getRequestBody(request *http.Request) io.ReadCloser {
36+
if request.GetBody != nil {
37+
bodyReader, bodyErr := request.GetBody()
38+
if bodyErr != nil {
39+
return nil
40+
}
41+
return bodyReader
42+
}
43+
44+
if request.Body != nil {
45+
bodyBytes, bodyErr := io.ReadAll(request.Body)
46+
if bodyErr == nil {
47+
request.Body.Close()
48+
bodyReader := io.NopCloser(bytes.NewBuffer(bodyBytes))
49+
request.Body = io.NopCloser(bytes.NewBuffer(bodyBytes))
50+
return bodyReader
51+
}
52+
}
53+
54+
return nil
55+
}
56+
57+
func decodeBody(bodyBytes []byte, contentEncoding string) (string, error) {
58+
if contentEncoding == "gzip" {
59+
reader, err := gzip.NewReader(bytes.NewReader(bodyBytes))
60+
if err != nil {
61+
return "", errors.Wrap(err, "failed to create gzip reader")
62+
}
63+
defer reader.Close()
64+
decodedBytes, err := io.ReadAll(reader)
65+
if err != nil {
66+
return "", errors.Wrap(err, "failed to read decoded body")
67+
}
68+
return string(decodedBytes), nil
69+
} else {
70+
return string(bodyBytes), nil
71+
}
72+
}
73+
74+
func logBody(logger *zerolog.Logger, logLevel zerolog.Level, logPrefix string, body io.ReadCloser, header http.Header) {
75+
if body != nil {
76+
bodyBytes, bodyErr := io.ReadAll(body)
77+
defer func() {
78+
closeErr := body.Close()
79+
if closeErr != nil {
80+
logger.WithLevel(logLevel).Err(closeErr).Msg("failed to close body")
81+
}
82+
}()
83+
84+
if bodyErr != nil {
85+
return
86+
}
87+
88+
bodyString, err := decodeBody(bodyBytes, header.Get("Content-Encoding"))
89+
if err != nil {
90+
logger.WithLevel(logLevel).Err(err).Msgf("%s Failed to decode request body", logPrefix)
91+
} else if len(bodyString) > 0 {
92+
logger.WithLevel(logLevel).Msgf("%s body: %v", logPrefix, bodyString)
93+
}
94+
}
95+
}
96+
97+
func LogRequest(r *http.Request, logger *zerolog.Logger) {
98+
if shouldNotLog(logger.GetLevel(), defaultNetworkLogLevel) { // Don't log if logger level is above the threshold
99+
return
100+
}
101+
102+
logPrefixRequest := fmt.Sprintf("> request [%p]:", r)
103+
logger.WithLevel(defaultNetworkLogLevel).Msgf("%s %s %s", logPrefixRequest, r.Method, r.URL.String())
104+
logger.WithLevel(defaultNetworkLogLevel).Msgf("%s header: %v", logPrefixRequest, r.Header)
105+
106+
// additional logs for trace level logging
107+
if shouldNotLog(logger.GetLevel(), extendedNetworkLogLevel) {
108+
return
109+
}
110+
111+
logBody(logger, defaultNetworkLogLevel, logPrefixRequest, getRequestBody(r), r.Header)
112+
}
113+
114+
func LogResponse(response *http.Response, logger *zerolog.Logger) {
115+
if shouldNotLog(logger.GetLevel(), defaultNetworkLogLevel) { // Don't log if logger level is above the threshold
116+
return
117+
}
118+
119+
if response != nil {
120+
logPrefixResponse := fmt.Sprintf("< response [%p]:", response.Request)
121+
logger.WithLevel(defaultNetworkLogLevel).Msgf("%s %s", logPrefixResponse, response.Status)
122+
logger.WithLevel(defaultNetworkLogLevel).Msgf("%s header: %v", logPrefixResponse, response.Header)
123+
124+
// additional logs for trace level logging and error responses
125+
if !(response.StatusCode >= 400 || !shouldNotLog(logger.GetLevel(), extendedNetworkLogLevel)) {
126+
return
127+
}
128+
129+
logBody(logger, defaultNetworkLogLevel, logPrefixResponse, getResponseBody(response), response.Header)
130+
}
131+
}

0 commit comments

Comments
 (0)