You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
189 lines
5.7 KiB
189 lines
5.7 KiB
8 years ago
|
/*
|
||
|
* Minio Cloud Storage, (C) 2017 Minio, Inc.
|
||
|
*
|
||
|
* 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.
|
||
|
*/
|
||
|
|
||
|
package cmd
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"net/http"
|
||
|
"time"
|
||
|
|
||
|
"go.uber.org/atomic"
|
||
|
)
|
||
|
|
||
|
// ConnStats - Network statistics
|
||
|
// Count total input/output transferred bytes during
|
||
|
// the server's life.
|
||
|
type ConnStats struct {
|
||
|
totalInputBytes atomic.Uint64
|
||
|
totalOutputBytes atomic.Uint64
|
||
|
}
|
||
|
|
||
|
// Increase total input bytes
|
||
|
func (s *ConnStats) incInputBytes(n int) {
|
||
|
s.totalInputBytes.Add(uint64(n))
|
||
|
}
|
||
|
|
||
|
// Increase total output bytes
|
||
|
func (s *ConnStats) incOutputBytes(n int) {
|
||
|
s.totalOutputBytes.Add(uint64(n))
|
||
|
}
|
||
|
|
||
|
// Return total input bytes
|
||
|
func (s *ConnStats) getTotalInputBytes() uint64 {
|
||
|
return s.totalInputBytes.Load()
|
||
|
}
|
||
|
|
||
|
// Return total output bytes
|
||
|
func (s *ConnStats) getTotalOutputBytes() uint64 {
|
||
|
return s.totalOutputBytes.Load()
|
||
|
}
|
||
|
|
||
|
// Prepare new ConnStats structure
|
||
|
func newConnStats() *ConnStats {
|
||
|
return &ConnStats{}
|
||
|
}
|
||
|
|
||
|
// HTTPMethodStats holds statistics information about
|
||
|
// a given HTTP method made by all clients
|
||
|
type HTTPMethodStats struct {
|
||
|
Counter atomic.Uint64
|
||
|
Duration atomic.Float64
|
||
|
}
|
||
|
|
||
|
// HTTPStats holds statistics information about
|
||
|
// HTTP requests made by all clients
|
||
|
type HTTPStats struct {
|
||
|
// HEAD request stats.
|
||
|
totalHEADs HTTPMethodStats
|
||
|
successHEADs HTTPMethodStats
|
||
|
|
||
|
// GET request stats.
|
||
|
totalGETs HTTPMethodStats
|
||
|
successGETs HTTPMethodStats
|
||
|
|
||
|
// PUT request stats.
|
||
|
totalPUTs HTTPMethodStats
|
||
|
successPUTs HTTPMethodStats
|
||
|
|
||
|
// POST request stats.
|
||
|
totalPOSTs HTTPMethodStats
|
||
|
successPOSTs HTTPMethodStats
|
||
|
|
||
|
// DELETE request stats.
|
||
|
totalDELETEs HTTPMethodStats
|
||
|
successDELETEs HTTPMethodStats
|
||
|
}
|
||
|
|
||
|
func durationStr(totalDuration, totalCount float64) string {
|
||
|
return fmt.Sprint(time.Duration(totalDuration/totalCount) * time.Second)
|
||
|
}
|
||
|
|
||
|
// Converts http stats into struct to be sent back to the client.
|
||
|
func (st HTTPStats) toServerHTTPStats() ServerHTTPStats {
|
||
|
serverStats := ServerHTTPStats{}
|
||
|
serverStats.TotalHEADStats = ServerHTTPMethodStats{
|
||
|
Count: st.totalHEADs.Counter.Load(),
|
||
|
AvgDuration: durationStr(st.totalHEADs.Duration.Load(), float64(st.totalHEADs.Counter.Load())),
|
||
|
}
|
||
|
serverStats.SuccessHEADStats = ServerHTTPMethodStats{
|
||
|
Count: st.successHEADs.Counter.Load(),
|
||
|
AvgDuration: durationStr(st.successHEADs.Duration.Load(), float64(st.successHEADs.Counter.Load())),
|
||
|
}
|
||
|
serverStats.TotalGETStats = ServerHTTPMethodStats{
|
||
|
Count: st.totalGETs.Counter.Load(),
|
||
|
AvgDuration: durationStr(st.totalGETs.Duration.Load(), float64(st.totalGETs.Counter.Load())),
|
||
|
}
|
||
|
serverStats.SuccessGETStats = ServerHTTPMethodStats{
|
||
|
Count: st.successGETs.Counter.Load(),
|
||
|
AvgDuration: durationStr(st.successGETs.Duration.Load(), float64(st.successGETs.Counter.Load())),
|
||
|
}
|
||
|
serverStats.TotalPUTStats = ServerHTTPMethodStats{
|
||
|
Count: st.totalPUTs.Counter.Load(),
|
||
|
AvgDuration: durationStr(st.totalPUTs.Duration.Load(), float64(st.totalPUTs.Counter.Load())),
|
||
|
}
|
||
|
serverStats.SuccessPUTStats = ServerHTTPMethodStats{
|
||
|
Count: st.successPUTs.Counter.Load(),
|
||
|
AvgDuration: durationStr(st.successPUTs.Duration.Load(), float64(st.successPUTs.Counter.Load())),
|
||
|
}
|
||
|
serverStats.TotalPOSTStats = ServerHTTPMethodStats{
|
||
|
Count: st.totalPOSTs.Counter.Load(),
|
||
|
AvgDuration: durationStr(st.totalPOSTs.Duration.Load(), float64(st.totalPOSTs.Counter.Load())),
|
||
|
}
|
||
|
serverStats.SuccessPOSTStats = ServerHTTPMethodStats{
|
||
|
Count: st.successPOSTs.Counter.Load(),
|
||
|
AvgDuration: durationStr(st.successPOSTs.Duration.Load(), float64(st.successPOSTs.Counter.Load())),
|
||
|
}
|
||
|
serverStats.TotalDELETEStats = ServerHTTPMethodStats{
|
||
|
Count: st.totalDELETEs.Counter.Load(),
|
||
|
AvgDuration: durationStr(st.totalDELETEs.Duration.Load(), float64(st.totalDELETEs.Counter.Load())),
|
||
|
}
|
||
|
serverStats.SuccessDELETEStats = ServerHTTPMethodStats{
|
||
|
Count: st.successDELETEs.Counter.Load(),
|
||
|
AvgDuration: durationStr(st.successDELETEs.Duration.Load(), float64(st.successDELETEs.Counter.Load())),
|
||
|
}
|
||
|
return serverStats
|
||
|
}
|
||
|
|
||
|
// Update statistics from http request and response data
|
||
|
func (st *HTTPStats) updateStats(r *http.Request, w *httpResponseRecorder, durationSecs float64) {
|
||
|
// A successful request has a 2xx response code
|
||
|
successReq := (w.respStatusCode >= 200 && w.respStatusCode < 300)
|
||
|
// Update stats according to method verb
|
||
|
switch r.Method {
|
||
|
case "HEAD":
|
||
|
st.totalHEADs.Counter.Inc()
|
||
|
st.totalHEADs.Duration.Add(durationSecs)
|
||
|
if successReq {
|
||
|
st.successHEADs.Counter.Inc()
|
||
|
st.successHEADs.Duration.Add(durationSecs)
|
||
|
}
|
||
|
case "GET":
|
||
|
st.totalGETs.Counter.Inc()
|
||
|
st.totalGETs.Duration.Add(durationSecs)
|
||
|
if successReq {
|
||
|
st.successGETs.Counter.Inc()
|
||
|
st.successGETs.Duration.Add(durationSecs)
|
||
|
}
|
||
|
case "PUT":
|
||
|
st.totalPUTs.Counter.Inc()
|
||
|
st.totalPUTs.Duration.Add(durationSecs)
|
||
|
if successReq {
|
||
|
st.successPUTs.Counter.Inc()
|
||
|
st.totalPUTs.Duration.Add(durationSecs)
|
||
|
}
|
||
|
case "POST":
|
||
|
st.totalPOSTs.Counter.Inc()
|
||
|
st.totalPOSTs.Duration.Add(durationSecs)
|
||
|
if successReq {
|
||
|
st.successPOSTs.Counter.Inc()
|
||
|
st.totalPOSTs.Duration.Add(durationSecs)
|
||
|
}
|
||
|
case "DELETE":
|
||
|
st.totalDELETEs.Counter.Inc()
|
||
|
st.totalDELETEs.Duration.Add(durationSecs)
|
||
|
if successReq {
|
||
|
st.successDELETEs.Counter.Inc()
|
||
|
st.successDELETEs.Duration.Add(durationSecs)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Prepare new HTTPStats structure
|
||
|
func newHTTPStats() *HTTPStats {
|
||
|
return &HTTPStats{}
|
||
|
}
|