1
0
Fork 0

Remove thoas/stats fork

This commit is contained in:
Julien Salleyron 2019-03-28 13:46:04 +01:00 committed by Traefiker Bot
parent 1bcb3d8cc2
commit 08d7bb0d08
5 changed files with 160 additions and 36 deletions

View file

@ -11,34 +11,58 @@ import (
// Stats data structure
type Stats struct {
mu sync.RWMutex
closed chan struct{}
Hostname string
Uptime time.Time
Pid int
ResponseCounts map[string]int
TotalResponseCounts map[string]int
TotalResponseTime time.Time
TotalResponseSize int64
MetricsCounts map[string]int
MetricsTimers map[string]time.Time
}
// Label data structure
type Label struct {
Name string
Value string
}
// New constructs a new Stats structure
func New() *Stats {
name, _ := os.Hostname()
stats := &Stats{
closed: make(chan struct{}, 1),
Uptime: time.Now(),
Pid: os.Getpid(),
ResponseCounts: map[string]int{},
TotalResponseCounts: map[string]int{},
TotalResponseTime: time.Time{},
Hostname: name,
}
go func() {
for {
stats.ResetResponseCounts()
select {
case <-stats.closed:
return
default:
stats.ResetResponseCounts()
time.Sleep(time.Second * 1)
time.Sleep(time.Second * 1)
}
}
}()
return stats
}
func (mw *Stats) Close() {
close(mw.closed)
}
// ResetResponseCounts reset the response counts
func (mw *Stats) ResetResponseCounts() {
mw.mu.Lock()
@ -53,7 +77,7 @@ func (mw *Stats) Handler(h http.Handler) http.Handler {
h.ServeHTTP(recorder, r)
mw.End(beginning, recorder)
mw.End(beginning, WithRecorder(recorder))
})
}
@ -63,7 +87,7 @@ func (mw *Stats) ServeHTTP(w http.ResponseWriter, r *http.Request, next http.Han
next(recorder, r)
mw.End(beginning, recorder)
mw.End(beginning, WithRecorder(recorder))
}
// Begin starts a recorder
@ -75,52 +99,73 @@ func (mw *Stats) Begin(w http.ResponseWriter) (time.Time, ResponseWriter) {
return start, writer
}
// EndWithStatus closes the recorder with a specific status
func (mw *Stats) EndWithStatus(start time.Time, status int) {
end := time.Now()
// End closes the recorder with a specific status
func (mw *Stats) End(start time.Time, opts ...Option) {
options := newOptions(opts...)
responseTime := end.Sub(start)
responseTime := time.Since(start)
mw.mu.Lock()
defer mw.mu.Unlock()
statusCode := fmt.Sprintf("%d", status)
mw.ResponseCounts[statusCode]++
mw.TotalResponseCounts[statusCode]++
mw.TotalResponseTime = mw.TotalResponseTime.Add(responseTime)
// If Hijacked connection do not count in response time
if options.StatusCode() != 0 {
statusCode := fmt.Sprintf("%d", options.StatusCode())
mw.ResponseCounts[statusCode]++
mw.TotalResponseCounts[statusCode]++
mw.TotalResponseTime = mw.TotalResponseTime.Add(responseTime)
mw.TotalResponseSize += int64(options.Size())
}
}
// End closes the recorder with the recorder status
func (mw *Stats) End(start time.Time, recorder ResponseWriter) {
mw.EndWithStatus(start, recorder.Status())
// MeasureSince method for execution time recording
func (mw *Stats) MeasureSince(key string, start time.Time) {
mw.MeasureSinceWithLabels(key, start, nil)
}
// MeasureSinceWithLabels method for execution time recording with custom labels
func (mw *Stats) MeasureSinceWithLabels(key string, start time.Time, labels []Label) {
labels = append(labels, Label{"host", mw.Hostname})
elapsed := time.Since(start)
mw.mu.Lock()
defer mw.mu.Unlock()
mw.MetricsCounts[key]++
mw.MetricsTimers[key] = mw.MetricsTimers[key].Add(elapsed)
}
// Data serializable structure
type Data struct {
Pid int `json:"pid"`
UpTime string `json:"uptime"`
UpTimeSec float64 `json:"uptime_sec"`
Time string `json:"time"`
TimeUnix int64 `json:"unixtime"`
StatusCodeCount map[string]int `json:"status_code_count"`
TotalStatusCodeCount map[string]int `json:"total_status_code_count"`
Count int `json:"count"`
TotalCount int `json:"total_count"`
TotalResponseTime string `json:"total_response_time"`
TotalResponseTimeSec float64 `json:"total_response_time_sec"`
AverageResponseTime string `json:"average_response_time"`
AverageResponseTimeSec float64 `json:"average_response_time_sec"`
Pid int `json:"pid"`
Hostname string `json:"hostname"`
UpTime string `json:"uptime"`
UpTimeSec float64 `json:"uptime_sec"`
Time string `json:"time"`
TimeUnix int64 `json:"unixtime"`
StatusCodeCount map[string]int `json:"status_code_count"`
TotalStatusCodeCount map[string]int `json:"total_status_code_count"`
Count int `json:"count"`
TotalCount int `json:"total_count"`
TotalResponseTime string `json:"total_response_time"`
TotalResponseTimeSec float64 `json:"total_response_time_sec"`
TotalResponseSize int64 `json:"total_response_size"`
AverageResponseSize int64 `json:"average_response_size"`
AverageResponseTime string `json:"average_response_time"`
AverageResponseTimeSec float64 `json:"average_response_time_sec"`
TotalMetricsCounts map[string]int `json:"total_metrics_counts"`
AverageMetricsTimers map[string]float64 `json:"average_metrics_timers"`
}
// Data returns the data serializable structure
func (mw *Stats) Data() *Data {
mw.mu.RLock()
responseCounts := make(map[string]int, len(mw.ResponseCounts))
totalResponseCounts := make(map[string]int, len(mw.TotalResponseCounts))
totalMetricsCounts := make(map[string]int, len(mw.MetricsCounts))
metricsCounts := make(map[string]float64, len(mw.MetricsCounts))
now := time.Now()
@ -139,11 +184,21 @@ func (mw *Stats) Data() *Data {
}
totalResponseTime := mw.TotalResponseTime.Sub(time.Time{})
totalResponseSize := mw.TotalResponseSize
averageResponseTime := time.Duration(0)
averageResponseSize := int64(0)
if totalCount > 0 {
avgNs := int64(totalResponseTime) / int64(totalCount)
averageResponseTime = time.Duration(avgNs)
averageResponseSize = int64(totalResponseSize) / int64(totalCount)
}
for key, count := range mw.MetricsCounts {
totalMetric := mw.MetricsTimers[key].Sub(time.Time{})
avgNs := int64(totalMetric) / int64(count)
metricsCounts[key] = time.Duration(avgNs).Seconds()
totalMetricsCounts[key] = count
}
mw.mu.RUnlock()
@ -159,9 +214,13 @@ func (mw *Stats) Data() *Data {
Count: count,
TotalCount: totalCount,
TotalResponseTime: totalResponseTime.String(),
TotalResponseSize: totalResponseSize,
TotalResponseTimeSec: totalResponseTime.Seconds(),
TotalMetricsCounts: totalMetricsCounts,
AverageResponseSize: averageResponseSize,
AverageResponseTime: averageResponseTime.String(),
AverageResponseTimeSec: averageResponseTime.Seconds(),
AverageMetricsTimers: metricsCounts,
}
return r