Documentation ¶
Overview ¶
Package httplog provides a standard http.RoundTripper transport that can be used with standard HTTP clients to log the raw (outgoing) HTTP request and response.
Index ¶
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var DefaultTransport = http.DefaultTransport
DefaultTransport is the default transport used by the HTTP logger.
Functions ¶
This section is empty.
Types ¶
type Option ¶ added in v0.2.0
type Option func(*RoundTripLogger)
Option is a roundtrip logger option.
func WithReqResBody ¶ added in v0.3.0
WithReqResBody is a roundtrip logger option to set whether or not to log the request and response body. Useful when body content is binary.
type RoundTripLogger ¶
type RoundTripLogger struct {
// contains filtered or unexported fields
}
RoundTripLogger provides a standard http.RoundTripper transport that can be used with standard HTTP clients to log the raw (outgoing) HTTP request and response.
Example (Logf) ¶
package main import ( "fmt" "io" "net/http" "net/http/httptest" "regexp" "strings" "github.com/kenshaw/httplog" ) func main() { ts := httptest.NewServer(writeHTML(`<body>hello</body>`)) defer ts.Close() // do http request (logf has same signature as log.Printf) transport := httplog.NewPrefixedRoundTripLogger(nil, logf) cl := &http.Client{ Transport: transport, } req, err := http.NewRequest("GET", ts.URL, nil) if err != nil { panic(err) } res, err := cl.Do(req) if err != nil { panic(err) } defer res.Body.Close() } var ( cleanRE = regexp.MustCompile(`\n(->|<-) (Host|Date):.*`) spaceRE = regexp.MustCompile(`(?m)\s+$`) ) func logf(s string, v ...interface{}) { clean := cleanRE.ReplaceAllString(fmt.Sprintf(s, v...), "") clean = spaceRE.ReplaceAllString(clean, "") fmt.Println(clean) } func writeHTML(content string) http.Handler { return http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) { res.Header().Set("Content-Type", "text/html") _, _ = io.WriteString(res, strings.TrimSpace(content)) }) }
Output: -> GET / HTTP/1.1 -> User-Agent: Go-http-client/1.1 -> Accept-Encoding: gzip -> -> <- HTTP/1.1 200 OK <- Content-Length: 18 <- Content-Type: text/html <- <- <body>hello</body>
Example (Printf) ¶
package main import ( "fmt" "io" "net/http" "net/http/httptest" "regexp" "strings" "github.com/kenshaw/httplog" ) func main() { ts := httptest.NewServer(writeHTML(`<body>hello</body>`)) defer ts.Close() // do http request (printf has same signature as fmt.Printf) transport := httplog.NewPrefixedRoundTripLogger(nil, printf) cl := &http.Client{ Transport: transport, } req, err := http.NewRequest("GET", ts.URL, nil) if err != nil { panic(err) } res, err := cl.Do(req) if err != nil { panic(err) } defer res.Body.Close() } var ( cleanRE = regexp.MustCompile(`\n(->|<-) (Host|Date):.*`) spaceRE = regexp.MustCompile(`(?m)\s+$`) ) func printf(s string, v ...interface{}) (int, error) { clean := cleanRE.ReplaceAllString(fmt.Sprintf(s, v...), "") clean = spaceRE.ReplaceAllString(clean, "") return fmt.Println(clean) } func writeHTML(content string) http.Handler { return http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) { res.Header().Set("Content-Type", "text/html") _, _ = io.WriteString(res, strings.TrimSpace(content)) }) }
Output: -> GET / HTTP/1.1 -> User-Agent: Go-http-client/1.1 -> Accept-Encoding: gzip -> -> <- HTTP/1.1 200 OK <- Content-Length: 18 <- Content-Type: text/html <- <- <body>hello</body>
Example (WithReqResBody) ¶
package main import ( "fmt" "io" "net/http" "net/http/httptest" "regexp" "strings" "github.com/kenshaw/httplog" ) func main() { ts := httptest.NewServer(writeHTML(`<body>hello</body>`)) defer ts.Close() // do http request (logf has same signature as log.Printf) transport := httplog.NewPrefixedRoundTripLogger(nil, logf, httplog.WithReqResBody(false, false)) cl := &http.Client{ Transport: transport, } req, err := http.NewRequest("GET", ts.URL, nil) if err != nil { panic(err) } res, err := cl.Do(req) if err != nil { panic(err) } defer res.Body.Close() } var ( cleanRE = regexp.MustCompile(`\n(->|<-) (Host|Date):.*`) spaceRE = regexp.MustCompile(`(?m)\s+$`) ) func logf(s string, v ...interface{}) { clean := cleanRE.ReplaceAllString(fmt.Sprintf(s, v...), "") clean = spaceRE.ReplaceAllString(clean, "") fmt.Println(clean) } func writeHTML(content string) http.Handler { return http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) { res.Header().Set("Content-Type", "text/html") _, _ = io.WriteString(res, strings.TrimSpace(content)) }) }
Output: -> GET / HTTP/1.1 -> User-Agent: Go-http-client/1.1 -> Accept-Encoding: gzip -> -> <- HTTP/1.1 200 OK <- Content-Length: 18 <- Content-Type: text/html <- <-
Example (Writer) ¶
package main import ( "io" "net/http" "net/http/httptest" "os" "regexp" "strings" "github.com/kenshaw/httplog" ) func main() { ts := httptest.NewServer(writeHTML(`<body>hello</body>`)) defer ts.Close() w := NewMyWriter(os.Stdout) // do http request (w is a io.Writer) transport := httplog.NewPrefixedRoundTripLogger(nil, w) cl := &http.Client{ Transport: transport, } req, err := http.NewRequest("GET", ts.URL, nil) if err != nil { panic(err) } res, err := cl.Do(req) if err != nil { panic(err) } defer res.Body.Close() } var ( cleanRE = regexp.MustCompile(`\n(->|<-) (Host|Date):.*`) spaceRE = regexp.MustCompile(`(?m)\s+$`) ) func NewMyWriter(w io.Writer) *MyWriter { return &MyWriter{w: w} } type MyWriter struct { w io.Writer } func (w *MyWriter) Write(buf []byte) (int, error) { clean := cleanRE.ReplaceAll(buf, nil) clean = spaceRE.ReplaceAll(clean, nil) return os.Stdout.Write(append(clean, '\n')) } func writeHTML(content string) http.Handler { return http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) { res.Header().Set("Content-Type", "text/html") _, _ = io.WriteString(res, strings.TrimSpace(content)) }) }
Output: -> GET / HTTP/1.1 -> User-Agent: Go-http-client/1.1 -> Accept-Encoding: gzip -> -> <- HTTP/1.1 200 OK <- Content-Length: 18 <- Content-Type: text/html <- <- <body>hello</body>
func NewPrefixedRoundTripLogger ¶
func NewPrefixedRoundTripLogger(transport http.RoundTripper, logger interface{}, opts ...Option) *RoundTripLogger
NewPrefixedRoundTripLogger creates a new HTTP transport that logs the raw (outgoing) HTTP request and response to the provided logger.
Prefixes requests and responses with "-> " and "<-", respectively. Adds an additional blank line ("\n\n") to the output of requests and responses.
Valid types for logger:
io.Writer func(string, ...interface{}) (int, error) // fmt.Printf func(string, ...interface{}) // log.Printf
Note: will panic() when an unknown logger type is passed.
func NewRoundTripLogger ¶
func NewRoundTripLogger(transport http.RoundTripper, reqf, resf func([]byte), opts ...Option) *RoundTripLogger
NewRoundTripLogger creates a new HTTP transport that logs the raw (outgoing) HTTP request and response.
func (*RoundTripLogger) RoundTrip ¶
RoundTrip satisfies the http.RoundTripper interface.