2016-05-24 15:04:28 +00:00
|
|
|
package tusd
|
|
|
|
|
|
|
|
import (
|
2017-02-01 07:37:46 +00:00
|
|
|
"sync"
|
2016-05-24 15:04:28 +00:00
|
|
|
"sync/atomic"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Metrics provides numbers about the usage of the tusd handler. Since these may
|
|
|
|
// be accessed from multiple goroutines, it is necessary to read and modify them
|
|
|
|
// atomically using the functions exposed in the sync/atomic package, such as
|
|
|
|
// atomic.LoadUint64. In addition the maps must not be modified to prevent data
|
|
|
|
// races.
|
|
|
|
type Metrics struct {
|
|
|
|
// RequestTotal counts the number of incoming requests per method
|
|
|
|
RequestsTotal map[string]*uint64
|
|
|
|
// ErrorsTotal counts the number of returned errors by their message
|
2017-02-01 07:37:46 +00:00
|
|
|
ErrorsTotal ErrorsTotalMap
|
2016-05-24 15:04:28 +00:00
|
|
|
BytesReceived *uint64
|
|
|
|
UploadsFinished *uint64
|
|
|
|
UploadsCreated *uint64
|
|
|
|
UploadsTerminated *uint64
|
|
|
|
}
|
|
|
|
|
|
|
|
// incRequestsTotal increases the counter for this request method atomically by
|
|
|
|
// one. The method must be one of GET, HEAD, POST, PATCH, DELETE.
|
2017-02-01 07:37:46 +00:00
|
|
|
func (m *Metrics) incRequestsTotal(method string) {
|
2016-05-24 15:27:07 +00:00
|
|
|
if ptr, ok := m.RequestsTotal[method]; ok {
|
|
|
|
atomic.AddUint64(ptr, 1)
|
|
|
|
}
|
2016-05-24 15:04:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// incErrorsTotal increases the counter for this error atomically by one.
|
2017-02-01 07:37:46 +00:00
|
|
|
func (m *Metrics) incErrorsTotal(err HTTPError) {
|
|
|
|
m.ErrorsTotal.incError(err)
|
2016-05-24 15:04:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// incBytesReceived increases the number of received bytes atomically be the
|
|
|
|
// specified number.
|
2017-02-01 07:37:46 +00:00
|
|
|
func (m *Metrics) incBytesReceived(delta uint64) {
|
2016-05-24 15:04:28 +00:00
|
|
|
atomic.AddUint64(m.BytesReceived, delta)
|
|
|
|
}
|
|
|
|
|
|
|
|
// incUploadsFinished increases the counter for finished uploads atomically by one.
|
2017-02-01 07:37:46 +00:00
|
|
|
func (m *Metrics) incUploadsFinished() {
|
2016-05-24 15:04:28 +00:00
|
|
|
atomic.AddUint64(m.UploadsFinished, 1)
|
|
|
|
}
|
|
|
|
|
|
|
|
// incUploadsCreated increases the counter for completed uploads atomically by one.
|
2017-02-01 07:37:46 +00:00
|
|
|
func (m *Metrics) incUploadsCreated() {
|
2016-05-24 15:04:28 +00:00
|
|
|
atomic.AddUint64(m.UploadsCreated, 1)
|
|
|
|
}
|
|
|
|
|
|
|
|
// incUploadsTerminated increases the counter for completed uploads atomically by one.
|
2017-02-01 07:37:46 +00:00
|
|
|
func (m *Metrics) incUploadsTerminated() {
|
2016-05-24 15:04:28 +00:00
|
|
|
atomic.AddUint64(m.UploadsTerminated, 1)
|
|
|
|
}
|
|
|
|
|
|
|
|
func newMetrics() Metrics {
|
|
|
|
return Metrics{
|
|
|
|
RequestsTotal: map[string]*uint64{
|
2016-05-24 15:27:07 +00:00
|
|
|
"GET": new(uint64),
|
|
|
|
"HEAD": new(uint64),
|
|
|
|
"POST": new(uint64),
|
|
|
|
"PATCH": new(uint64),
|
|
|
|
"DELETE": new(uint64),
|
|
|
|
"OPTIONS": new(uint64),
|
2016-05-24 15:04:28 +00:00
|
|
|
},
|
|
|
|
ErrorsTotal: newErrorsTotalMap(),
|
|
|
|
BytesReceived: new(uint64),
|
|
|
|
UploadsFinished: new(uint64),
|
|
|
|
UploadsCreated: new(uint64),
|
|
|
|
UploadsTerminated: new(uint64),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-01 07:37:46 +00:00
|
|
|
// ErrorsTotalMap stores the counter for the different http errors.
|
|
|
|
type ErrorsTotalMap struct {
|
|
|
|
sync.RWMutex
|
|
|
|
m map[HTTPError]*uint64
|
|
|
|
}
|
|
|
|
|
|
|
|
func newErrorsTotalMap() ErrorsTotalMap {
|
|
|
|
m := make(map[HTTPError]*uint64, 20)
|
|
|
|
return ErrorsTotalMap{
|
|
|
|
m: m,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// incErrorsTotal increases the counter for this error atomically by one.
|
|
|
|
func (e *ErrorsTotalMap) incError(err HTTPError) {
|
|
|
|
// The goal is to have a valid ptr to the number of HTTPError
|
|
|
|
e.RLock()
|
|
|
|
if ptr, ok := e.m[err]; !ok {
|
|
|
|
// The ptr does not seem to exist for this err
|
|
|
|
// Hence we create it (using a write lock)
|
|
|
|
e.RUnlock()
|
|
|
|
e.Lock()
|
|
|
|
// We ensure that the ptr wasn't created in the meantime
|
|
|
|
if ptr, ok = e.m[err]; !ok {
|
|
|
|
ptr = new(uint64)
|
|
|
|
*ptr = 0
|
|
|
|
e.m[err] = ptr
|
|
|
|
}
|
|
|
|
e.Unlock()
|
|
|
|
} else {
|
|
|
|
e.RUnlock()
|
|
|
|
}
|
|
|
|
// We can then increase the counter
|
|
|
|
atomic.AddUint64(e.m[err], 1)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Load retrieves the map of the counter pointers atomically
|
|
|
|
func (e *ErrorsTotalMap) Load() (m map[HTTPError]*uint64) {
|
|
|
|
m = make(map[HTTPError]*uint64, len(e.m))
|
|
|
|
e.RLock()
|
|
|
|
for err, ptr := range e.m {
|
|
|
|
m[err] = ptr
|
|
|
|
}
|
|
|
|
e.RUnlock()
|
|
|
|
return
|
2016-05-24 15:04:28 +00:00
|
|
|
}
|