|
|
|
@ -18,7 +18,6 @@ package cmd |
|
|
|
|
|
|
|
|
|
import ( |
|
|
|
|
"bytes" |
|
|
|
|
"fmt" |
|
|
|
|
"io" |
|
|
|
|
"io/ioutil" |
|
|
|
|
"net" |
|
|
|
@ -34,8 +33,6 @@ import ( |
|
|
|
|
trace "github.com/minio/minio/pkg/trace" |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
var traceBodyPlaceHolder = []byte("<BODY>") |
|
|
|
|
|
|
|
|
|
// recordRequest - records the first recLen bytes
|
|
|
|
|
// of a given io.Reader
|
|
|
|
|
type recordRequest struct { |
|
|
|
@ -78,81 +75,7 @@ func (r *recordRequest) Data() []byte { |
|
|
|
|
return r.buf.Bytes() |
|
|
|
|
} |
|
|
|
|
// ... otherwise we return <BODY> placeholder
|
|
|
|
|
return traceBodyPlaceHolder |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// recordResponseWriter - records the first recLen bytes
|
|
|
|
|
// of a given http.ResponseWriter
|
|
|
|
|
type recordResponseWriter struct { |
|
|
|
|
// Data source to record
|
|
|
|
|
http.ResponseWriter |
|
|
|
|
// Response body should be logged
|
|
|
|
|
logBody bool |
|
|
|
|
// Internal recording buffer
|
|
|
|
|
headers bytes.Buffer |
|
|
|
|
body bytes.Buffer |
|
|
|
|
// The status code of the current HTTP request
|
|
|
|
|
statusCode int |
|
|
|
|
// Indicate if headers are written in the log
|
|
|
|
|
headersLogged bool |
|
|
|
|
// number of bytes written
|
|
|
|
|
bytesWritten int |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Write the headers into the given buffer
|
|
|
|
|
func (r *recordResponseWriter) writeHeaders(w io.Writer, statusCode int, headers http.Header) { |
|
|
|
|
n, _ := fmt.Fprintf(w, "%d %s\n", statusCode, http.StatusText(statusCode)) |
|
|
|
|
r.bytesWritten += n |
|
|
|
|
for k, v := range headers { |
|
|
|
|
n, _ := fmt.Fprintf(w, "%s: %s\n", k, v[0]) |
|
|
|
|
r.bytesWritten += n |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Record the headers.
|
|
|
|
|
func (r *recordResponseWriter) WriteHeader(i int) { |
|
|
|
|
r.statusCode = i |
|
|
|
|
if !r.headersLogged { |
|
|
|
|
r.writeHeaders(&r.headers, i, r.ResponseWriter.Header()) |
|
|
|
|
r.headersLogged = true |
|
|
|
|
} |
|
|
|
|
r.ResponseWriter.WriteHeader(i) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (r *recordResponseWriter) Write(p []byte) (n int, err error) { |
|
|
|
|
n, err = r.ResponseWriter.Write(p) |
|
|
|
|
r.bytesWritten += n |
|
|
|
|
if !r.headersLogged { |
|
|
|
|
// We assume the response code to be '200 OK' when WriteHeader() is not called,
|
|
|
|
|
// that way following Golang HTTP response behavior.
|
|
|
|
|
r.writeHeaders(&r.headers, http.StatusOK, r.ResponseWriter.Header()) |
|
|
|
|
r.headersLogged = true |
|
|
|
|
} |
|
|
|
|
if r.statusCode >= http.StatusBadRequest || r.logBody { |
|
|
|
|
// Always logging error responses.
|
|
|
|
|
r.body.Write(p) |
|
|
|
|
} |
|
|
|
|
return n, err |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (r *recordResponseWriter) Size() int { |
|
|
|
|
return r.bytesWritten |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Calls the underlying Flush.
|
|
|
|
|
func (r *recordResponseWriter) Flush() { |
|
|
|
|
r.ResponseWriter.(http.Flusher).Flush() |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Return response body.
|
|
|
|
|
func (r *recordResponseWriter) Body() []byte { |
|
|
|
|
// If there was an error response or body logging is enabled
|
|
|
|
|
// then we return the body contents
|
|
|
|
|
if r.statusCode >= http.StatusBadRequest || r.logBody { |
|
|
|
|
return r.body.Bytes() |
|
|
|
|
} |
|
|
|
|
// ... otherwise we return the <BODY> place holder
|
|
|
|
|
return traceBodyPlaceHolder |
|
|
|
|
return logger.BodyPlaceHolder |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// getOpName sanitizes the operation name for mc
|
|
|
|
@ -205,16 +128,15 @@ func Trace(f http.HandlerFunc, logBody bool, w http.ResponseWriter, r *http.Requ |
|
|
|
|
Headers: reqHeaders, |
|
|
|
|
Body: reqBodyRecorder.Data(), |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Setup a http response body recorder
|
|
|
|
|
respBodyRecorder := &recordResponseWriter{ResponseWriter: w, logBody: logBody} |
|
|
|
|
f(logger.NewResponseWriter(respBodyRecorder), r) |
|
|
|
|
rw, _ := w.(*logger.ResponseWriter) |
|
|
|
|
rw.LogBody = logBody |
|
|
|
|
f(rw, r) |
|
|
|
|
|
|
|
|
|
rs := trace.ResponseInfo{ |
|
|
|
|
Time: time.Now().UTC(), |
|
|
|
|
Headers: respBodyRecorder.Header().Clone(), |
|
|
|
|
StatusCode: respBodyRecorder.statusCode, |
|
|
|
|
Body: respBodyRecorder.Body(), |
|
|
|
|
Headers: rw.Header().Clone(), |
|
|
|
|
StatusCode: rw.StatusCode, |
|
|
|
|
Body: rw.Body(), |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if rs.StatusCode == 0 { |
|
|
|
@ -225,9 +147,10 @@ func Trace(f http.HandlerFunc, logBody bool, w http.ResponseWriter, r *http.Requ |
|
|
|
|
t.RespInfo = rs |
|
|
|
|
|
|
|
|
|
t.CallStats = trace.CallStats{ |
|
|
|
|
Latency: rs.Time.Sub(rq.Time), |
|
|
|
|
InputBytes: reqBodyRecorder.Size(), |
|
|
|
|
OutputBytes: respBodyRecorder.Size(), |
|
|
|
|
Latency: rs.Time.Sub(rw.StartTime), |
|
|
|
|
InputBytes: reqBodyRecorder.Size(), |
|
|
|
|
OutputBytes: rw.Size(), |
|
|
|
|
TimeToFirstByte: rw.TimeToFirstByte, |
|
|
|
|
} |
|
|
|
|
return t |
|
|
|
|
} |
|
|
|
|