storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/cmd/http-stats.go (about) 1 /* 2 * MinIO Cloud Storage, (C) 2017 MinIO, Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package cmd 18 19 import ( 20 "net/http" 21 "strings" 22 "sync" 23 "sync/atomic" 24 25 "github.com/prometheus/client_golang/prometheus" 26 27 "storj.io/minio/cmd/logger" 28 ) 29 30 // ConnStats - Network statistics 31 // Count total input/output transferred bytes during 32 // the server's life. 33 type ConnStats struct { 34 totalInputBytes uint64 35 totalOutputBytes uint64 36 s3InputBytes uint64 37 s3OutputBytes uint64 38 } 39 40 // Increase total input bytes 41 func (s *ConnStats) incInputBytes(n int) { 42 atomic.AddUint64(&s.totalInputBytes, uint64(n)) 43 } 44 45 // Increase total output bytes 46 func (s *ConnStats) incOutputBytes(n int) { 47 atomic.AddUint64(&s.totalOutputBytes, uint64(n)) 48 } 49 50 // Return total input bytes 51 func (s *ConnStats) getTotalInputBytes() uint64 { 52 return atomic.LoadUint64(&s.totalInputBytes) 53 } 54 55 // Return total output bytes 56 func (s *ConnStats) getTotalOutputBytes() uint64 { 57 return atomic.LoadUint64(&s.totalOutputBytes) 58 } 59 60 // Increase outbound input bytes 61 func (s *ConnStats) incS3InputBytes(n int) { 62 atomic.AddUint64(&s.s3InputBytes, uint64(n)) 63 } 64 65 // Increase outbound output bytes 66 func (s *ConnStats) incS3OutputBytes(n int) { 67 atomic.AddUint64(&s.s3OutputBytes, uint64(n)) 68 } 69 70 // Return outbound input bytes 71 func (s *ConnStats) getS3InputBytes() uint64 { 72 return atomic.LoadUint64(&s.s3InputBytes) 73 } 74 75 // Return outbound output bytes 76 func (s *ConnStats) getS3OutputBytes() uint64 { 77 return atomic.LoadUint64(&s.s3OutputBytes) 78 } 79 80 // Return connection stats (total input/output bytes and total s3 input/output bytes) 81 func (s *ConnStats) toServerConnStats() ServerConnStats { 82 return ServerConnStats{ 83 TotalInputBytes: s.getTotalInputBytes(), // Traffic including reserved bucket 84 TotalOutputBytes: s.getTotalOutputBytes(), // Traffic including reserved bucket 85 S3InputBytes: s.getS3InputBytes(), // Traffic for client buckets 86 S3OutputBytes: s.getS3OutputBytes(), // Traffic for client buckets 87 } 88 } 89 90 // Prepare new ConnStats structure 91 func newConnStats() *ConnStats { 92 return &ConnStats{} 93 } 94 95 // HTTPAPIStats holds statistics information about 96 // a given API in the requests. 97 type HTTPAPIStats struct { 98 apiStats map[string]int 99 sync.RWMutex 100 } 101 102 // Inc increments the api stats counter. 103 func (stats *HTTPAPIStats) Inc(api string) { 104 if stats == nil { 105 return 106 } 107 stats.Lock() 108 defer stats.Unlock() 109 if stats.apiStats == nil { 110 stats.apiStats = make(map[string]int) 111 } 112 stats.apiStats[api]++ 113 } 114 115 // Dec increments the api stats counter. 116 func (stats *HTTPAPIStats) Dec(api string) { 117 if stats == nil { 118 return 119 } 120 stats.Lock() 121 defer stats.Unlock() 122 if val, ok := stats.apiStats[api]; ok && val > 0 { 123 stats.apiStats[api]-- 124 } 125 } 126 127 // Load returns the recorded stats. 128 func (stats *HTTPAPIStats) Load() map[string]int { 129 stats.Lock() 130 defer stats.Unlock() 131 var apiStats = make(map[string]int, len(stats.apiStats)) 132 for k, v := range stats.apiStats { 133 apiStats[k] = v 134 } 135 return apiStats 136 } 137 138 // HTTPStats holds statistics information about 139 // HTTP requests made by all clients 140 type HTTPStats struct { 141 s3RequestsInQueue int32 142 currentS3Requests HTTPAPIStats 143 totalS3Requests HTTPAPIStats 144 totalS3Errors HTTPAPIStats 145 totalS3Canceled HTTPAPIStats 146 rejectedRequestsAuth uint64 147 rejectedRequestsTime uint64 148 rejectedRequestsHeader uint64 149 rejectedRequestsInvalid uint64 150 } 151 152 func (st *HTTPStats) addRequestsInQueue(i int32) { 153 atomic.AddInt32(&st.s3RequestsInQueue, i) 154 } 155 156 // Converts http stats into struct to be sent back to the client. 157 func (st *HTTPStats) toServerHTTPStats() ServerHTTPStats { 158 serverStats := ServerHTTPStats{} 159 serverStats.S3RequestsInQueue = atomic.LoadInt32(&st.s3RequestsInQueue) 160 serverStats.TotalS3RejectedAuth = atomic.LoadUint64(&st.rejectedRequestsAuth) 161 serverStats.TotalS3RejectedTime = atomic.LoadUint64(&st.rejectedRequestsTime) 162 serverStats.TotalS3RejectedHeader = atomic.LoadUint64(&st.rejectedRequestsHeader) 163 serverStats.TotalS3RejectedInvalid = atomic.LoadUint64(&st.rejectedRequestsInvalid) 164 serverStats.CurrentS3Requests = ServerHTTPAPIStats{ 165 APIStats: st.currentS3Requests.Load(), 166 } 167 serverStats.TotalS3Requests = ServerHTTPAPIStats{ 168 APIStats: st.totalS3Requests.Load(), 169 } 170 serverStats.TotalS3Errors = ServerHTTPAPIStats{ 171 APIStats: st.totalS3Errors.Load(), 172 } 173 serverStats.TotalS3Canceled = ServerHTTPAPIStats{ 174 APIStats: st.totalS3Canceled.Load(), 175 } 176 return serverStats 177 } 178 179 // Update statistics from http request and response data 180 func (st *HTTPStats) updateStats(api string, r *http.Request, w *logger.ResponseWriter) { 181 // A successful request has a 2xx response code 182 successReq := w.StatusCode >= 200 && w.StatusCode < 300 183 184 if !strings.HasSuffix(r.URL.Path, prometheusMetricsPathLegacy) || 185 !strings.HasSuffix(r.URL.Path, prometheusMetricsV2ClusterPath) || 186 !strings.HasSuffix(r.URL.Path, prometheusMetricsV2NodePath) { 187 st.totalS3Requests.Inc(api) 188 if !successReq { 189 switch w.StatusCode { 190 case 0: 191 case 499: 192 // 499 is a good error, shall be counted at canceled. 193 st.totalS3Canceled.Inc(api) 194 default: 195 st.totalS3Errors.Inc(api) 196 } 197 } 198 } 199 200 // Increment the prometheus http request response histogram with appropriate label 201 httpRequestsDuration.With(prometheus.Labels{"api": api}).Observe(w.TimeToFirstByte.Seconds()) 202 } 203 204 // Prepare new HTTPStats structure 205 func newHTTPStats() *HTTPStats { 206 return &HTTPStats{} 207 }