| 
									
										
										
										
											2017-10-25 10:04:51 +08:00
										 |  |  | /* | 
					
						
							| 
									
										
										
										
											2019-04-10 02:39:42 +08:00
										 |  |  |  * MinIO Cloud Storage, (C) 2017 MinIO, Inc. | 
					
						
							| 
									
										
										
										
											2017-10-25 10:04:51 +08:00
										 |  |  |  * | 
					
						
							|  |  |  |  * Licensed under the Apache License, Version 2.0 (the "License"); | 
					
						
							|  |  |  |  * you may not use this file except in compliance with the License. | 
					
						
							|  |  |  |  * You may obtain a copy of the License at | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *     http://www.apache.org/licenses/LICENSE-2.0
 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Unless required by applicable law or agreed to in writing, software | 
					
						
							|  |  |  |  * distributed under the License is distributed on an "AS IS" BASIS, | 
					
						
							|  |  |  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
					
						
							|  |  |  |  * See the License for the specific language governing permissions and | 
					
						
							|  |  |  |  * limitations under the License. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-09 06:54:41 +08:00
										 |  |  | package cmd | 
					
						
							| 
									
										
										
										
											2017-10-25 10:04:51 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"bytes" | 
					
						
							|  |  |  | 	"io" | 
					
						
							|  |  |  | 	"io/ioutil" | 
					
						
							| 
									
										
										
										
											2019-07-06 11:41:35 +08:00
										 |  |  | 	"net" | 
					
						
							| 
									
										
										
										
											2017-10-25 10:04:51 +08:00
										 |  |  | 	"net/http" | 
					
						
							|  |  |  | 	"reflect" | 
					
						
							|  |  |  | 	"runtime" | 
					
						
							| 
									
										
										
										
											2019-06-09 06:54:41 +08:00
										 |  |  | 	"strconv" | 
					
						
							| 
									
										
										
										
											2017-10-25 10:04:51 +08:00
										 |  |  | 	"strings" | 
					
						
							|  |  |  | 	"time" | 
					
						
							| 
									
										
										
										
											2019-06-09 06:54:41 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-16 07:17:46 +08:00
										 |  |  | 	"github.com/minio/minio/cmd/logger" | 
					
						
							| 
									
										
										
										
											2019-09-13 06:29:59 +08:00
										 |  |  | 	"github.com/minio/minio/pkg/handlers" | 
					
						
							| 
									
										
										
										
											2019-06-09 06:54:41 +08:00
										 |  |  | 	trace "github.com/minio/minio/pkg/trace" | 
					
						
							| 
									
										
										
										
											2017-10-25 10:04:51 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // recordRequest - records the first recLen bytes
 | 
					
						
							|  |  |  | // of a given io.Reader
 | 
					
						
							|  |  |  | type recordRequest struct { | 
					
						
							|  |  |  | 	// Data source to record
 | 
					
						
							|  |  |  | 	io.Reader | 
					
						
							|  |  |  | 	// Response body should be logged
 | 
					
						
							|  |  |  | 	logBody bool | 
					
						
							|  |  |  | 	// Internal recording buffer
 | 
					
						
							|  |  |  | 	buf bytes.Buffer | 
					
						
							| 
									
										
										
										
											2019-07-19 06:29:17 +08:00
										 |  |  | 	// request headers
 | 
					
						
							|  |  |  | 	headers http.Header | 
					
						
							|  |  |  | 	// total bytes read including header size
 | 
					
						
							|  |  |  | 	bytesRead int | 
					
						
							| 
									
										
										
										
											2017-10-25 10:04:51 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (r *recordRequest) Read(p []byte) (n int, err error) { | 
					
						
							|  |  |  | 	n, err = r.Reader.Read(p) | 
					
						
							| 
									
										
										
										
											2019-07-19 06:29:17 +08:00
										 |  |  | 	r.bytesRead += n | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-25 10:04:51 +08:00
										 |  |  | 	if r.logBody { | 
					
						
							|  |  |  | 		r.buf.Write(p[:n]) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return n, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return n, err | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2019-07-19 06:29:17 +08:00
										 |  |  | func (r *recordRequest) Size() int { | 
					
						
							|  |  |  | 	sz := r.bytesRead | 
					
						
							|  |  |  | 	for k, v := range r.headers { | 
					
						
							|  |  |  | 		sz += len(k) + len(v) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return sz | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2017-10-25 10:04:51 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | // Return the bytes that were recorded.
 | 
					
						
							|  |  |  | func (r *recordRequest) Data() []byte { | 
					
						
							| 
									
										
										
										
											2019-07-11 02:49:02 +08:00
										 |  |  | 	// If body logging is enabled then we return the actual body
 | 
					
						
							|  |  |  | 	if r.logBody { | 
					
						
							|  |  |  | 		return r.buf.Bytes() | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// ... otherwise we return <BODY> placeholder
 | 
					
						
							| 
									
										
										
										
											2019-09-24 04:34:28 +08:00
										 |  |  | 	return logger.BodyPlaceHolder | 
					
						
							| 
									
										
										
										
											2017-10-25 10:04:51 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-19 04:55:13 +08:00
										 |  |  | // getOpName sanitizes the operation name for mc
 | 
					
						
							|  |  |  | func getOpName(name string) (op string) { | 
					
						
							|  |  |  | 	op = strings.TrimPrefix(name, "github.com/minio/minio/cmd.") | 
					
						
							|  |  |  | 	op = strings.TrimSuffix(op, "Handler-fm") | 
					
						
							|  |  |  | 	op = strings.Replace(op, "objectAPIHandlers", "s3", 1) | 
					
						
							|  |  |  | 	op = strings.Replace(op, "webAPIHandlers", "s3", 1) | 
					
						
							|  |  |  | 	op = strings.Replace(op, "adminAPIHandlers", "admin", 1) | 
					
						
							|  |  |  | 	op = strings.Replace(op, "(*storageRESTServer)", "internal", 1) | 
					
						
							|  |  |  | 	op = strings.Replace(op, "(*peerRESTServer)", "internal", 1) | 
					
						
							|  |  |  | 	op = strings.Replace(op, "(*lockRESTServer)", "internal", 1) | 
					
						
							|  |  |  | 	op = strings.Replace(op, "stsAPIHandlers", "sts", 1) | 
					
						
							|  |  |  | 	op = strings.Replace(op, "LivenessCheckHandler", "healthcheck", 1) | 
					
						
							|  |  |  | 	op = strings.Replace(op, "ReadinessCheckHandler", "healthcheck", 1) | 
					
						
							|  |  |  | 	return op | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-09 06:54:41 +08:00
										 |  |  | // Trace gets trace of http request
 | 
					
						
							|  |  |  | func Trace(f http.HandlerFunc, logBody bool, w http.ResponseWriter, r *http.Request) trace.Info { | 
					
						
							| 
									
										
										
										
											2019-06-19 04:55:13 +08:00
										 |  |  | 	name := getOpName(runtime.FuncForPC(reflect.ValueOf(f).Pointer()).Name()) | 
					
						
							| 
									
										
										
										
											2019-06-09 06:54:41 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-19 06:29:17 +08:00
										 |  |  | 	// Setup a http request body recorder
 | 
					
						
							| 
									
										
										
										
											2019-09-12 01:21:43 +08:00
										 |  |  | 	reqHeaders := r.Header.Clone() | 
					
						
							| 
									
										
										
										
											2019-07-19 06:29:17 +08:00
										 |  |  | 	reqHeaders.Set("Host", r.Host) | 
					
						
							| 
									
										
										
										
											2019-09-28 01:19:27 +08:00
										 |  |  | 	if len(r.TransferEncoding) == 0 { | 
					
						
							|  |  |  | 		reqHeaders.Set("Content-Length", strconv.Itoa(int(r.ContentLength))) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-07-19 06:29:17 +08:00
										 |  |  | 	for _, enc := range r.TransferEncoding { | 
					
						
							|  |  |  | 		reqHeaders.Add("Transfer-Encoding", enc) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-09 06:54:41 +08:00
										 |  |  | 	var reqBodyRecorder *recordRequest | 
					
						
							|  |  |  | 	t := trace.Info{FuncName: name} | 
					
						
							| 
									
										
										
										
											2019-07-19 06:29:17 +08:00
										 |  |  | 	reqBodyRecorder = &recordRequest{Reader: r.Body, logBody: logBody, headers: reqHeaders} | 
					
						
							| 
									
										
										
										
											2019-06-09 06:54:41 +08:00
										 |  |  | 	r.Body = ioutil.NopCloser(reqBodyRecorder) | 
					
						
							| 
									
										
										
										
											2019-07-06 11:41:35 +08:00
										 |  |  | 	t.NodeName = r.Host | 
					
						
							|  |  |  | 	if globalIsDistXL { | 
					
						
							|  |  |  | 		t.NodeName = GetLocalPeer(globalEndpoints) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// strip port from the host address
 | 
					
						
							|  |  |  | 	if host, _, err := net.SplitHostPort(t.NodeName); err == nil { | 
					
						
							|  |  |  | 		t.NodeName = host | 
					
						
							| 
									
										
										
										
											2019-06-09 06:54:41 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-07-06 11:41:35 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-11 02:49:02 +08:00
										 |  |  | 	rq := trace.RequestInfo{ | 
					
						
							|  |  |  | 		Time:     time.Now().UTC(), | 
					
						
							|  |  |  | 		Method:   r.Method, | 
					
						
							|  |  |  | 		Path:     r.URL.Path, | 
					
						
							|  |  |  | 		RawQuery: r.URL.RawQuery, | 
					
						
							| 
									
										
										
										
											2019-09-13 06:29:59 +08:00
										 |  |  | 		Client:   handlers.GetSourceIP(r), | 
					
						
							| 
									
										
										
										
											2019-07-11 02:49:02 +08:00
										 |  |  | 		Headers:  reqHeaders, | 
					
						
							|  |  |  | 		Body:     reqBodyRecorder.Data(), | 
					
						
							| 
									
										
										
										
											2019-06-09 06:54:41 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-10-27 23:49:16 +08:00
										 |  |  | 	rw := logger.NewResponseWriter(w) | 
					
						
							| 
									
										
										
										
											2019-09-24 04:34:28 +08:00
										 |  |  | 	rw.LogBody = logBody | 
					
						
							|  |  |  | 	f(rw, r) | 
					
						
							| 
									
										
										
										
											2019-06-09 06:54:41 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-11 02:49:02 +08:00
										 |  |  | 	rs := trace.ResponseInfo{ | 
					
						
							|  |  |  | 		Time:       time.Now().UTC(), | 
					
						
							| 
									
										
										
										
											2019-09-24 04:34:28 +08:00
										 |  |  | 		Headers:    rw.Header().Clone(), | 
					
						
							|  |  |  | 		StatusCode: rw.StatusCode, | 
					
						
							|  |  |  | 		Body:       rw.Body(), | 
					
						
							| 
									
										
										
										
											2019-07-11 02:49:02 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-09 06:54:41 +08:00
										 |  |  | 	if rs.StatusCode == 0 { | 
					
						
							|  |  |  | 		rs.StatusCode = http.StatusOK | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-07-11 02:49:02 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-09 06:54:41 +08:00
										 |  |  | 	t.ReqInfo = rq | 
					
						
							|  |  |  | 	t.RespInfo = rs | 
					
						
							| 
									
										
										
										
											2019-07-19 06:29:17 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-16 07:17:46 +08:00
										 |  |  | 	t.CallStats = trace.CallStats{ | 
					
						
							| 
									
										
										
										
											2019-09-24 04:34:28 +08:00
										 |  |  | 		Latency:         rs.Time.Sub(rw.StartTime), | 
					
						
							|  |  |  | 		InputBytes:      reqBodyRecorder.Size(), | 
					
						
							|  |  |  | 		OutputBytes:     rw.Size(), | 
					
						
							|  |  |  | 		TimeToFirstByte: rw.TimeToFirstByte, | 
					
						
							| 
									
										
										
										
											2019-08-16 07:17:46 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-06-09 06:54:41 +08:00
										 |  |  | 	return t | 
					
						
							| 
									
										
										
										
											2017-10-25 10:04:51 +08:00
										 |  |  | } |