Skip to content

Commit

Permalink
Use http.RoundTripper for client side logging (#152)
Browse files Browse the repository at this point in the history
Instead of introducing custom Doer interface.
This makes it possible to configure a standard HTTP client
  • Loading branch information
raphael committed Oct 21, 2022
1 parent 49dd369 commit 5ba461a
Show file tree
Hide file tree
Showing 4 changed files with 21 additions and 23 deletions.
4 changes: 2 additions & 2 deletions example/weather/services/forecaster/cmd/forecaster/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,8 @@ func main() {
ctx = metrics.Context(ctx, genforecaster.ServiceName)

// 4. Create clients
c := &http.Client{Transport: trace.Client(ctx, http.DefaultTransport)}
wc := weathergov.New(log.Client(c))
c := &http.Client{Transport: log.Client(trace.Client(ctx, http.DefaultTransport))}
wc := weathergov.New(c)

// 5. Create service & endpoints
svc := forecaster.New(wc)
Expand Down
4 changes: 2 additions & 2 deletions example/weather/services/locator/cmd/locator/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,8 @@ func main() {
ctx = metrics.Context(ctx, genlocator.ServiceName)

// 4. Create clients
c := &http.Client{Transport: trace.Client(ctx, http.DefaultTransport)}
ipc := ipapi.New(log.Client(c))
c := &http.Client{Transport: log.Client(trace.Client(ctx, http.DefaultTransport))}
ipc := ipapi.New(c)

// 5. Create service & endpoints
svc := locator.New(ipc)
Expand Down
24 changes: 10 additions & 14 deletions log/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,6 @@ import (
)

type (
// Doer is a client that can execute HTTP requests.
Doer interface {
Do(*http.Request) (*http.Response, error)
}

// HTTPLogOption is a function that applies a configuration option to log
// HTTP middleware.
HTTPLogOption func(*httpLogOptions)
Expand All @@ -30,9 +25,10 @@ type (
httpClientOptions struct {
iserr func(int) bool
}
// client wraps an HTTP client and logs requests and responses.

// client wraps an HTTP roundtripper and logs requests and responses.
client struct {
Doer
http.RoundTripper
options *httpClientOptions
}
)
Expand Down Expand Up @@ -62,16 +58,16 @@ func HTTP(logCtx context.Context, opts ...HTTPLogOption) func(http.Handler) http
}
}

// Client returns a HTTP client that wraps the given doer and log requests and
// responses using the clue logger stored in the request context.
func Client(doer Doer, opts ...HTTPClientLogOption) Doer {
// Client wraps the given roundtripper and log requests and responses using the
// clue logger stored in the request context.
func Client(t http.RoundTripper, opts ...HTTPClientLogOption) http.RoundTripper {
options := &httpClientOptions{
iserr: func(status int) bool { return status >= 400 },
}
for _, o := range opts {
o(options)
}
return &client{Doer: doer, options: options}
return &client{RoundTripper: t, options: options}
}

// WithPathFilter adds a path filter to the HTTP middleware. Requests whose path
Expand All @@ -91,14 +87,14 @@ func WithErrorStatus(status int) HTTPClientLogOption {
}
}

// Do executes the given HTTP request and logs the request and response. The
// RoundTrip executes the given HTTP request and logs the request and response. The
// request context must be initialized with a clue logger.
func (c *client) Do(req *http.Request) (resp *http.Response, err error) {
func (c *client) RoundTrip(req *http.Request) (resp *http.Response, err error) {
msgKV := KV{K: MessageKey, V: "finished client HTTP request"}
methKV := KV{K: HTTPMethodKey, V: req.Method}
urlKV := KV{K: HTTPURLKey, V: req.URL.String()}
then := timeNow()
resp, err = c.Doer.Do(req)
resp, err = c.RoundTripper.RoundTrip(req)
if err != nil {
Error(req.Context(), err, msgKV, methKV, urlKV)
return
Expand Down
12 changes: 7 additions & 5 deletions log/http_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ func TestClient(t *testing.T) {
{"no logger", true, nil, nil, ""},
{"success", false, nil, nil, successLogs},
{"error", false, fmt.Errorf("error"), nil, errorLogs},
{"error with status", false, fmt.Errorf("error"), WithErrorStatus(200), statusLogs},
{"error with status", false, nil, WithErrorStatus(200), statusLogs},
}
now := timeNow
timeNow = func() time.Time { return time.Date(2022, time.January, 9, 20, 29, 45, 0, time.UTC) }
Expand All @@ -77,12 +77,14 @@ func TestClient(t *testing.T) {
}
server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, _ *http.Request) { rw.Write([]byte(`OK`)) }))
defer server.Close()
client := Client(server.Client())
client := server.Client()
if c.clientErr != nil {
client = Client(&errorClient{err: c.clientErr})
client.Transport = &errorClient{err: c.clientErr}
}
if c.opt != nil {
client = Client(server.Client(), c.opt)
client.Transport = Client(client.Transport, c.opt)
} else {
client.Transport = Client(client.Transport)
}

req, _ := http.NewRequest("GET", server.URL, nil)
Expand Down Expand Up @@ -120,6 +122,6 @@ type errorClient struct {
err error
}

func (c *errorClient) Do(req *http.Request) (*http.Response, error) {
func (c *errorClient) RoundTrip(req *http.Request) (*http.Response, error) {
return nil, c.err
}

0 comments on commit 5ba461a

Please sign in to comment.