Skip to content

Commit

Permalink
#180 #182 debug log callback added to customize log info before logging
Browse files Browse the repository at this point in the history
  • Loading branch information
jeevatkm committed Sep 19, 2018
1 parent d4920dc commit fdc2f93
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 9 deletions.
22 changes: 22 additions & 0 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ type Client struct {
udBeforeRequest []func(*Client, *Request) error
preReqHook func(*Client, *Request) error
afterResponse []func(*Client, *Response) error
requestLog func(*RequestLog) error
responseLog func(*ResponseLog) error
}

// User type is to hold an username and password information
Expand Down Expand Up @@ -383,6 +385,26 @@ func (c *Client) SetDebugBodyLimit(sl int64) *Client {
return c
}

// OnRequestLog method used to set request log callback into resty. Registered callback gets
// called before the resty actually logs the information.
func (c *Client) OnRequestLog(rl func(*RequestLog) error) *Client {
if c.requestLog != nil {
c.Log.Printf("Overwriting an existing on-request-log callback from=%s to=%s", functionName(c.requestLog), functionName(rl))
}
c.requestLog = rl
return c
}

// OnResponseLog method used to set response log callback into resty. Registered callback gets
// called before the resty actually logs the information.
func (c *Client) OnResponseLog(rl func(*ResponseLog) error) *Client {
if c.responseLog != nil {
c.Log.Printf("Overwriting an existing on-response-log callback from=%s to=%s", functionName(c.responseLog), functionName(rl))
}
c.responseLog = rl
return c
}

// SetDisableWarn method disables the warning message on `go-resty` client.
// For example: go-resty warns the user when BasicAuth used on HTTP mode.
// resty.SetDisableWarn(true)
Expand Down
53 changes: 53 additions & 0 deletions client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -498,3 +498,56 @@ func TestAutoGzip(t *testing.T) {
logResponse(t, resp)
}
}

