Skip to content

Commit e8a9a8e

Browse files
authored
feat(core): move request logging from client to a transport (#1482)
1 parent b55a21c commit e8a9a8e

File tree

4 files changed

+88
-46
lines changed

4 files changed

+88
-46
lines changed

internal/auth/token.go

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,18 @@ func (t *Token) Headers() http.Header {
2525
return headers
2626
}
2727

28+
func AnonymizeTokenHeaders(headers http.Header) http.Header {
29+
key := headers.Get(XAuthTokenHeader)
30+
if key != "" {
31+
headers.Set(XAuthTokenHeader, HideSecretKey(key))
32+
}
33+
return headers
34+
}
35+
2836
// AnonymizedHeaders returns an anonymized version of Headers()
2937
// This method could be use for logging purpose.
3038
func (t *Token) AnonymizedHeaders() http.Header {
31-
headers := http.Header{}
32-
headers.Set(XAuthTokenHeader, HideSecretKey(t.SecretKey))
33-
return headers
39+
return AnonymizeTokenHeaders(t.Headers())
3440
}
3541

3642
func HideSecretKey(k string) string {

scw/client.go

Lines changed: 14 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,10 @@ import (
88
"math"
99
"net"
1010
"net/http"
11-
"net/http/httputil"
1211
"reflect"
1312
"strconv"
1413
"strings"
1514
"sync"
16-
"sync/atomic"
1715
"time"
1816

1917
"github.com/scaleway/scaleway-sdk-go/internal/auth"
@@ -73,6 +71,11 @@ func NewClient(opts ...ClientOption) (*Client, error) {
7371
setInsecureMode(s.httpClient)
7472
}
7573

74+
if logger.ShouldLog(logger.LogLevelDebug) {
75+
logger.Debugf("client: using request logger")
76+
setRequestLogging(s.httpClient)
77+
}
78+
7679
logger.Debugf("client: using sdk version " + version)
7780

7881
return &Client{
@@ -182,13 +185,8 @@ func (c *Client) Do(req *ScalewayRequest, res interface{}, opts ...RequestOption
182185
return c.do(req, res)
183186
}
184187

185-
// requestNumber auto increments on each do().
186-
// This allows easy distinguishing of concurrently performed requests in log.
187-
var requestNumber uint32
188-
189188
// do performs a single HTTP request based on the ScalewayRequest object.
190189
func (c *Client) do(req *ScalewayRequest, res interface{}) (sdkErr error) {
191-
currentRequestNumber := atomic.AddUint32(&requestNumber, 1)
192190

193191
if req == nil {
194192
return errors.New("request must be non-nil")
@@ -213,29 +211,6 @@ func (c *Client) do(req *ScalewayRequest, res interface{}) (sdkErr error) {
213211
httpRequest = httpRequest.WithContext(req.ctx)
214212
}
215213

216-
if logger.ShouldLog(logger.LogLevelDebug) {
217-
// Keep original headers (before anonymization)
218-
originalHeaders := httpRequest.Header
219-
220-
// Get anonymized headers
221-
httpRequest.Header = req.getAllHeaders(req.auth, c.userAgent, true)
222-
223-
dump, err := httputil.DumpRequestOut(httpRequest, true)
224-
if err != nil {
225-
logger.Warningf("cannot dump outgoing request: %s", err)
226-
} else {
227-
var logString string
228-
logString += "\n--------------- Scaleway SDK REQUEST %d : ---------------\n"
229-
logString += "%s\n"
230-
logString += "---------------------------------------------------------"
231-
232-
logger.Debugf(logString, currentRequestNumber, dump)
233-
}
234-
235-
// Restore original headers before sending the request
236-
httpRequest.Header = originalHeaders
237-
}
238-
239214
// execute request
240215
httpResponse, err := c.httpClient.Do(httpRequest)
241216
if err != nil {
@@ -248,19 +223,6 @@ func (c *Client) do(req *ScalewayRequest, res interface{}) (sdkErr error) {
248223
sdkErr = errors.Wrap(closeErr, "could not close http response")
249224
}
250225
}()
251-
if logger.ShouldLog(logger.LogLevelDebug) {
252-
dump, err := httputil.DumpResponse(httpResponse, true)
253-
if err != nil {
254-
logger.Warningf("cannot dump ingoing response: %s", err)
255-
} else {
256-
var logString string
257-
logString += "\n--------------- Scaleway SDK RESPONSE %d : ---------------\n"
258-
logString += "%s\n"
259-
logString += "----------------------------------------------------------"
260-
261-
logger.Debugf(logString, currentRequestNumber, dump)
262-
}
263-
}
264226

265227
sdkErr = hasResponseError(httpResponse)
266228
if sdkErr != nil {
@@ -561,3 +523,12 @@ func setInsecureMode(c httpClient) {
561523
}
562524
transportClient.TLSClientConfig.InsecureSkipVerify = true
563525
}
526+
527+
func setRequestLogging(c httpClient) {
528+
standardHTTPClient, ok := c.(*http.Client)
529+
if !ok {
530+
logger.Warningf("client: cannot use request logger with HTTP client of type %T", c)
531+
return
532+
}
533+
standardHTTPClient.Transport = &requestLoggerTransport{rt: standardHTTPClient.Transport}
534+
}

scw/client_test.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,9 @@ func TestNewClientWithOptions(t *testing.T) {
124124
testhelpers.Equals(t, testAPIURL, client.apiURL)
125125

126126
clientTransport, ok := client.httpClient.(*http.Client).Transport.(*http.Transport)
127+
if loggerTransport, isLogger := client.httpClient.(*http.Client).Transport.(*requestLoggerTransport); !ok && isLogger {
128+
clientTransport, ok = loggerTransport.rt.(*http.Transport)
129+
}
127130
testhelpers.Assert(t, ok, "clientTransport must be not nil")
128131
testhelpers.Assert(t, clientTransport.TLSClientConfig != nil, "TLSClientConfig must be not nil")
129132
testhelpers.Equals(t, testInsecure, clientTransport.TLSClientConfig.InsecureSkipVerify)

scw/transport.go

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package scw
2+
3+
import (
4+
"net/http"
5+
"net/http/httputil"
6+
"sync/atomic"
7+
8+
"github.com/scaleway/scaleway-sdk-go/internal/auth"
9+
"github.com/scaleway/scaleway-sdk-go/logger"
10+
)
11+
12+
type requestLoggerTransport struct {
13+
rt http.RoundTripper
14+
// requestNumber auto increments on each do().
15+
// This allows easy distinguishing of concurrently performed requests in log.
16+
requestNumber uint32
17+
}
18+
19+
func (l *requestLoggerTransport) RoundTrip(request *http.Request) (*http.Response, error) {
20+
currentRequestNumber := atomic.AddUint32(&l.requestNumber, 1)
21+
// Keep original headers (before anonymization)
22+
originalHeaders := request.Header
23+
24+
// Get anonymized headers
25+
request.Header = auth.AnonymizeTokenHeaders(request.Header.Clone())
26+
27+
dump, err := httputil.DumpRequestOut(request, true)
28+
if err != nil {
29+
logger.Warningf("cannot dump outgoing request: %s", err)
30+
} else {
31+
var logString string
32+
logString += "\n--------------- Scaleway SDK REQUEST %d : ---------------\n"
33+
logString += "%s\n"
34+
logString += "---------------------------------------------------------"
35+
36+
logger.Debugf(logString, currentRequestNumber, dump)
37+
}
38+
39+
// Restore original headers before sending the request
40+
request.Header = originalHeaders
41+
response, requestError := l.rt.RoundTrip(request)
42+
if requestError != nil {
43+
_, isSdkError := requestError.(SdkError)
44+
if !isSdkError {
45+
return response, err
46+
}
47+
}
48+
49+
dump, err = httputil.DumpResponse(response, true)
50+
if err != nil {
51+
logger.Warningf("cannot dump ingoing response: %s", err)
52+
} else {
53+
var logString string
54+
logString += "\n--------------- Scaleway SDK RESPONSE %d : ---------------\n"
55+
logString += "%s\n"
56+
logString += "----------------------------------------------------------"
57+
58+
logger.Debugf(logString, currentRequestNumber, dump)
59+
}
60+
61+
return response, requestError
62+
}

0 commit comments

Comments
 (0)