So much of what we develop these days ends up using HTTP. Usually when developing an HTTP service it’s quite easy to add logging to aid debugging. But when developing a client, especially one that uses a library which does the actual HTTP calls, it can be harder to insert logging in all the right places.
The GODEBUG environment variable 🔗
GODEBUG is a useful, but somewhat under-documented way of debugging Go programs. HTTP debugging in particular uses the following variables:
GODEBUG=http2server=1 # server logging
GODEBUG=http2client=2 # client verbose logging
GODEBUG=http2debug=1 # log both
Add logging code 🔗
Even with GODEBUG I got no logs from the oauth2 package. I found a solution for injecting my own logging code:
type LoggingRoundTripper struct {
Wrapped http.RoundTripper
DumpRequest bool
DumpResponse bool
}
func (lrt LoggingRoundTripper) RoundTrip(r *http.Request) (*http.Response,
error,
) {
dump, err := httputil.DumpRequest(r, lrt.DumpRequest)
if err != nil {
slog.Error("error", slog.Any("err", err))
return nil, fmt.Errorf("failed to dump request: %w", err)
}
fmt.Println(string(dump))
res, err := lrt.Wrapped.RoundTrip(r)
if err != nil {
slog.Error("error", slog.Any("err", err))
return nil, fmt.Errorf("failed HTTP request: %w", err)
}
dump, err = httputil.DumpResponse(res, lrt.DumpResponse)
if err != nil {
slog.Error("error", slog.Any("err", err))
return nil, fmt.Errorf("failed dumping response: %w", err)
}
fmt.Println(string(dump))
return res, nil
}
// To use with oauth2
ctx = context.WithValue(ctx, oauth2.HTTPClient, &http.Client{Transport:
LoggingRoundTripper{
Wrapped: http.DefaultTransport,
DumpRequest: true,
DumpResponse: true,
}})
conf := &oauth2.Config{...}
conf.Exchange(ctx, ...)