| 
									
										
										
										
											2021-04-19 03:41:13 +08:00
										 |  |  | // Copyright (c) 2015-2021 MinIO, Inc.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // This file is part of MinIO Object Storage stack
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // This program is free software: you can redistribute it and/or modify
 | 
					
						
							|  |  |  | // it under the terms of the GNU Affero General Public License as published by
 | 
					
						
							|  |  |  | // the Free Software Foundation, either version 3 of the License, or
 | 
					
						
							|  |  |  | // (at your option) any later version.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // This program is distributed in the hope that it will be useful
 | 
					
						
							|  |  |  | // but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
					
						
							|  |  |  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
					
						
							|  |  |  | // GNU Affero General Public License for more details.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // You should have received a copy of the GNU Affero General Public License
 | 
					
						
							|  |  |  | // along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
					
						
							| 
									
										
										
										
											2017-04-07 14:08:33 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | package cmd | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"net/http" | 
					
						
							| 
									
										
										
										
											2024-03-05 02:05:56 +08:00
										 |  |  | 	"strings" | 
					
						
							| 
									
										
										
										
											2019-10-23 12:01:14 +08:00
										 |  |  | 	"sync" | 
					
						
							| 
									
										
										
										
											2020-05-04 13:35:40 +08:00
										 |  |  | 	"sync/atomic" | 
					
						
							| 
									
										
										
										
											2017-04-07 14:08:33 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-29 02:20:27 +08:00
										 |  |  | 	xhttp "github.com/minio/minio/internal/http" | 
					
						
							| 
									
										
										
										
											2018-04-19 07:01:42 +08:00
										 |  |  | 	"github.com/prometheus/client_golang/prometheus" | 
					
						
							| 
									
										
										
										
											2017-04-07 14:08:33 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-08 22:35:11 +08:00
										 |  |  | // connStats - Network statistics
 | 
					
						
							| 
									
										
										
										
											2017-04-07 14:08:33 +08:00
										 |  |  | // Count total input/output transferred bytes during
 | 
					
						
							|  |  |  | // the server's life.
 | 
					
						
							| 
									
										
										
										
											2023-07-08 22:35:11 +08:00
										 |  |  | type connStats struct { | 
					
						
							|  |  |  | 	internodeInputBytes  uint64 | 
					
						
							|  |  |  | 	internodeOutputBytes uint64 | 
					
						
							|  |  |  | 	s3InputBytes         uint64 | 
					
						
							|  |  |  | 	s3OutputBytes        uint64 | 
					
						
							| 
									
										
										
										
											2017-04-07 14:08:33 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-15 06:14:24 +08:00
										 |  |  | // Increase internode total input bytes
 | 
					
						
							| 
									
										
										
										
											2023-07-08 22:35:11 +08:00
										 |  |  | func (s *connStats) incInternodeInputBytes(n int64) { | 
					
						
							|  |  |  | 	atomic.AddUint64(&s.internodeInputBytes, uint64(n)) | 
					
						
							| 
									
										
										
										
											2017-04-07 14:08:33 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-15 06:14:24 +08:00
										 |  |  | // Increase internode total output bytes
 | 
					
						
							| 
									
										
										
										
											2023-07-08 22:35:11 +08:00
										 |  |  | func (s *connStats) incInternodeOutputBytes(n int64) { | 
					
						
							|  |  |  | 	atomic.AddUint64(&s.internodeOutputBytes, uint64(n)) | 
					
						
							| 
									
										
										
										
											2017-04-07 14:08:33 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-15 06:14:24 +08:00
										 |  |  | // Return internode total input bytes
 | 
					
						
							| 
									
										
										
										
											2023-07-08 22:35:11 +08:00
										 |  |  | func (s *connStats) getInternodeInputBytes() uint64 { | 
					
						
							|  |  |  | 	return atomic.LoadUint64(&s.internodeInputBytes) | 
					
						
							| 
									
										
										
										
											2017-04-07 14:08:33 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Return total output bytes
 | 
					
						
							| 
									
										
										
										
											2023-07-08 22:35:11 +08:00
										 |  |  | func (s *connStats) getInternodeOutputBytes() uint64 { | 
					
						
							|  |  |  | 	return atomic.LoadUint64(&s.internodeOutputBytes) | 
					
						
							| 
									
										
										
										
											2017-04-07 14:08:33 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-15 06:14:24 +08:00
										 |  |  | // Increase S3 total input bytes
 | 
					
						
							| 
									
										
										
										
											2023-07-08 22:35:11 +08:00
										 |  |  | func (s *connStats) incS3InputBytes(n int64) { | 
					
						
							| 
									
										
										
										
											2020-05-04 13:35:40 +08:00
										 |  |  | 	atomic.AddUint64(&s.s3InputBytes, uint64(n)) | 
					
						
							| 
									
										
										
										
											2019-10-23 12:01:14 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-15 06:14:24 +08:00
										 |  |  | // Increase S3 total output bytes
 | 
					
						
							| 
									
										
										
										
											2023-07-08 22:35:11 +08:00
										 |  |  | func (s *connStats) incS3OutputBytes(n int64) { | 
					
						
							| 
									
										
										
										
											2020-05-04 13:35:40 +08:00
										 |  |  | 	atomic.AddUint64(&s.s3OutputBytes, uint64(n)) | 
					
						
							| 
									
										
										
										
											2019-10-23 12:01:14 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-15 06:14:24 +08:00
										 |  |  | // Return S3 total input bytes
 | 
					
						
							| 
									
										
										
										
											2023-07-08 22:35:11 +08:00
										 |  |  | func (s *connStats) getS3InputBytes() uint64 { | 
					
						
							| 
									
										
										
										
											2020-05-04 13:35:40 +08:00
										 |  |  | 	return atomic.LoadUint64(&s.s3InputBytes) | 
					
						
							| 
									
										
										
										
											2019-10-23 12:01:14 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-15 06:14:24 +08:00
										 |  |  | // Return S3 total output bytes
 | 
					
						
							| 
									
										
										
										
											2023-07-08 22:35:11 +08:00
										 |  |  | func (s *connStats) getS3OutputBytes() uint64 { | 
					
						
							| 
									
										
										
										
											2020-05-04 13:35:40 +08:00
										 |  |  | 	return atomic.LoadUint64(&s.s3OutputBytes) | 
					
						
							| 
									
										
										
										
											2019-10-23 12:01:14 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Return connection stats (total input/output bytes and total s3 input/output bytes)
 | 
					
						
							| 
									
										
										
										
											2023-07-08 22:35:11 +08:00
										 |  |  | func (s *connStats) toServerConnStats() serverConnStats { | 
					
						
							|  |  |  | 	return serverConnStats{ | 
					
						
							|  |  |  | 		internodeInputBytes:  s.getInternodeInputBytes(),  // Traffic internode received
 | 
					
						
							|  |  |  | 		internodeOutputBytes: s.getInternodeOutputBytes(), // Traffic internode sent
 | 
					
						
							|  |  |  | 		s3InputBytes:         s.getS3InputBytes(),         // Traffic S3 received
 | 
					
						
							|  |  |  | 		s3OutputBytes:        s.getS3OutputBytes(),        // Traffic S3 sent
 | 
					
						
							| 
									
										
										
										
											2017-04-21 22:15:53 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-07 14:08:33 +08:00
										 |  |  | // Prepare new ConnStats structure
 | 
					
						
							| 
									
										
										
										
											2023-07-08 22:35:11 +08:00
										 |  |  | func newConnStats() *connStats { | 
					
						
							|  |  |  | 	return &connStats{} | 
					
						
							| 
									
										
										
										
											2017-04-07 14:08:33 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-15 06:14:24 +08:00
										 |  |  | type bucketS3RXTX struct { | 
					
						
							|  |  |  | 	s3InputBytes  uint64 | 
					
						
							|  |  |  | 	s3OutputBytes uint64 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-22 00:41:59 +08:00
										 |  |  | type bucketHTTPAPIStats struct { | 
					
						
							|  |  |  | 	currentS3Requests *HTTPAPIStats | 
					
						
							|  |  |  | 	totalS3Requests   *HTTPAPIStats | 
					
						
							|  |  |  | 	totalS34xxErrors  *HTTPAPIStats | 
					
						
							|  |  |  | 	totalS35xxErrors  *HTTPAPIStats | 
					
						
							|  |  |  | 	totalS3Canceled   *HTTPAPIStats | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type bucketHTTPStats struct { | 
					
						
							|  |  |  | 	sync.RWMutex | 
					
						
							|  |  |  | 	httpStats map[string]bucketHTTPAPIStats | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func newBucketHTTPStats() *bucketHTTPStats { | 
					
						
							|  |  |  | 	return &bucketHTTPStats{ | 
					
						
							|  |  |  | 		httpStats: make(map[string]bucketHTTPAPIStats), | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (bh *bucketHTTPStats) delete(bucket string) { | 
					
						
							|  |  |  | 	bh.Lock() | 
					
						
							|  |  |  | 	defer bh.Unlock() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	delete(bh.httpStats, bucket) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (bh *bucketHTTPStats) updateHTTPStats(bucket, api string, w *xhttp.ResponseRecorder) { | 
					
						
							|  |  |  | 	if bh == nil { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-19 13:25:12 +08:00
										 |  |  | 	if w != nil { | 
					
						
							|  |  |  | 		// Increment the prometheus http request response histogram with API, Bucket
 | 
					
						
							|  |  |  | 		bucketHTTPRequestsDuration.With(prometheus.Labels{ | 
					
						
							|  |  |  | 			"api":    api, | 
					
						
							|  |  |  | 			"bucket": bucket, | 
					
						
							|  |  |  | 		}).Observe(w.TimeToFirstByte.Seconds()) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-22 00:41:59 +08:00
										 |  |  | 	bh.Lock() | 
					
						
							|  |  |  | 	defer bh.Unlock() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	hstats, ok := bh.httpStats[bucket] | 
					
						
							|  |  |  | 	if !ok { | 
					
						
							|  |  |  | 		hstats = bucketHTTPAPIStats{ | 
					
						
							|  |  |  | 			currentS3Requests: &HTTPAPIStats{}, | 
					
						
							|  |  |  | 			totalS3Requests:   &HTTPAPIStats{}, | 
					
						
							|  |  |  | 			totalS3Canceled:   &HTTPAPIStats{}, | 
					
						
							|  |  |  | 			totalS34xxErrors:  &HTTPAPIStats{}, | 
					
						
							|  |  |  | 			totalS35xxErrors:  &HTTPAPIStats{}, | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if w == nil { // when response recorder nil, this is an active request
 | 
					
						
							|  |  |  | 		hstats.currentS3Requests.Inc(api) | 
					
						
							|  |  |  | 		bh.httpStats[bucket] = hstats | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} // else {
 | 
					
						
							|  |  |  | 	hstats.currentS3Requests.Dec(api) // decrement this once we have the response recorder.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	hstats.totalS3Requests.Inc(api) | 
					
						
							|  |  |  | 	code := w.StatusCode | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	switch { | 
					
						
							|  |  |  | 	case code == 0: | 
					
						
							|  |  |  | 	case code == 499: | 
					
						
							|  |  |  | 		// 499 is a good error, shall be counted as canceled.
 | 
					
						
							|  |  |  | 		hstats.totalS3Canceled.Inc(api) | 
					
						
							|  |  |  | 	case code >= http.StatusBadRequest: | 
					
						
							|  |  |  | 		if code >= http.StatusInternalServerError { | 
					
						
							|  |  |  | 			hstats.totalS35xxErrors.Inc(api) | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			hstats.totalS34xxErrors.Inc(api) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	bh.httpStats[bucket] = hstats | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (bh *bucketHTTPStats) load(bucket string) bucketHTTPAPIStats { | 
					
						
							|  |  |  | 	if bh == nil { | 
					
						
							|  |  |  | 		return bucketHTTPAPIStats{ | 
					
						
							|  |  |  | 			currentS3Requests: &HTTPAPIStats{}, | 
					
						
							|  |  |  | 			totalS3Requests:   &HTTPAPIStats{}, | 
					
						
							|  |  |  | 			totalS3Canceled:   &HTTPAPIStats{}, | 
					
						
							|  |  |  | 			totalS34xxErrors:  &HTTPAPIStats{}, | 
					
						
							|  |  |  | 			totalS35xxErrors:  &HTTPAPIStats{}, | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	bh.RLock() | 
					
						
							|  |  |  | 	defer bh.RUnlock() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	val, ok := bh.httpStats[bucket] | 
					
						
							|  |  |  | 	if ok { | 
					
						
							|  |  |  | 		return val | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return bucketHTTPAPIStats{ | 
					
						
							|  |  |  | 		currentS3Requests: &HTTPAPIStats{}, | 
					
						
							|  |  |  | 		totalS3Requests:   &HTTPAPIStats{}, | 
					
						
							|  |  |  | 		totalS3Canceled:   &HTTPAPIStats{}, | 
					
						
							|  |  |  | 		totalS34xxErrors:  &HTTPAPIStats{}, | 
					
						
							|  |  |  | 		totalS35xxErrors:  &HTTPAPIStats{}, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-15 06:14:24 +08:00
										 |  |  | type bucketConnStats struct { | 
					
						
							|  |  |  | 	sync.RWMutex | 
					
						
							|  |  |  | 	stats map[string]*bucketS3RXTX | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func newBucketConnStats() *bucketConnStats { | 
					
						
							|  |  |  | 	return &bucketConnStats{ | 
					
						
							|  |  |  | 		stats: make(map[string]*bucketS3RXTX), | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Increase S3 total input bytes for input bucket
 | 
					
						
							|  |  |  | func (s *bucketConnStats) incS3InputBytes(bucket string, n int64) { | 
					
						
							|  |  |  | 	s.Lock() | 
					
						
							|  |  |  | 	defer s.Unlock() | 
					
						
							|  |  |  | 	stats, ok := s.stats[bucket] | 
					
						
							|  |  |  | 	if !ok { | 
					
						
							|  |  |  | 		stats = &bucketS3RXTX{ | 
					
						
							|  |  |  | 			s3InputBytes: uint64(n), | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		stats.s3InputBytes += uint64(n) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	s.stats[bucket] = stats | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Increase S3 total output bytes for input bucket
 | 
					
						
							|  |  |  | func (s *bucketConnStats) incS3OutputBytes(bucket string, n int64) { | 
					
						
							|  |  |  | 	s.Lock() | 
					
						
							|  |  |  | 	defer s.Unlock() | 
					
						
							|  |  |  | 	stats, ok := s.stats[bucket] | 
					
						
							|  |  |  | 	if !ok { | 
					
						
							|  |  |  | 		stats = &bucketS3RXTX{ | 
					
						
							|  |  |  | 			s3OutputBytes: uint64(n), | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		stats.s3OutputBytes += uint64(n) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	s.stats[bucket] = stats | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-11 22:46:24 +08:00
										 |  |  | type inOutBytes struct { | 
					
						
							|  |  |  | 	In  uint64 | 
					
						
							|  |  |  | 	Out uint64 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-15 06:14:24 +08:00
										 |  |  | // Return S3 total input bytes for input bucket
 | 
					
						
							| 
									
										
										
										
											2023-07-11 22:46:24 +08:00
										 |  |  | func (s *bucketConnStats) getS3InOutBytes() map[string]inOutBytes { | 
					
						
							| 
									
										
										
										
											2022-06-15 06:14:24 +08:00
										 |  |  | 	s.RLock() | 
					
						
							|  |  |  | 	defer s.RUnlock() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-11 22:46:24 +08:00
										 |  |  | 	if len(s.stats) == 0 { | 
					
						
							|  |  |  | 		return nil | 
					
						
							| 
									
										
										
										
											2022-06-15 06:14:24 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-11 22:46:24 +08:00
										 |  |  | 	bucketStats := make(map[string]inOutBytes, len(s.stats)) | 
					
						
							|  |  |  | 	for k, v := range s.stats { | 
					
						
							|  |  |  | 		bucketStats[k] = inOutBytes{ | 
					
						
							|  |  |  | 			In:  v.s3InputBytes, | 
					
						
							|  |  |  | 			Out: v.s3OutputBytes, | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2022-06-15 06:14:24 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-07-11 22:46:24 +08:00
										 |  |  | 	return bucketStats | 
					
						
							| 
									
										
										
										
											2022-06-15 06:14:24 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-10 17:15:15 +08:00
										 |  |  | // Return S3 total input/output bytes for each
 | 
					
						
							|  |  |  | func (s *bucketConnStats) getBucketS3InOutBytes(buckets []string) map[string]inOutBytes { | 
					
						
							|  |  |  | 	s.RLock() | 
					
						
							|  |  |  | 	defer s.RUnlock() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if len(s.stats) == 0 || len(buckets) == 0 { | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	bucketStats := make(map[string]inOutBytes, len(buckets)) | 
					
						
							|  |  |  | 	for _, bucket := range buckets { | 
					
						
							|  |  |  | 		if stats, ok := s.stats[bucket]; ok { | 
					
						
							|  |  |  | 			bucketStats[bucket] = inOutBytes{ | 
					
						
							|  |  |  | 				In:  stats.s3InputBytes, | 
					
						
							|  |  |  | 				Out: stats.s3OutputBytes, | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return bucketStats | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-15 06:14:24 +08:00
										 |  |  | // delete metrics once bucket is deleted.
 | 
					
						
							|  |  |  | func (s *bucketConnStats) delete(bucket string) { | 
					
						
							|  |  |  | 	s.Lock() | 
					
						
							|  |  |  | 	defer s.Unlock() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	delete(s.stats, bucket) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-23 12:01:14 +08:00
										 |  |  | // HTTPAPIStats holds statistics information about
 | 
					
						
							|  |  |  | // a given API in the requests.
 | 
					
						
							|  |  |  | type HTTPAPIStats struct { | 
					
						
							| 
									
										
										
										
											2020-01-16 17:35:30 +08:00
										 |  |  | 	apiStats map[string]int | 
					
						
							| 
									
										
										
										
											2019-10-23 12:01:14 +08:00
										 |  |  | 	sync.RWMutex | 
					
						
							| 
									
										
										
										
											2017-04-07 14:08:33 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-23 12:01:14 +08:00
										 |  |  | // Inc increments the api stats counter.
 | 
					
						
							|  |  |  | func (stats *HTTPAPIStats) Inc(api string) { | 
					
						
							|  |  |  | 	if stats == nil { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-03-19 07:19:29 +08:00
										 |  |  | 	stats.Lock() | 
					
						
							|  |  |  | 	defer stats.Unlock() | 
					
						
							| 
									
										
										
										
											2020-01-16 17:35:30 +08:00
										 |  |  | 	if stats.apiStats == nil { | 
					
						
							|  |  |  | 		stats.apiStats = make(map[string]int) | 
					
						
							| 
									
										
										
										
											2019-10-23 12:01:14 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-03-19 07:19:29 +08:00
										 |  |  | 	stats.apiStats[api]++ | 
					
						
							| 
									
										
										
										
											2019-10-23 12:01:14 +08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2017-04-07 14:08:33 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-23 12:01:14 +08:00
										 |  |  | // Dec increments the api stats counter.
 | 
					
						
							|  |  |  | func (stats *HTTPAPIStats) Dec(api string) { | 
					
						
							|  |  |  | 	if stats == nil { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-03-19 07:19:29 +08:00
										 |  |  | 	stats.Lock() | 
					
						
							|  |  |  | 	defer stats.Unlock() | 
					
						
							| 
									
										
										
										
											2020-01-16 17:35:30 +08:00
										 |  |  | 	if val, ok := stats.apiStats[api]; ok && val > 0 { | 
					
						
							|  |  |  | 		stats.apiStats[api]-- | 
					
						
							| 
									
										
										
										
											2019-10-23 12:01:14 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2017-04-07 14:08:33 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-22 00:41:59 +08:00
										 |  |  | // Get returns the current counter on input API string
 | 
					
						
							|  |  |  | func (stats *HTTPAPIStats) Get(api string) int { | 
					
						
							|  |  |  | 	if stats == nil { | 
					
						
							|  |  |  | 		return 0 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	stats.RLock() | 
					
						
							|  |  |  | 	defer stats.RUnlock() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	val, ok := stats.apiStats[api] | 
					
						
							|  |  |  | 	if ok { | 
					
						
							|  |  |  | 		return val | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-23 12:01:14 +08:00
										 |  |  | // Load returns the recorded stats.
 | 
					
						
							| 
									
										
										
										
											2024-03-05 02:05:56 +08:00
										 |  |  | func (stats *HTTPAPIStats) Load(toLower bool) map[string]int { | 
					
						
							| 
									
										
										
										
											2023-06-22 00:41:59 +08:00
										 |  |  | 	if stats == nil { | 
					
						
							|  |  |  | 		return map[string]int{} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	stats.RLock() | 
					
						
							|  |  |  | 	defer stats.RUnlock() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-03 01:15:06 +08:00
										 |  |  | 	apiStats := make(map[string]int, len(stats.apiStats)) | 
					
						
							| 
									
										
										
										
											2020-01-16 17:35:30 +08:00
										 |  |  | 	for k, v := range stats.apiStats { | 
					
						
							| 
									
										
										
										
											2024-03-05 02:05:56 +08:00
										 |  |  | 		if toLower { | 
					
						
							|  |  |  | 			k = strings.ToLower(k) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-01-16 17:35:30 +08:00
										 |  |  | 		apiStats[k] = v | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return apiStats | 
					
						
							| 
									
										
										
										
											2019-10-23 12:01:14 +08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2017-04-07 14:08:33 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-23 12:01:14 +08:00
										 |  |  | // HTTPStats holds statistics information about
 | 
					
						
							|  |  |  | // HTTP requests made by all clients
 | 
					
						
							|  |  |  | type HTTPStats struct { | 
					
						
							| 
									
										
										
										
											2022-02-18 07:22:26 +08:00
										 |  |  | 	s3RequestsInQueue       int32 // ref: https://golang.org/pkg/sync/atomic/#pkg-note-BUG
 | 
					
						
							|  |  |  | 	_                       int32 // For 64 bits alignment
 | 
					
						
							|  |  |  | 	s3RequestsIncoming      uint64 | 
					
						
							| 
									
										
										
										
											2021-06-23 22:15:43 +08:00
										 |  |  | 	rejectedRequestsAuth    uint64 | 
					
						
							|  |  |  | 	rejectedRequestsTime    uint64 | 
					
						
							|  |  |  | 	rejectedRequestsHeader  uint64 | 
					
						
							|  |  |  | 	rejectedRequestsInvalid uint64 | 
					
						
							| 
									
										
										
										
											2021-03-31 14:19:36 +08:00
										 |  |  | 	currentS3Requests       HTTPAPIStats | 
					
						
							|  |  |  | 	totalS3Requests         HTTPAPIStats | 
					
						
							|  |  |  | 	totalS3Errors           HTTPAPIStats | 
					
						
							| 
									
										
										
										
											2022-06-09 02:22:34 +08:00
										 |  |  | 	totalS34xxErrors        HTTPAPIStats | 
					
						
							|  |  |  | 	totalS35xxErrors        HTTPAPIStats | 
					
						
							| 
									
										
										
										
											2021-03-31 14:19:36 +08:00
										 |  |  | 	totalS3Canceled         HTTPAPIStats | 
					
						
							| 
									
										
										
										
											2017-04-07 14:08:33 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-28 00:34:52 +08:00
										 |  |  | func (st *HTTPStats) loadRequestsInQueue() int32 { | 
					
						
							|  |  |  | 	return atomic.LoadInt32(&st.s3RequestsInQueue) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-20 16:21:55 +08:00
										 |  |  | func (st *HTTPStats) addRequestsInQueue(i int32) { | 
					
						
							|  |  |  | 	atomic.AddInt32(&st.s3RequestsInQueue, i) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-08 08:30:14 +08:00
										 |  |  | func (st *HTTPStats) incS3RequestsIncoming() { | 
					
						
							|  |  |  | 	// Golang automatically resets to zero if this overflows
 | 
					
						
							|  |  |  | 	atomic.AddUint64(&st.s3RequestsIncoming, 1) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-07 14:08:33 +08:00
										 |  |  | // Converts http stats into struct to be sent back to the client.
 | 
					
						
							| 
									
										
										
										
											2024-03-05 02:05:56 +08:00
										 |  |  | func (st *HTTPStats) toServerHTTPStats(toLowerKeys bool) ServerHTTPStats { | 
					
						
							| 
									
										
										
										
											2017-04-07 14:08:33 +08:00
										 |  |  | 	serverStats := ServerHTTPStats{} | 
					
						
							| 
									
										
										
										
											2022-02-08 08:30:14 +08:00
										 |  |  | 	serverStats.S3RequestsIncoming = atomic.SwapUint64(&st.s3RequestsIncoming, 0) | 
					
						
							| 
									
										
										
										
											2021-02-20 16:21:55 +08:00
										 |  |  | 	serverStats.S3RequestsInQueue = atomic.LoadInt32(&st.s3RequestsInQueue) | 
					
						
							| 
									
										
										
										
											2021-03-31 14:19:36 +08:00
										 |  |  | 	serverStats.TotalS3RejectedAuth = atomic.LoadUint64(&st.rejectedRequestsAuth) | 
					
						
							|  |  |  | 	serverStats.TotalS3RejectedTime = atomic.LoadUint64(&st.rejectedRequestsTime) | 
					
						
							|  |  |  | 	serverStats.TotalS3RejectedHeader = atomic.LoadUint64(&st.rejectedRequestsHeader) | 
					
						
							|  |  |  | 	serverStats.TotalS3RejectedInvalid = atomic.LoadUint64(&st.rejectedRequestsInvalid) | 
					
						
							| 
									
										
										
										
											2019-10-23 12:01:14 +08:00
										 |  |  | 	serverStats.CurrentS3Requests = ServerHTTPAPIStats{ | 
					
						
							| 
									
										
										
										
											2024-03-05 02:05:56 +08:00
										 |  |  | 		APIStats: st.currentS3Requests.Load(toLowerKeys), | 
					
						
							| 
									
										
										
										
											2017-04-07 14:08:33 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-10-23 12:01:14 +08:00
										 |  |  | 	serverStats.TotalS3Requests = ServerHTTPAPIStats{ | 
					
						
							| 
									
										
										
										
											2024-03-05 02:05:56 +08:00
										 |  |  | 		APIStats: st.totalS3Requests.Load(toLowerKeys), | 
					
						
							| 
									
										
										
										
											2017-04-07 14:08:33 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-10-23 12:01:14 +08:00
										 |  |  | 	serverStats.TotalS3Errors = ServerHTTPAPIStats{ | 
					
						
							| 
									
										
										
										
											2024-03-05 02:05:56 +08:00
										 |  |  | 		APIStats: st.totalS3Errors.Load(toLowerKeys), | 
					
						
							| 
									
										
										
										
											2017-04-07 14:08:33 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2022-06-09 02:22:34 +08:00
										 |  |  | 	serverStats.TotalS34xxErrors = ServerHTTPAPIStats{ | 
					
						
							| 
									
										
										
										
											2024-03-05 02:05:56 +08:00
										 |  |  | 		APIStats: st.totalS34xxErrors.Load(toLowerKeys), | 
					
						
							| 
									
										
										
										
											2022-06-09 02:22:34 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	serverStats.TotalS35xxErrors = ServerHTTPAPIStats{ | 
					
						
							| 
									
										
										
										
											2024-03-05 02:05:56 +08:00
										 |  |  | 		APIStats: st.totalS35xxErrors.Load(toLowerKeys), | 
					
						
							| 
									
										
										
										
											2022-06-09 02:22:34 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-03-25 01:25:27 +08:00
										 |  |  | 	serverStats.TotalS3Canceled = ServerHTTPAPIStats{ | 
					
						
							| 
									
										
										
										
											2024-03-05 02:05:56 +08:00
										 |  |  | 		APIStats: st.totalS3Canceled.Load(toLowerKeys), | 
					
						
							| 
									
										
										
										
											2021-03-25 01:25:27 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-04-07 14:08:33 +08:00
										 |  |  | 	return serverStats | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Update statistics from http request and response data
 | 
					
						
							| 
									
										
										
										
											2023-04-13 05:37:19 +08:00
										 |  |  | func (st *HTTPStats) updateStats(api string, w *xhttp.ResponseRecorder) { | 
					
						
							| 
									
										
										
										
											2022-06-11 15:50:31 +08:00
										 |  |  | 	st.totalS3Requests.Inc(api) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-11 15:02:25 +08:00
										 |  |  | 	// Increment the prometheus http request response histogram with appropriate label
 | 
					
						
							|  |  |  | 	httpRequestsDuration.With(prometheus.Labels{"api": api}).Observe(w.TimeToFirstByte.Seconds()) | 
					
						
							| 
									
										
										
										
											2022-06-09 02:22:34 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	code := w.StatusCode | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	switch { | 
					
						
							|  |  |  | 	case code == 0: | 
					
						
							|  |  |  | 	case code == 499: | 
					
						
							|  |  |  | 		// 499 is a good error, shall be counted as canceled.
 | 
					
						
							|  |  |  | 		st.totalS3Canceled.Inc(api) | 
					
						
							|  |  |  | 	case code >= http.StatusBadRequest: | 
					
						
							|  |  |  | 		st.totalS3Errors.Inc(api) | 
					
						
							|  |  |  | 		if code >= http.StatusInternalServerError { | 
					
						
							|  |  |  | 			st.totalS35xxErrors.Inc(api) | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			st.totalS34xxErrors.Inc(api) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-04-07 14:08:33 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Prepare new HTTPStats structure
 | 
					
						
							|  |  |  | func newHTTPStats() *HTTPStats { | 
					
						
							|  |  |  | 	return &HTTPStats{} | 
					
						
							|  |  |  | } |