Remove thoas/stats fork
This commit is contained in:
parent
1bcb3d8cc2
commit
08d7bb0d08
5 changed files with 160 additions and 36 deletions
4
Gopkg.lock
generated
4
Gopkg.lock
generated
|
@ -1469,11 +1469,11 @@
|
||||||
revision = "c4434f09ec131ecf30f986d5dcb1636508bfa49a"
|
revision = "c4434f09ec131ecf30f986d5dcb1636508bfa49a"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
digest = "1:c269070c7f286c941392076d6c42a4a7a062356f7083a48bd202397e877a965e"
|
digest = "1:84b9a5318d8ce3b8a9b1509bf15734f4f9dcd4decf9d9e9c7346a16c7b64d49e"
|
||||||
name = "github.com/thoas/stats"
|
name = "github.com/thoas/stats"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
pruneopts = "NUT"
|
pruneopts = "NUT"
|
||||||
revision = "152b5d051953fdb6e45f14b6826962aadc032324"
|
revision = "4975baf6a358ed3ddaa42133996e1959f96c9300"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
|
|
|
@ -85,9 +85,10 @@ required = [
|
||||||
name = "github.com/containous/staert"
|
name = "github.com/containous/staert"
|
||||||
version = "3.1.2"
|
version = "3.1.2"
|
||||||
|
|
||||||
#[[constraint]]
|
[[constraint]]
|
||||||
# name = "github.com/containous/traefik-extra-service-fabric"
|
name = "github.com/thoas/stats"
|
||||||
# version = "1.3.0"
|
# related to https://github.com/thoas/stats/pull/32
|
||||||
|
revision = "4975baf6a358ed3ddaa42133996e1959f96c9300"
|
||||||
|
|
||||||
[[constraint]]
|
[[constraint]]
|
||||||
name = "github.com/coreos/go-systemd"
|
name = "github.com/coreos/go-systemd"
|
||||||
|
|
59
vendor/github.com/thoas/stats/options.go
generated
vendored
Normal file
59
vendor/github.com/thoas/stats/options.go
generated
vendored
Normal 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
|
||||||
|
}
|
7
vendor/github.com/thoas/stats/recorder.go
generated
vendored
7
vendor/github.com/thoas/stats/recorder.go
generated
vendored
|
@ -28,13 +28,15 @@ type recorderResponseWriter struct {
|
||||||
status int
|
status int
|
||||||
size int
|
size int
|
||||||
beforeFuncs []beforeFunc
|
beforeFuncs []beforeFunc
|
||||||
|
written bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRecorderResponseWriter(w http.ResponseWriter, statusCode int) ResponseWriter {
|
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) {
|
func (r *recorderResponseWriter) WriteHeader(code int) {
|
||||||
|
r.written = true
|
||||||
r.ResponseWriter.WriteHeader(code)
|
r.ResponseWriter.WriteHeader(code)
|
||||||
r.status = code
|
r.status = code
|
||||||
}
|
}
|
||||||
|
@ -78,6 +80,9 @@ func (r *recorderResponseWriter) CloseNotify() <-chan bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *recorderResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
func (r *recorderResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
||||||
|
if !r.written {
|
||||||
|
r.status = 0
|
||||||
|
}
|
||||||
hijacker, ok := r.ResponseWriter.(http.Hijacker)
|
hijacker, ok := r.ResponseWriter.(http.Hijacker)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, nil, fmt.Errorf("the ResponseWriter doesn't support the Hijacker interface")
|
return nil, nil, fmt.Errorf("the ResponseWriter doesn't support the Hijacker interface")
|
||||||
|
|
119
vendor/github.com/thoas/stats/stats.go
generated
vendored
119
vendor/github.com/thoas/stats/stats.go
generated
vendored
|
@ -11,34 +11,58 @@ import (
|
||||||
// Stats data structure
|
// Stats data structure
|
||||||
type Stats struct {
|
type Stats struct {
|
||||||
mu sync.RWMutex
|
mu sync.RWMutex
|
||||||
|
closed chan struct{}
|
||||||
|
Hostname string
|
||||||
Uptime time.Time
|
Uptime time.Time
|
||||||
Pid int
|
Pid int
|
||||||
ResponseCounts map[string]int
|
ResponseCounts map[string]int
|
||||||
TotalResponseCounts map[string]int
|
TotalResponseCounts map[string]int
|
||||||
TotalResponseTime time.Time
|
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
|
// New constructs a new Stats structure
|
||||||
func New() *Stats {
|
func New() *Stats {
|
||||||
|
name, _ := os.Hostname()
|
||||||
|
|
||||||
stats := &Stats{
|
stats := &Stats{
|
||||||
|
closed: make(chan struct{}, 1),
|
||||||
Uptime: time.Now(),
|
Uptime: time.Now(),
|
||||||
Pid: os.Getpid(),
|
Pid: os.Getpid(),
|
||||||
ResponseCounts: map[string]int{},
|
ResponseCounts: map[string]int{},
|
||||||
TotalResponseCounts: map[string]int{},
|
TotalResponseCounts: map[string]int{},
|
||||||
TotalResponseTime: time.Time{},
|
TotalResponseTime: time.Time{},
|
||||||
|
Hostname: name,
|
||||||
}
|
}
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
stats.ResetResponseCounts()
|
select {
|
||||||
|
case <-stats.closed:
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
stats.ResetResponseCounts()
|
||||||
|
|
||||||
time.Sleep(time.Second * 1)
|
time.Sleep(time.Second * 1)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
return stats
|
return stats
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (mw *Stats) Close() {
|
||||||
|
close(mw.closed)
|
||||||
|
}
|
||||||
|
|
||||||
// ResetResponseCounts reset the response counts
|
// ResetResponseCounts reset the response counts
|
||||||
func (mw *Stats) ResetResponseCounts() {
|
func (mw *Stats) ResetResponseCounts() {
|
||||||
mw.mu.Lock()
|
mw.mu.Lock()
|
||||||
|
@ -53,7 +77,7 @@ func (mw *Stats) Handler(h http.Handler) http.Handler {
|
||||||
|
|
||||||
h.ServeHTTP(recorder, r)
|
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)
|
next(recorder, r)
|
||||||
|
|
||||||
mw.End(beginning, recorder)
|
mw.End(beginning, WithRecorder(recorder))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Begin starts a recorder
|
// Begin starts a recorder
|
||||||
|
@ -75,52 +99,73 @@ func (mw *Stats) Begin(w http.ResponseWriter) (time.Time, ResponseWriter) {
|
||||||
return start, writer
|
return start, writer
|
||||||
}
|
}
|
||||||
|
|
||||||
// EndWithStatus closes the recorder with a specific status
|
// End closes the recorder with a specific status
|
||||||
func (mw *Stats) EndWithStatus(start time.Time, status int) {
|
func (mw *Stats) End(start time.Time, opts ...Option) {
|
||||||
end := time.Now()
|
options := newOptions(opts...)
|
||||||
|
|
||||||
responseTime := end.Sub(start)
|
responseTime := time.Since(start)
|
||||||
|
|
||||||
mw.mu.Lock()
|
mw.mu.Lock()
|
||||||
|
|
||||||
defer mw.mu.Unlock()
|
defer mw.mu.Unlock()
|
||||||
|
|
||||||
statusCode := fmt.Sprintf("%d", status)
|
// If Hijacked connection do not count in response time
|
||||||
|
if options.StatusCode() != 0 {
|
||||||
mw.ResponseCounts[statusCode]++
|
statusCode := fmt.Sprintf("%d", options.StatusCode())
|
||||||
mw.TotalResponseCounts[statusCode]++
|
mw.ResponseCounts[statusCode]++
|
||||||
mw.TotalResponseTime = mw.TotalResponseTime.Add(responseTime)
|
mw.TotalResponseCounts[statusCode]++
|
||||||
|
mw.TotalResponseTime = mw.TotalResponseTime.Add(responseTime)
|
||||||
|
mw.TotalResponseSize += int64(options.Size())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// End closes the recorder with the recorder status
|
// MeasureSince method for execution time recording
|
||||||
func (mw *Stats) End(start time.Time, recorder ResponseWriter) {
|
func (mw *Stats) MeasureSince(key string, start time.Time) {
|
||||||
mw.EndWithStatus(start, recorder.Status())
|
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
|
// Data serializable structure
|
||||||
type Data struct {
|
type Data struct {
|
||||||
Pid int `json:"pid"`
|
Pid int `json:"pid"`
|
||||||
UpTime string `json:"uptime"`
|
Hostname string `json:"hostname"`
|
||||||
UpTimeSec float64 `json:"uptime_sec"`
|
UpTime string `json:"uptime"`
|
||||||
Time string `json:"time"`
|
UpTimeSec float64 `json:"uptime_sec"`
|
||||||
TimeUnix int64 `json:"unixtime"`
|
Time string `json:"time"`
|
||||||
StatusCodeCount map[string]int `json:"status_code_count"`
|
TimeUnix int64 `json:"unixtime"`
|
||||||
TotalStatusCodeCount map[string]int `json:"total_status_code_count"`
|
StatusCodeCount map[string]int `json:"status_code_count"`
|
||||||
Count int `json:"count"`
|
TotalStatusCodeCount map[string]int `json:"total_status_code_count"`
|
||||||
TotalCount int `json:"total_count"`
|
Count int `json:"count"`
|
||||||
TotalResponseTime string `json:"total_response_time"`
|
TotalCount int `json:"total_count"`
|
||||||
TotalResponseTimeSec float64 `json:"total_response_time_sec"`
|
TotalResponseTime string `json:"total_response_time"`
|
||||||
AverageResponseTime string `json:"average_response_time"`
|
TotalResponseTimeSec float64 `json:"total_response_time_sec"`
|
||||||
AverageResponseTimeSec float64 `json:"average_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
|
// Data returns the data serializable structure
|
||||||
func (mw *Stats) Data() *Data {
|
func (mw *Stats) Data() *Data {
|
||||||
|
|
||||||
mw.mu.RLock()
|
mw.mu.RLock()
|
||||||
|
|
||||||
responseCounts := make(map[string]int, len(mw.ResponseCounts))
|
responseCounts := make(map[string]int, len(mw.ResponseCounts))
|
||||||
totalResponseCounts := make(map[string]int, len(mw.TotalResponseCounts))
|
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()
|
now := time.Now()
|
||||||
|
|
||||||
|
@ -139,11 +184,21 @@ func (mw *Stats) Data() *Data {
|
||||||
}
|
}
|
||||||
|
|
||||||
totalResponseTime := mw.TotalResponseTime.Sub(time.Time{})
|
totalResponseTime := mw.TotalResponseTime.Sub(time.Time{})
|
||||||
|
totalResponseSize := mw.TotalResponseSize
|
||||||
|
|
||||||
averageResponseTime := time.Duration(0)
|
averageResponseTime := time.Duration(0)
|
||||||
|
averageResponseSize := int64(0)
|
||||||
if totalCount > 0 {
|
if totalCount > 0 {
|
||||||
avgNs := int64(totalResponseTime) / int64(totalCount)
|
avgNs := int64(totalResponseTime) / int64(totalCount)
|
||||||
averageResponseTime = time.Duration(avgNs)
|
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()
|
mw.mu.RUnlock()
|
||||||
|
@ -159,9 +214,13 @@ func (mw *Stats) Data() *Data {
|
||||||
Count: count,
|
Count: count,
|
||||||
TotalCount: totalCount,
|
TotalCount: totalCount,
|
||||||
TotalResponseTime: totalResponseTime.String(),
|
TotalResponseTime: totalResponseTime.String(),
|
||||||
|
TotalResponseSize: totalResponseSize,
|
||||||
TotalResponseTimeSec: totalResponseTime.Seconds(),
|
TotalResponseTimeSec: totalResponseTime.Seconds(),
|
||||||
|
TotalMetricsCounts: totalMetricsCounts,
|
||||||
|
AverageResponseSize: averageResponseSize,
|
||||||
AverageResponseTime: averageResponseTime.String(),
|
AverageResponseTime: averageResponseTime.String(),
|
||||||
AverageResponseTimeSec: averageResponseTime.Seconds(),
|
AverageResponseTimeSec: averageResponseTime.Seconds(),
|
||||||
|
AverageMetricsTimers: metricsCounts,
|
||||||
}
|
}
|
||||||
|
|
||||||
return r
|
return r
|
||||||
|
|
Loading…
Reference in a new issue