diff --git a/cmd/admin-handlers.go b/cmd/admin-handlers.go index b06921761..3a2e0934d 100644 --- a/cmd/admin-handlers.go +++ b/cmd/admin-handlers.go @@ -1465,6 +1465,7 @@ func (a adminAPIHandlers) SetConfigKeysHandler(w http.ResponseWriter, r *http.Re func (a adminAPIHandlers) TraceHandler(w http.ResponseWriter, r *http.Request) { ctx := newContext(r, w, "HTTPTrace") trcAll := r.URL.Query().Get("all") == "true" + trcErr := r.URL.Query().Get("err") == "true" // Validate request signature. adminAPIErr := checkAdminRequestAuthType(ctx, r, "") @@ -1487,11 +1488,15 @@ func (a adminAPIHandlers) TraceHandler(w http.ResponseWriter, r *http.Request) { traceCh := make(chan interface{}, 4000) filter := func(entry interface{}) bool { + trcInfo := entry.(trace.Info) + if trcErr && isHTTPStatusOK(trcInfo.RespInfo.StatusCode) { + return false + } if trcAll { return true } - trcInfo := entry.(trace.Info) return !strings.HasPrefix(trcInfo.ReqInfo.Path, minioReservedBucketPath) + } remoteHosts := getRemoteHosts(globalEndpoints) peers, err := getRestClients(remoteHosts) @@ -1501,7 +1506,7 @@ func (a adminAPIHandlers) TraceHandler(w http.ResponseWriter, r *http.Request) { globalHTTPTrace.Subscribe(traceCh, doneCh, filter) for _, peer := range peers { - peer.Trace(traceCh, doneCh, trcAll) + peer.Trace(traceCh, doneCh, trcAll, trcErr) } enc := json.NewEncoder(w) diff --git a/cmd/handler-utils.go b/cmd/handler-utils.go index 69f2905fa..2cf381ccb 100644 --- a/cmd/handler-utils.go +++ b/cmd/handler-utils.go @@ -394,3 +394,20 @@ func getHostName(r *http.Request) (hostName string) { } return } + +func isHTTPStatusOK(statusCode int) bool { + // List of success status. + var successStatus = []int{ + http.StatusOK, + http.StatusCreated, + http.StatusAccepted, + http.StatusNoContent, + http.StatusPartialContent, + } + for _, okstatus := range successStatus { + if statusCode == okstatus { + return true + } + } + return false +} diff --git a/cmd/peer-rest-client.go b/cmd/peer-rest-client.go index 27d14c4fe..33da7efee 100644 --- a/cmd/peer-rest-client.go +++ b/cmd/peer-rest-client.go @@ -467,9 +467,10 @@ func (client *peerRESTClient) BackgroundHealStatus() (madmin.BgHealState, error) return state, err } -func (client *peerRESTClient) doTrace(traceCh chan interface{}, doneCh chan struct{}, trcAll bool) { +func (client *peerRESTClient) doTrace(traceCh chan interface{}, doneCh chan struct{}, trcAll, trcErr bool) { values := make(url.Values) values.Set(peerRESTTraceAll, strconv.FormatBool(trcAll)) + values.Set(peerRESTTraceErr, strconv.FormatBool(trcErr)) // To cancel the REST request in case doneCh gets closed. ctx, cancel := context.WithCancel(context.Background()) @@ -507,10 +508,10 @@ func (client *peerRESTClient) doTrace(traceCh chan interface{}, doneCh chan stru } // Trace - send http trace request to peer nodes -func (client *peerRESTClient) Trace(traceCh chan interface{}, doneCh chan struct{}, trcAll bool) { +func (client *peerRESTClient) Trace(traceCh chan interface{}, doneCh chan struct{}, trcAll, trcErr bool) { go func() { for { - client.doTrace(traceCh, doneCh, trcAll) + client.doTrace(traceCh, doneCh, trcAll, trcErr) select { case <-doneCh: return diff --git a/cmd/peer-rest-common.go b/cmd/peer-rest-common.go index a030756ab..2408d7311 100644 --- a/cmd/peer-rest-common.go +++ b/cmd/peer-rest-common.go @@ -56,4 +56,5 @@ const ( peerRESTProfiler = "profiler" peerRESTDryRun = "dry-run" peerRESTTraceAll = "all" + peerRESTTraceErr = "err" ) diff --git a/cmd/peer-rest-server.go b/cmd/peer-rest-server.go index d0843180d..2e96e2f09 100644 --- a/cmd/peer-rest-server.go +++ b/cmd/peer-rest-server.go @@ -717,17 +717,23 @@ func (s *peerRESTServer) TraceHandler(w http.ResponseWriter, r *http.Request) { return } trcAll := r.URL.Query().Get(peerRESTTraceAll) == "true" + trcErr := r.URL.Query().Get(peerRESTTraceErr) == "true" w.Header().Set(xhttp.Connection, "close") w.WriteHeader(http.StatusOK) w.(http.Flusher).Flush() filter := func(entry interface{}) bool { + trcInfo := entry.(trace.Info) + + if trcErr && isHTTPStatusOK(trcInfo.RespInfo.StatusCode) { + return false + } if trcAll { return true } - trcInfo := entry.(trace.Info) return !strings.HasPrefix(trcInfo.ReqInfo.Path, minioReservedBucketPath) + } doneCh := make(chan struct{}) diff --git a/pkg/madmin/api-trace.go b/pkg/madmin/api-trace.go index fd193ade9..9b276bb10 100644 --- a/pkg/madmin/api-trace.go +++ b/pkg/madmin/api-trace.go @@ -32,7 +32,7 @@ type TraceInfo struct { } // Trace - listen on http trace notifications. -func (adm AdminClient) Trace(allTrace bool, doneCh <-chan struct{}) <-chan TraceInfo { +func (adm AdminClient) Trace(allTrace, errTrace bool, doneCh <-chan struct{}) <-chan TraceInfo { traceInfoCh := make(chan TraceInfo) // Only success, start a routine to start reading line by line. go func(traceInfoCh chan<- TraceInfo) { @@ -40,6 +40,7 @@ func (adm AdminClient) Trace(allTrace bool, doneCh <-chan struct{}) <-chan Trace for { urlValues := make(url.Values) urlValues.Set("all", strconv.FormatBool(allTrace)) + urlValues.Set("err", strconv.FormatBool(errTrace)) reqData := requestData{ relPath: "/v1/trace", queryValues: urlValues, diff --git a/pkg/madmin/examples/trace.go b/pkg/madmin/examples/trace.go index 7566562ac..7909b2447 100644 --- a/pkg/madmin/examples/trace.go +++ b/pkg/madmin/examples/trace.go @@ -20,6 +20,7 @@ package main import ( + "fmt" "log" "github.com/minio/minio/pkg/madmin" @@ -40,7 +41,9 @@ func main() { // Start listening on all http trace activity from all servers // in the minio cluster. - traceCh := madmClnt.Trace(false, doneCh) + allTrace := false + errTrace := false + traceCh := madmClnt.Trace(allTrace, errTrace, doneCh) for traceInfo := range traceCh { if traceInfo.Err != nil { fmt.Println(traceInfo.Err)