func TestLogCallbacks(t *testing.T) {
ts := createAuthServer(t)
defer ts.Close()

c := New().SetDebug(true)

var lgr bytes.Buffer
c.SetLogger(&lgr)

c.OnRequestLog(func(r *RequestLog) error {
// masking authorzation header
r.Header.Set("Authorization", "Bearer *******************************")
return nil
})
c.OnResponseLog(func(r *ResponseLog) error {
r.Header.Add("X-Debug-Resposne-Log", "Modified :)")
r.Body += "\nModified the response body content"
return nil
})

c.SetTLSClientConfig(&tls.Config{InsecureSkipVerify: true}).
SetAuthToken("004DDB79-6801-4587-B976-F093E6AC44FF")

resp, err := c.R().
SetAuthToken("004DDB79-6801-4587-B976-F093E6AC44FF-Request").
Get(ts.URL + "/profile")

assertError(t, err)
assertEqual(t, http.StatusOK, resp.StatusCode())

// Validating debug log updates
logInfo := lgr.String()
assertEqual(t, true, strings.Contains(logInfo, "Bearer *******************************"))
assertEqual(t, true, strings.Contains(logInfo, "X-Debug-Resposne-Log"))
assertEqual(t, true, strings.Contains(logInfo, "Modified the response body content"))

// Error scenario
c.OnRequestLog(func(r *RequestLog) error { return errors.New("request test error") })
resp, err = c.R().
SetAuthToken("004DDB79-6801-4587-B976-F093E6AC44FF-Request").
Get(ts.URL + "/profile")
assertEqual(t, errors.New("request test error"), err)
assertNil(t, resp)

c.OnRequestLog(nil)
c.OnResponseLog(func(r *ResponseLog) error { return errors.New("response test error") })
resp, err = c.R().
SetAuthToken("004DDB79-6801-4587-B976-F093E6AC44FF-Request").
Get(ts.URL + "/profile")
assertEqual(t, errors.New("response test error"), err)
assertNotNil(t, resp)
}
23 changes: 18 additions & 5 deletions middleware.go
Original file line number Diff line number Diff line change
Expand Up @@ -223,12 +223,19 @@ func addCredentials(c *Client, r *Request) error {
func requestLogger(c *Client, r *Request) error {
if c.Debug {
rr := r.RawRequest
rl := &RequestLog{Header: copyHeaders(rr.Header), Body: r.fmtBodyString()}
if c.requestLog != nil {
if err := c.requestLog(rl); err != nil {
return err
}
}

reqLog := "\n---------------------- REQUEST LOG -----------------------\n" +
fmt.Sprintf("%s %s %s\n", r.Method, rr.URL.RequestURI(), rr.Proto) +
fmt.Sprintf("HOST : %s\n", rr.URL.Host) +
fmt.Sprintf("HEADERS:\n") +
composeHeaders(rr.Header) + "\n" +
fmt.Sprintf("BODY :\n%v\n", r.fmtBodyString()) +
composeHeaders(rl.Header) + "\n" +
fmt.Sprintf("BODY :\n%v\n", rl.Body) +
"----------------------------------------------------------\n"

c.Log.Print(reqLog)
Expand All @@ -243,17 +250,23 @@ func requestLogger(c *Client, r *Request) error {

func responseLogger(c *Client, res *Response) error {
if c.Debug {
rl := &ResponseLog{Header: copyHeaders(res.Header()), Body: res.fmtBodyString(c.debugBodySizeLimit)}
if c.responseLog != nil {
if err := c.responseLog(rl); err != nil {
return err
}
}

resLog := "\n---------------------- RESPONSE LOG -----------------------\n" +
fmt.Sprintf("STATUS : %s\n", res.Status()) +
fmt.Sprintf("RECEIVED AT : %v\n", res.ReceivedAt().Format(time.RFC3339Nano)) +
fmt.Sprintf("RESPONSE TIME : %v\n", res.Time()) +
"HEADERS:\n" +
composeHeaders(res.Header()) + "\n"

composeHeaders(rl.Header) + "\n"
if res.Request.isSaveResponse {
resLog += fmt.Sprintf("BODY :\n***** RESPONSE WRITTEN INTO FILE *****\n")
} else {
resLog += fmt.Sprintf("BODY :\n%v\n", res.fmtBodyString(c.debugBodySizeLimit))
resLog += fmt.Sprintf("BODY :\n%v\n", rl.Body)
}
resLog += "----------------------------------------------------------\n"

Expand Down
36 changes: 32 additions & 4 deletions util.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,30 @@ func Unmarshalc(c *Client, ct string, b []byte, d interface{}) (err error) {
return
}

//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
// RequestLog and ResponseLog type
//___________________________________

// RequestLog struct is used to collected information from resty request
// instance for debug logging. It sent to request log callback before resty
// actually logs the information.
type RequestLog struct {
Header http.Header
Body string
}

// ResponseLog struct is used to collected information from resty response
// instance for debug logging. It sent to response log callback before resty
// actually logs the information.
type ResponseLog struct {
Header http.Header
Body string
}

//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
// Package Unexported methods
//___________________________________

// way to disable the HTML escape as opt-in
func jsonMarshal(c *Client, r *Request, d interface{}) ([]byte, error) {
if !r.jsonEscapeHTML {
Expand All @@ -94,10 +118,6 @@ func jsonMarshal(c *Client, r *Request, d interface{}) ([]byte, error) {
return c.JSONMarshal(d)
}

//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
// Package Unexported methods
//___________________________________

func firstNonEmpty(v ...string) string {
for _, s := range v {
if !IsStringEmpty(s) {
Expand Down Expand Up @@ -251,3 +271,11 @@ func sortHeaderKeys(hdrs http.Header) []string {
sort.Strings(keys)
return keys
}

func copyHeaders(hdrs http.Header) http.Header {
nh := http.Header{}
for k, v := range hdrs {
nh[k] = v
}
return nh
}

0 comments on commit fdc2f93

Please sign in to comment.