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

4
Gopkg.lock generated
View file

@ -1469,11 +1469,11 @@
revision = "c4434f09ec131ecf30f986d5dcb1636508bfa49a"
[[projects]]
digest = "1:c269070c7f286c941392076d6c42a4a7a062356f7083a48bd202397e877a965e"
digest = "1:84b9a5318d8ce3b8a9b1509bf15734f4f9dcd4decf9d9e9c7346a16c7b64d49e"
name = "github.com/thoas/stats"
packages = ["."]
pruneopts = "NUT"
revision = "152b5d051953fdb6e45f14b6826962aadc032324"
revision = "4975baf6a358ed3ddaa42133996e1959f96c9300"
[[projects]]
branch = "master"

View file

@ -85,9 +85,10 @@ required = [
name = "github.com/containous/staert"
version = "3.1.2"
#[[constraint]]
# name = "github.com/containous/traefik-extra-service-fabric"
# version = "1.3.0"
[[constraint]]
name = "github.com/thoas/stats"
# related to https://github.com/thoas/stats/pull/32
revision = "4975baf6a358ed3ddaa42133996e1959f96c9300"
[[constraint]]
name = "github.com/coreos/go-systemd"

59
vendor/github.com/thoas/stats/options.go generated vendored Normal file
View file

@ -0,0 +1,59 @@
package stats
// Options are stats options.
type Options struct {
statusCode *int
size int
recorder ResponseWriter
}
// StatusCode returns the response status code.
func (o Options) StatusCode() int {
if o.recorder != nil {
return o.recorder.Status()
}
return *o.statusCode
}
// Size returns the response size.
func (o Options) Size() int {
if o.recorder != nil {
return o.recorder.Size()
}
return o.size
}
// Option represents a stats option.
type Option func(*Options)
// WithStatusCode sets the status code to use in stats.
func WithStatusCode(statusCode int) Option {
return func(o *Options) {
o.statusCode = &statusCode
}
}
// WithSize sets the size to use in stats.
func WithSize(size int) Option {
return func(o *Options) {
o.size = size
}
}
// WithRecorder sets the recorder to use in stats.
func WithRecorder(recorder ResponseWriter) Option {
return func(o *Options) {
o.recorder = recorder
}
}
// newOptions takes functional options and returns options.
func newOptions(options ...Option) *Options {
opts := &Options{}
for _, o := range options {
o(opts)
}
return opts
}

View file

@ -28,13 +28,15 @@ type recorderResponseWriter struct {
status int
size int
beforeFuncs []beforeFunc
written bool
}
func NewRecorderResponseWriter(w http.ResponseWriter, statusCode int) ResponseWriter {
return &recorderResponseWriter{w, statusCode, 0, nil}
return &recorderResponseWriter{ResponseWriter: w, status: statusCode}
}
func (r *recorderResponseWriter) WriteHeader(code int) {
r.written = true
r.ResponseWriter.WriteHeader(code)
r.status = code
}
@ -78,6 +80,9 @@ func (r *recorderResponseWriter) CloseNotify() <-chan bool {
}
func (r *recorderResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
if !r.written {
r.status = 0
}
hijacker, ok := r.ResponseWriter.(http.Hijacker)
if !ok {
return nil, nil, fmt.Errorf("the ResponseWriter doesn't support the Hijacker interface")

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