-
Notifications
You must be signed in to change notification settings - Fork 77
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Support Atlas client customization & logging
Signed-off-by: Jose Vazquez <jose.vazquez@mongodb.com>
- Loading branch information
Showing
5 changed files
with
215 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
package atlas | ||
|
||
import ( | ||
"fmt" | ||
"log" | ||
"net/http" | ||
"net/http/httputil" | ||
"os" | ||
"strings" | ||
) | ||
|
||
func dumpRequest(req *http.Request) string { | ||
if req == nil { | ||
return "<nil>" | ||
} | ||
requestDump, err := httputil.DumpRequest(req, true) | ||
if err != nil { | ||
return err.Error() | ||
} | ||
return string(requestDump) | ||
} | ||
|
||
func dumpResponse(rsp *http.Response) string { | ||
if rsp == nil { | ||
return "<nil>" | ||
} | ||
responseDump, err := httputil.DumpResponse(rsp, true) | ||
if err != nil { | ||
return err.Error() | ||
} | ||
return string(responseDump) | ||
} | ||
|
||
// AtlasLog is all is needed to implement logging | ||
type AtlasLog interface { | ||
Printf(format string, v ...any) | ||
} | ||
|
||
// ClientLogger is a wrapper or a http.RoundTripper that logs all interactions | ||
type ClientLogger struct { | ||
wrapped http.RoundTripper | ||
logger AtlasLog | ||
} | ||
|
||
// NewDefaultClientLogger creates a new ClientLogger with default http transport and logger | ||
func NewDefaultClientLogger() *ClientLogger { | ||
return NewClientLogger(http.DefaultTransport, NewDefaultFileLog()) | ||
} | ||
|
||
// NewClientLogger creates a new ClientLogger instance | ||
func NewClientLogger(roundTripper http.RoundTripper, logger AtlasLog) *ClientLogger { | ||
return &ClientLogger{wrapped: roundTripper, logger: logger} | ||
} | ||
|
||
// RoundTrip implements http.RoundTripper for ClientLogger | ||
func (cl *ClientLogger) RoundTrip(req *http.Request) (*http.Response, error) { | ||
cl.logger.Printf("SENT To Atlas Request:\n%s\n", dumpRequest(req)) | ||
rsp, err := cl.wrapped.RoundTrip(req) | ||
cl.logger.Printf("RECV From Atlas Response:\n%s\n", dumpResponse(rsp)) | ||
return rsp, err | ||
} | ||
|
||
// FileLog dumps interactions to both standard log and a named file | ||
type FileLog struct { | ||
filename string | ||
} | ||
|
||
// NewDefaultFileLog returns a FileLog to "./atlas-http.log" | ||
func NewDefaultFileLog() *FileLog { | ||
return NewFileLog("./atlas-http.log") | ||
} | ||
|
||
// NewFileLog returns a FileLog to the given filename | ||
func NewFileLog(filename string) *FileLog { | ||
return &FileLog{filename: filename} | ||
} | ||
|
||
// Printf implements AtlasLog for FileLog | ||
func (fl *FileLog) Printf(format string, args ...any) { | ||
log.Printf(format, args...) | ||
f, err := os.OpenFile(fl.filename, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0600) | ||
if err != nil { | ||
log.Printf("failed log to filename: %v\n", err) | ||
return | ||
} | ||
defer f.Close() | ||
fmt.Fprintf(f, format, args...) | ||
if !strings.HasSuffix(format, "\n") { | ||
fmt.Fprintln(f) | ||
} | ||
} | ||
|
||
// LoggedClientBuilder returns a function to create logged clients. | ||
// Usage example: | ||
// | ||
// atlas.CustomHTTPClientFn = atlas.LoggedClientBuilder(atlas.NewDefaultClientLogger()) | ||
func LoggedClientBuilder(cl *ClientLogger) HTTPClientFn { | ||
return func() *http.Client { | ||
return &http.Client{Transport: cl} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
package atlas_test | ||
|
||
import ( | ||
"bytes" | ||
"fmt" | ||
"net/http" | ||
"os" | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
|
||
"github.com/mongodb/mongodb-atlas-kubernetes/pkg/controller/atlas" | ||
) | ||
|
||
func TestNewDefaultLogger(t *testing.T) { | ||
assert.Equal(t, | ||
atlas.NewClientLogger(http.DefaultTransport, atlas.NewDefaultFileLog()), | ||
atlas.NewDefaultClientLogger(), | ||
) | ||
} | ||
|
||
type fakeRoundTripper struct{} | ||
|
||
func (frt *fakeRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { | ||
return nil, nil | ||
} | ||
|
||
type fakeLogger struct { | ||
buf *bytes.Buffer | ||
} | ||
|
||
func NewFakeLogger() *fakeLogger { | ||
return &fakeLogger{buf: bytes.NewBufferString("")} | ||
} | ||
|
||
func (fl *fakeLogger) Printf(format string, v ...any) { | ||
fmt.Fprintf(fl.buf, format, v...) | ||
} | ||
|
||
func (fl *fakeLogger) String() string { | ||
return fl.buf.String() | ||
} | ||
|
||
var emptyRoundTrip = `SENT To Atlas Request: | ||
<nil> | ||
RECV From Atlas Response: | ||
<nil> | ||
` | ||
|
||
func TestRoundTrip(t *testing.T) { | ||
logger := NewFakeLogger() | ||
cl := atlas.NewClientLogger(&fakeRoundTripper{}, logger) | ||
|
||
_, err := cl.RoundTrip(nil) | ||
|
||
assert.NoError(t, err) | ||
assert.Equal(t, emptyRoundTrip, logger.String()) | ||
} | ||
|
||
func TestNewDefaultFileLog(t *testing.T) { | ||
assert.Equal(t, | ||
atlas.NewFileLog("./atlas-http.log"), | ||
atlas.NewDefaultFileLog(), | ||
) | ||
} | ||
|
||
func TestLogToFile(t *testing.T) { | ||
tmpfile, err := os.CreateTemp("", "log-to-file-test") | ||
assert.NoError(t, err) | ||
tmpfile.Close() | ||
defer os.Remove(tmpfile.Name()) | ||
fl := atlas.NewFileLog(tmpfile.Name()) | ||
|
||
fl.Printf("this is a %s", "test") | ||
contents, err := os.ReadFile(tmpfile.Name()) | ||
assert.NoError(t, err) | ||
assert.Equal(t, "this is a test\n", string(contents)) | ||
} | ||
|
||
func TestLoggerClientBuilder(t *testing.T) { | ||
cl := atlas.NewDefaultClientLogger() | ||
customClientFn := atlas.LoggedClientBuilder(cl) | ||
cc := customClientFn() | ||
assert.Equal(t, cl, cc.Transport) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters