Skip to content

Commit 9eea9b1

Browse files
Merge pull request #9 from mittwald/feat/logging
feat: add option to log all executed requests
2 parents b89a4c5 + b5e2ce5 commit 9eea9b1

File tree

2 files changed

+80
-0
lines changed

2 files changed

+80
-0
lines changed

mittwaldv2/client_opt_logging.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package mittwaldv2
2+
3+
import (
4+
"context"
5+
"log/slog"
6+
7+
"github.com/mittwald/api-client-go/pkg/httpclient"
8+
)
9+
10+
// WithRequestLogging adds a logging middleware to the request runner chain
11+
// allowing you to log all executed HTTP requests in a slog.Logger of your
12+
// choice.
13+
//
14+
// Be mindful of the log{Request,Response}Bodies parameters; these will cause
15+
// the logger to print the full request bodies without redaction, which may
16+
// easily leak sensitive data.
17+
func WithRequestLogging(logger *slog.Logger, logRequestBodies, logResponseBodies bool) ClientOption {
18+
return func(ctx context.Context, runner httpclient.RequestRunner) (httpclient.RequestRunner, error) {
19+
return httpclient.NewLoggingClient(runner, logger, logRequestBodies, logResponseBodies), nil
20+
}
21+
}

pkg/httpclient/client_logging.go

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package httpclient
2+
3+
import (
4+
"bytes"
5+
"io"
6+
"log/slog"
7+
"net/http"
8+
)
9+
10+
type loggingClient struct {
11+
inner RequestRunner
12+
logger *slog.Logger
13+
logRequestBodies bool
14+
logResponseBodies bool
15+
}
16+
17+
func NewLoggingClient(inner RequestRunner, logger *slog.Logger, logRequestBodies, logResponseBodies bool) RequestRunner {
18+
return &loggingClient{
19+
inner: inner,
20+
logger: logger,
21+
logRequestBodies: logRequestBodies,
22+
logResponseBodies: logResponseBodies,
23+
}
24+
}
25+
26+
func (c *loggingClient) Do(request *http.Request) (*http.Response, error) {
27+
l := c.logger.With("req.method", request.Method, "req.url", request.URL.String())
28+
29+
if c.logRequestBodies && request.Body != nil {
30+
body, err := io.ReadAll(request.Body)
31+
if err != nil {
32+
return nil, err
33+
}
34+
35+
request.Body = io.NopCloser(bytes.NewBuffer(body))
36+
l = l.With("req.body", string(body))
37+
}
38+
39+
l.Debug("executing request")
40+
41+
response, err := c.inner.Do(request)
42+
43+
if response != nil {
44+
l = l.With("res.status", response.StatusCode)
45+
if c.logResponseBodies && response.Body != nil {
46+
body, err := io.ReadAll(response.Body)
47+
if err != nil {
48+
return nil, err
49+
}
50+
51+
response.Body = io.NopCloser(bytes.NewBuffer(body))
52+
l = l.With("res.body", string(body))
53+
}
54+
}
55+
56+
l.Debug("received response")
57+
58+
return response, err
59+
}

0 commit comments

Comments
 (0)