Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implemented new logging system #562

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,13 +127,19 @@ func (c *httpClient) doRequest(ctx context.Context, method, url string, params R
if params.Body != nil {
buf := new(bytes.Buffer)
if err := json.NewEncoder(buf).Encode(params.Body); err != nil {
if c.debug && c.logger != nil {
c.logger.Errorf("failed to encode body: %v", err)
}
return fmt.Errorf("failed to encode body: %w", err)
}
bodyReader = buf
}

req, err := http.NewRequestWithContext(ctx, method, url, bodyReader)
if err != nil {
if c.debug && c.logger != nil {
c.logger.Errorf("failed to create request: %v", err)
}
return fmt.Errorf("failed to create request: %w", err)
}

Expand All @@ -147,26 +153,48 @@ func (c *httpClient) doRequest(ctx context.Context, method, url string, params R
// Apply mutators
for _, mutate := range mutators {
if err := mutate(req); err != nil {
if c.debug && c.logger != nil {
c.logger.Errorf("failed to mutate request: %v", err)
}
return fmt.Errorf("failed to mutate request: %w", err)
}
}

// Log the request if in debug mode
if c.debug && c.logger != nil {
c.logger.Debugf("sending request: %s %s", method, url)
lgarber-akamai marked this conversation as resolved.
Show resolved Hide resolved
}

// Send the request
resp, err := c.httpClient.Do(req)
if err != nil {
if c.debug && c.logger != nil {
c.logger.Errorf("failed to send request: %v", err)
}
return fmt.Errorf("failed to send request: %w", err)
}
defer resp.Body.Close()

// Check for HTTP errors
resp, err = coupleAPIErrorsHTTP(resp, err)
if err != nil {
if c.debug && c.logger != nil {
c.logger.Errorf("received HTTP error: %v", err)
}
return err
}

// Log the response if in debug mode
if c.debug && c.logger != nil {
c.logger.Debugf("received response: %s %s, status: %d", method, url, resp.StatusCode)
}

// Decode the response body
if params.Response != nil {
if err := json.NewDecoder(resp.Body).Decode(params.Response); err != nil {
if c.debug && c.logger != nil {
c.logger.Errorf("failed to decode response: %v", err)
}
return fmt.Errorf("failed to decode response: %w", err)
}
}
Expand Down Expand Up @@ -199,6 +227,20 @@ func (c *Client) SetLogger(logger Logger) *Client {
return c
}

//nolint:unused
func (c *httpClient) httpSetDebug(debug bool) *httpClient {
c.debug = debug

return c
}

//nolint:unused
func (c *httpClient) httpSetLogger(logger httpLogger) *httpClient {
c.logger = logger

return c
}

// OnBeforeRequest adds a handler to the request body to run before the request is sent
func (c *Client) OnBeforeRequest(m func(request *Request) error) {
c.resty.OnBeforeRequest(func(_ *resty.Client, req *resty.Request) error {
Expand Down
2 changes: 2 additions & 0 deletions client_http.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,6 @@ type httpClient struct {
cachedEntries map[string]clientCacheEntry
//nolint:unused
cachedEntryLock *sync.RWMutex
//nolint:unused
logger httpLogger
}
72 changes: 72 additions & 0 deletions client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -336,3 +336,75 @@ func TestDoRequest_MutatorError(t *testing.T) {
t.Fatalf("expected error %q, got: %v", expectedErr, err)
}
}

func TestDoRequestLogging_Success(t *testing.T) {
var logBuffer bytes.Buffer
logger := createLogger()
logger.l.SetOutput(&logBuffer) // Redirect log output to buffer

client := &httpClient{
httpClient: http.DefaultClient,
debug: true,
logger: logger,
}

handler := func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(`{"message":"success"}`))
}
server := httptest.NewServer(http.HandlerFunc(handler))
defer server.Close()

params := RequestParams{
Response: &map[string]string{},
}

err := client.doRequest(context.Background(), http.MethodGet, server.URL, params)
if err != nil {
t.Fatal(cmp.Diff(nil, err))
}

logInfo := logBuffer.String()
expectedLogs := []string{
"DEBUG RESTY sending request: GET " + server.URL,
"DEBUG RESTY received response: GET " + server.URL + ", status: 200",
}

for _, expectedLog := range expectedLogs {
if !strings.Contains(logInfo, expectedLog) {
t.Fatalf("expected log %q not found in logs", expectedLog)
}
}
}

func TestDoRequestLogging_Error(t *testing.T) {
var logBuffer bytes.Buffer
logger := createLogger()
logger.l.SetOutput(&logBuffer) // Redirect log output to buffer

client := &httpClient{
httpClient: http.DefaultClient,
debug: true,
logger: logger,
}

params := RequestParams{
Body: map[string]interface{}{
"invalid": func() {},
},
}

err := client.doRequest(context.Background(), http.MethodPost, "http://example.com", params)
expectedErr := "failed to encode body"
if err == nil || !strings.Contains(err.Error(), expectedErr) {
t.Fatalf("expected error %q, got: %v", expectedErr, err)
}

logInfo := logBuffer.String()
expectedLog := "ERROR RESTY failed to encode body"

if !strings.Contains(logInfo, expectedLog) {
t.Fatalf("expected log %q not found in logs", expectedLog)
}
}
51 changes: 51 additions & 0 deletions logger.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package linodego

import (
"log"
"os"
)

//nolint:unused
type httpLogger interface {
Errorf(format string, v ...interface{})
Warnf(format string, v ...interface{})
Debugf(format string, v ...interface{})
}

//nolint:unused
type logger struct {
l *log.Logger
}

//nolint:unused
func createLogger() *logger {
l := &logger{l: log.New(os.Stderr, "", log.Ldate|log.Lmicroseconds)}
return l
}

//nolint:unused
var _ httpLogger = (*logger)(nil)

//nolint:unused
func (l *logger) Errorf(format string, v ...interface{}) {
l.output("ERROR RESTY "+format, v...)
}

//nolint:unused
func (l *logger) Warnf(format string, v ...interface{}) {
l.output("WARN RESTY "+format, v...)
}

//nolint:unused
func (l *logger) Debugf(format string, v ...interface{}) {
l.output("DEBUG RESTY "+format, v...)
}

//nolint:unused
func (l *logger) output(format string, v ...interface{}) { //nolint:goprintffuncname
if len(v) == 0 {
l.l.Print(format)
return
}
l.l.Printf(format, v...)
}
Loading