- Separate out the packages' files' documentation into a separate documentation file per package.
- Provide better examples in the examples subdirectory. - Make the comments consistent in terms of using multi-line format for future-proofing. - Extract major constants out.
This commit is contained in:
parent
5ea9b1a0b5
commit
e0b92aec7a
23
README.md
23
README.md
|
@ -17,42 +17,47 @@ to be made, but this task has been deferred for now.
|
||||||
# Continuous Integration
|
# Continuous Integration
|
||||||
[![Build Status](https://secure.travis-ci.org/matttproud/golang_instrumentation.png?branch=master)](http://travis-ci.org/matttproud/golang_instrumentation)
|
[![Build Status](https://secure.travis-ci.org/matttproud/golang_instrumentation.png?branch=master)](http://travis-ci.org/matttproud/golang_instrumentation)
|
||||||
|
|
||||||
# Metrics
|
# Documentation
|
||||||
|
Please read the [generated documentation](http://go.pkgdoc.org/launchpad.net/gocheck)
|
||||||
|
for the project's documentation from source code.
|
||||||
|
|
||||||
|
# Basic Overview
|
||||||
|
## Metrics
|
||||||
A metric is a measurement mechanism.
|
A metric is a measurement mechanism.
|
||||||
|
|
||||||
## Gauge
|
### Gauge
|
||||||
A Gauge is a metric that exposes merely an instantaneous value or some snapshot
|
A Gauge is a metric that exposes merely an instantaneous value or some snapshot
|
||||||
thereof.
|
thereof.
|
||||||
|
|
||||||
## Histogram
|
### Histogram
|
||||||
A Histogram is a metric that captures events or samples into buckets. It
|
A Histogram is a metric that captures events or samples into buckets. It
|
||||||
exposes its values via percentile estimations.
|
exposes its values via percentile estimations.
|
||||||
|
|
||||||
### Buckets
|
#### Buckets
|
||||||
A Bucket is a generic container that collects samples and their values. It
|
A Bucket is a generic container that collects samples and their values. It
|
||||||
prescribes no behavior on its own aside from merely accepting a value,
|
prescribes no behavior on its own aside from merely accepting a value,
|
||||||
leaving it up to the concrete implementation to what to do with the injected
|
leaving it up to the concrete implementation to what to do with the injected
|
||||||
values.
|
values.
|
||||||
|
|
||||||
#### Accumulating Bucket
|
##### Accumulating Bucket
|
||||||
An Accumulating Bucket is a bucket that appends the new sample to a timestamped
|
An Accumulating Bucket is a bucket that appends the new sample to a timestamped
|
||||||
priority queue such that the eldest values are evicted according to a given
|
priority queue such that the eldest values are evicted according to a given
|
||||||
policy.
|
policy.
|
||||||
|
|
||||||
#### Eviction Policies
|
##### Eviction Policies
|
||||||
Once an Accumulating Bucket reaches capacity, its eviction policy is invoked.
|
Once an Accumulating Bucket reaches capacity, its eviction policy is invoked.
|
||||||
This reaps the oldest N objects subject to certain behavior.
|
This reaps the oldest N objects subject to certain behavior.
|
||||||
|
|
||||||
##### Remove Oldest
|
###### Remove Oldest
|
||||||
This merely removes the oldest N items without performing some aggregation
|
This merely removes the oldest N items without performing some aggregation
|
||||||
replacement operation on them.
|
replacement operation on them.
|
||||||
|
|
||||||
##### Aggregate Oldest
|
###### Aggregate Oldest
|
||||||
This removes the oldest N items while performing some summary aggregation
|
This removes the oldest N items while performing some summary aggregation
|
||||||
operation thereupon, which is then appended to the list in the former values'
|
operation thereupon, which is then appended to the list in the former values'
|
||||||
place.
|
place.
|
||||||
|
|
||||||
#### Tallying Bucket
|
##### Tallying Bucket
|
||||||
A Tallying Bucket differs from an Accumulating Bucket in that it never stores
|
A Tallying Bucket differs from an Accumulating Bucket in that it never stores
|
||||||
any of the values emitted into it but rather exposes a simplied summary
|
any of the values emitted into it but rather exposes a simplied summary
|
||||||
representation thereof. For instance, if a values therein is requested,
|
representation thereof. For instance, if a values therein is requested,
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2012, Matt T. Proud
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Use of this source code is governed by a BSD-style license that can be found in
|
||||||
|
the LICENSE file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
registry.go provides a container for centralized exposition of metrics to
|
||||||
|
their prospective consumers.
|
||||||
|
|
||||||
|
registry.Register("human_readable_metric_name", metric)
|
||||||
|
|
||||||
|
Please try to observe the following rules when naming metrics:
|
||||||
|
|
||||||
|
- Use underbars "_" to separate words.
|
||||||
|
|
||||||
|
- Have the metric name start from generality and work toward specificity
|
||||||
|
toward the end. For example, when working with multiple caching subsystems,
|
||||||
|
consider using the following structure "cache" + "user_credentials" →
|
||||||
|
"cache_user_credentials" and "cache" + "value_transformations" →
|
||||||
|
"cache_value_transformations".
|
||||||
|
|
||||||
|
- Have whatever is being measured follow the system and subsystem names cited
|
||||||
|
supra. For instance, with "insertions", "deletions", "evictions",
|
||||||
|
"replacements" of the above cache, they should be named as
|
||||||
|
"cache_user_credentials_insertions" and "cache_user_credentials_deletions" and
|
||||||
|
"cache_user_credentials_deletions" and "cache_user_credentials_evictions".
|
||||||
|
|
||||||
|
- If what is being measured has a standardized unit around it, consider
|
||||||
|
providing a unit for it.
|
||||||
|
|
||||||
|
- Consider adding an additional suffix that designates what the value represents
|
||||||
|
such as a "total" or "size"---e.g., "cache_user_credentials_size_kb" or
|
||||||
|
"cache_user_credentials_insertions_total".
|
||||||
|
|
||||||
|
- Give heed to how future-proof the names are. Things may depend on these
|
||||||
|
names; and as your service evolves, the calculated values may take on
|
||||||
|
different meanings, which can be difficult to reflect if deployed code depends
|
||||||
|
on antique names.
|
||||||
|
|
||||||
|
Further considerations:
|
||||||
|
|
||||||
|
- The Registry's exposition mechanism is not backed by authorization and
|
||||||
|
authentication. This is something that will need to be addressed for
|
||||||
|
production services that are directly exposed to the outside world.
|
||||||
|
|
||||||
|
- Engage in as little in-process processing of values as possible. The job
|
||||||
|
of processing and aggregation of these values belongs in a separate
|
||||||
|
post-processing job. The same goes for archiving. I will need to evaluate
|
||||||
|
hooks into something like OpenTSBD.
|
||||||
|
*/
|
||||||
|
package registry
|
|
@ -1,16 +1,19 @@
|
||||||
// Copyright (c) 2012, Matt T. Proud
|
/*
|
||||||
// All rights reserved.
|
Copyright (c) 2012, Matt T. Proud
|
||||||
//
|
All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// main.go provides a simple example of how to use this instrumentation
|
Use of this source code is governed by a BSD-style
|
||||||
// framework in the context of having something that emits values into
|
license that can be found in the LICENSE file.
|
||||||
// its collectors.
|
*/
|
||||||
//
|
|
||||||
// The emitted values correspond to uniform, normal, and exponential
|
|
||||||
// distributions.
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
main.go provides a simple example of how to use this instrumentation
|
||||||
|
framework in the context of having something that emits values into
|
||||||
|
its collectors.
|
||||||
|
|
||||||
|
The emitted values correspond to uniform, normal, and exponential
|
||||||
|
distributions.
|
||||||
|
*/
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -43,12 +46,12 @@ func main() {
|
||||||
zed_rpc_calls := &metrics.GaugeMetric{}
|
zed_rpc_calls := &metrics.GaugeMetric{}
|
||||||
|
|
||||||
metrics := registry.NewRegistry()
|
metrics := registry.NewRegistry()
|
||||||
metrics.Register("foo_rpc_latency_ms_histogram", foo_rpc_latency)
|
metrics.Register("rpc_latency_foo_microseconds", foo_rpc_latency)
|
||||||
metrics.Register("foo_rpc_call_count", foo_rpc_calls)
|
metrics.Register("rpc_calls_foo_total", foo_rpc_calls)
|
||||||
metrics.Register("bar_rpc_latency_ms_histogram", bar_rpc_latency)
|
metrics.Register("rpc_latency_bar_microseconds", bar_rpc_latency)
|
||||||
metrics.Register("bar_rpc_call_count", bar_rpc_calls)
|
metrics.Register("rpc_calls_bar_total", bar_rpc_calls)
|
||||||
metrics.Register("zed_rpc_latency_ms_histogram", zed_rpc_latency)
|
metrics.Register("rpc_latency_zed_microseconds", zed_rpc_latency)
|
||||||
metrics.Register("zed_rpc_call_count", zed_rpc_calls)
|
metrics.Register("rpc_calls_zed_total", zed_rpc_calls)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
|
|
|
@ -1,12 +1,15 @@
|
||||||
// Copyright (c) 2012, Matt T. Proud
|
/*
|
||||||
// All rights reserved.
|
Copyright (c) 2012, Matt T. Proud
|
||||||
//
|
All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// main.go provides a simple skeletal example of how this instrumentation
|
Use of this source code is governed by a BSD-style
|
||||||
// framework is registered and invoked.
|
license that can be found in the LICENSE file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
main.go provides a simple skeletal example of how this instrumentation
|
||||||
|
framework is registered and invoked.
|
||||||
|
*/
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
// Copyright (c) 2012, Matt T. Proud
|
/*
|
||||||
// All rights reserved.
|
Copyright (c) 2012, Matt T. Proud
|
||||||
//
|
All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// distributions.go provides basic distribution-generating functions that are
|
Use of this source code is governed by a BSD-style
|
||||||
// used primarily in testing contexts.
|
license that can be found in the LICENSE file.
|
||||||
|
*/
|
||||||
|
|
||||||
package maths
|
package maths
|
||||||
|
|
||||||
|
@ -13,7 +12,9 @@ import (
|
||||||
"math"
|
"math"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Go's standard library does not offer a factorial function.
|
/*
|
||||||
|
Go's standard library does not offer a factorial function.
|
||||||
|
*/
|
||||||
func Factorial(of int) int64 {
|
func Factorial(of int) int64 {
|
||||||
if of <= 0 {
|
if of <= 0 {
|
||||||
return 1
|
return 1
|
||||||
|
@ -28,9 +29,11 @@ func Factorial(of int) int64 {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create calculate the value of a probability density for a given binomial
|
/*
|
||||||
// statistic, where k is the target count of true cases, n is the number of
|
Create calculate the value of a probability density for a given binomial
|
||||||
// subjects, and p is the probability.
|
statistic, where k is the target count of true cases, n is the number of
|
||||||
|
subjects, and p is the probability.
|
||||||
|
*/
|
||||||
func BinomialPDF(k, n int, p float64) float64 {
|
func BinomialPDF(k, n int, p float64) float64 {
|
||||||
binomialCoefficient := float64(Factorial(n)) / float64(Factorial(k)*Factorial(n-k))
|
binomialCoefficient := float64(Factorial(n)) / float64(Factorial(k)*Factorial(n-k))
|
||||||
intermediate := math.Pow(p, float64(k)) * math.Pow(1-p, float64(n-k))
|
intermediate := math.Pow(p, float64(k)) * math.Pow(1-p, float64(n-k))
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2012, Matt T. Proud
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Use of this source code is governed by a BSD-style
|
||||||
|
license that can be found in the LICENSE file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
The maths package provides a number of mathematical-related helpers:
|
||||||
|
|
||||||
|
distributions.go provides basic distribution-generating functions that are
|
||||||
|
used primarily in testing contexts.
|
||||||
|
|
||||||
|
helpers_for_testing.go provides a testing assistents for this package and its
|
||||||
|
dependents.
|
||||||
|
|
||||||
|
maths_test.go provides a test suite for all tests in the maths package
|
||||||
|
hierarchy. It employs the gocheck framework for test scaffolding.
|
||||||
|
|
||||||
|
statistics.go provides basic summary statistics functions for the purpose of
|
||||||
|
metrics aggregation.
|
||||||
|
|
||||||
|
statistics_test.go provides a test complement for the statistics.go module.
|
||||||
|
*/
|
||||||
|
package maths
|
|
@ -1,11 +1,10 @@
|
||||||
// Copyright (c) 2012, Matt T. Proud
|
/*
|
||||||
// All rights reserved.
|
Copyright (c) 2012, Matt T. Proud
|
||||||
//
|
All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// helpers_for_testing.go provides a testing assistents for this package and its
|
Use of this source code is governed by a BSD-style
|
||||||
// dependents.
|
license that can be found in the LICENSE file.
|
||||||
|
*/
|
||||||
|
|
||||||
package maths
|
package maths
|
||||||
|
|
||||||
|
@ -19,8 +18,10 @@ type isNaNChecker struct {
|
||||||
*CheckerInfo
|
*CheckerInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
// This piece provides a simple tester for the gocheck testing library to
|
/*
|
||||||
// ascertain if a value is not-a-number.
|
This piece provides a simple tester for the gocheck testing library to
|
||||||
|
ascertain if a value is not-a-number.
|
||||||
|
*/
|
||||||
var IsNaN Checker = &isNaNChecker{
|
var IsNaN Checker = &isNaNChecker{
|
||||||
&CheckerInfo{Name: "IsNaN", Params: []string{"value"}},
|
&CheckerInfo{Name: "IsNaN", Params: []string{"value"}},
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
// Copyright (c) 2012, Matt T. Proud
|
/*
|
||||||
// All rights reserved.
|
Copyright (c) 2012, Matt T. Proud
|
||||||
//
|
All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// maths_test.go provides a test suite for all tests in the maths package
|
Use of this source code is governed by a BSD-style
|
||||||
// hierarchy. It employs the gocheck framework for test scaffolding.
|
license that can be found in the LICENSE file.
|
||||||
|
*/
|
||||||
|
|
||||||
package maths
|
package maths
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,10 @@
|
||||||
// Copyright (c) 2012, Matt T. Proud
|
/*
|
||||||
// All rights reserved.
|
Copyright (c) 2012, Matt T. Proud
|
||||||
//
|
All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// statistics.go provides basic summary statistics functions for the purpose of
|
Use of this source code is governed by a BSD-style
|
||||||
// metrics aggregation.
|
license that can be found in the LICENSE file.
|
||||||
|
*/
|
||||||
// TODO(mtp): Split this out into a summary statistics file once moving/rolling
|
|
||||||
// averages are calculated.
|
|
||||||
|
|
||||||
package maths
|
package maths
|
||||||
|
|
||||||
|
@ -17,8 +13,15 @@ import (
|
||||||
"sort"
|
"sort"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ReductionMethod provides a method for reducing metrics into a given scalar
|
/*
|
||||||
// value.
|
TODO(mtp): Split this out into a summary statistics file once moving/rolling
|
||||||
|
averages are calculated.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
ReductionMethod provides a method for reducing metrics into a given scalar
|
||||||
|
value.
|
||||||
|
*/
|
||||||
type ReductionMethod func([]float64) float64
|
type ReductionMethod func([]float64) float64
|
||||||
|
|
||||||
var Average ReductionMethod = func(input []float64) float64 {
|
var Average ReductionMethod = func(input []float64) float64 {
|
||||||
|
@ -37,7 +40,9 @@ var Average ReductionMethod = func(input []float64) float64 {
|
||||||
return sum / count
|
return sum / count
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract the first modal value.
|
/*
|
||||||
|
Extract the first modal value.
|
||||||
|
*/
|
||||||
var FirstMode ReductionMethod = func(input []float64) float64 {
|
var FirstMode ReductionMethod = func(input []float64) float64 {
|
||||||
valuesToFrequency := map[float64]int64{}
|
valuesToFrequency := map[float64]int64{}
|
||||||
var largestTally int64 = math.MinInt64
|
var largestTally int64 = math.MinInt64
|
||||||
|
@ -58,7 +63,9 @@ var FirstMode ReductionMethod = func(input []float64) float64 {
|
||||||
return largestTallyValue
|
return largestTallyValue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate the percentile by choosing the nearest neighboring value.
|
/*
|
||||||
|
Calculate the percentile by choosing the nearest neighboring value.
|
||||||
|
*/
|
||||||
func NearestRank(input []float64, percentile float64) float64 {
|
func NearestRank(input []float64, percentile float64) float64 {
|
||||||
inputSize := len(input)
|
inputSize := len(input)
|
||||||
|
|
||||||
|
@ -81,7 +88,10 @@ func NearestRank(input []float64, percentile float64) float64 {
|
||||||
return copiedInput[preliminaryIndex]
|
return copiedInput[preliminaryIndex]
|
||||||
}
|
}
|
||||||
|
|
||||||
func NearestRankReducer(percentile float64) func(input []float64) float64 {
|
/*
|
||||||
|
Generate a ReductionMethod based off of extracting a given percentile value.
|
||||||
|
*/
|
||||||
|
func NearestRankReducer(percentile float64) ReductionMethod {
|
||||||
return func(input []float64) float64 {
|
return func(input []float64) float64 {
|
||||||
return NearestRank(input, percentile)
|
return NearestRank(input, percentile)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
// Copyright (c) 2012, Matt T. Proud
|
/*
|
||||||
// All rights reserved.
|
Copyright (c) 2012, Matt T. Proud
|
||||||
//
|
All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// statistics_test.go provides a test complement for the statistics.go module.
|
Use of this source code is governed by a BSD-style
|
||||||
|
license that can be found in the LICENSE file.
|
||||||
|
*/
|
||||||
|
|
||||||
package maths
|
package maths
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,10 @@
|
||||||
// Copyright (c) 2012, Matt T. Proud
|
/*
|
||||||
// All rights reserved.
|
Copyright (c) 2012, Matt T. Proud
|
||||||
//
|
All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// accumulating_bucket.go provides a histogram bucket type that accumulates
|
Use of this source code is governed by a BSD-style
|
||||||
// elements until a given capacity and enacts a given eviction policy upon
|
license that can be found in the LICENSE file.
|
||||||
// such a condition.
|
*/
|
||||||
|
|
||||||
package metrics
|
package metrics
|
||||||
|
|
||||||
|
@ -29,9 +27,11 @@ type AccumulatingBucket struct {
|
||||||
evictionPolicy EvictionPolicy
|
evictionPolicy EvictionPolicy
|
||||||
}
|
}
|
||||||
|
|
||||||
// AccumulatingBucketBuilder is a convenience method for generating a
|
/*
|
||||||
// BucketBuilder that produces AccumatingBucket entries with a certain
|
AccumulatingBucketBuilder is a convenience method for generating a
|
||||||
// behavior set.
|
BucketBuilder that produces AccumatingBucket entries with a certain
|
||||||
|
behavior set.
|
||||||
|
*/
|
||||||
func AccumulatingBucketBuilder(evictionPolicy EvictionPolicy, maximumSize int) BucketBuilder {
|
func AccumulatingBucketBuilder(evictionPolicy EvictionPolicy, maximumSize int) BucketBuilder {
|
||||||
return func() Bucket {
|
return func() Bucket {
|
||||||
return &AccumulatingBucket{
|
return &AccumulatingBucket{
|
||||||
|
@ -42,8 +42,10 @@ func AccumulatingBucketBuilder(evictionPolicy EvictionPolicy, maximumSize int) B
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add a value to the bucket. Depending on whether the bucket is full, it may
|
/*
|
||||||
// trigger an eviction of older items.
|
Add a value to the bucket. Depending on whether the bucket is full, it may
|
||||||
|
trigger an eviction of older items.
|
||||||
|
*/
|
||||||
func (b *AccumulatingBucket) Add(value float64) {
|
func (b *AccumulatingBucket) Add(value float64) {
|
||||||
b.mutex.Lock()
|
b.mutex.Lock()
|
||||||
defer b.mutex.Unlock()
|
defer b.mutex.Unlock()
|
||||||
|
@ -98,9 +100,11 @@ func (b *AccumulatingBucket) ValueForIndex(index int) float64 {
|
||||||
|
|
||||||
sort.Float64s(sortedElements)
|
sort.Float64s(sortedElements)
|
||||||
|
|
||||||
// N.B.(mtp): Interfacing components should not need to comprehend what
|
/*
|
||||||
// eviction and storage container strategies used; therefore,
|
N.B.(mtp): Interfacing components should not need to comprehend what
|
||||||
// we adjust this silently.
|
eviction and storage container strategies used; therefore,
|
||||||
|
we adjust this silently.
|
||||||
|
*/
|
||||||
targetIndex := int(float64(elementCount-1) * (float64(index) / float64(b.observations)))
|
targetIndex := int(float64(elementCount-1) * (float64(index) / float64(b.observations)))
|
||||||
|
|
||||||
return sortedElements[targetIndex]
|
return sortedElements[targetIndex]
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
// Copyright (c) 2012, Matt T. Proud
|
/*
|
||||||
// All rights reserved.
|
Copyright (c) 2012, Matt T. Proud
|
||||||
//
|
All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// accumulating_bucket_test.go provides a test complement for the
|
Use of this source code is governed by a BSD-style
|
||||||
// accumulating_bucket_go module.
|
license that can be found in the LICENSE file.
|
||||||
|
*/
|
||||||
|
|
||||||
package metrics
|
package metrics
|
||||||
|
|
||||||
|
@ -124,17 +123,23 @@ func (s *S) TestAccumulatingBucketValueForIndex(c *C) {
|
||||||
c.Assert(b.ValueForIndex(i), maths.IsNaN)
|
c.Assert(b.ValueForIndex(i), maths.IsNaN)
|
||||||
}
|
}
|
||||||
|
|
||||||
// The bucket has only observed one item and contains now one item.
|
/*
|
||||||
|
The bucket has only observed one item and contains now one item.
|
||||||
|
*/
|
||||||
b.Add(1.0)
|
b.Add(1.0)
|
||||||
|
|
||||||
c.Check(b.ValueForIndex(0), Equals, 1.0)
|
c.Check(b.ValueForIndex(0), Equals, 1.0)
|
||||||
// Let's sanity check what occurs if presumably an eviction happened and
|
/*
|
||||||
// we requested an index larger than what is contained.
|
Let's sanity check what occurs if presumably an eviction happened and
|
||||||
|
we requested an index larger than what is contained.
|
||||||
|
*/
|
||||||
c.Check(b.ValueForIndex(1), Equals, 1.0)
|
c.Check(b.ValueForIndex(1), Equals, 1.0)
|
||||||
|
|
||||||
for i := 2.0; i <= 100; i += 1 {
|
for i := 2.0; i <= 100; i += 1 {
|
||||||
b.Add(i)
|
b.Add(i)
|
||||||
// TODO(mtp): This is a sin. Provide a mechanism for deterministic testing.
|
/*
|
||||||
|
TODO(mtp): This is a sin. Provide a mechanism for deterministic testing.
|
||||||
|
*/
|
||||||
time.Sleep(1 * time.Millisecond)
|
time.Sleep(1 * time.Millisecond)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -144,13 +149,17 @@ func (s *S) TestAccumulatingBucketValueForIndex(c *C) {
|
||||||
|
|
||||||
for i := 101.0; i <= 150; i += 1 {
|
for i := 101.0; i <= 150; i += 1 {
|
||||||
b.Add(i)
|
b.Add(i)
|
||||||
// TODO(mtp): This is a sin. Provide a mechanism for deterministic testing.
|
/*
|
||||||
|
TODO(mtp): This is a sin. Provide a mechanism for deterministic testing.
|
||||||
|
*/
|
||||||
time.Sleep(1 * time.Millisecond)
|
time.Sleep(1 * time.Millisecond)
|
||||||
}
|
}
|
||||||
|
|
||||||
// The bucket's capacity has been exceeded by inputs at this point;
|
/*
|
||||||
// consequently, we search for a given element by percentage offset
|
The bucket's capacity has been exceeded by inputs at this point;
|
||||||
// therein.
|
consequently, we search for a given element by percentage offset
|
||||||
|
therein.
|
||||||
|
*/
|
||||||
c.Check(b.ValueForIndex(0), Equals, 51.0)
|
c.Check(b.ValueForIndex(0), Equals, 51.0)
|
||||||
c.Check(b.ValueForIndex(50), Equals, 84.0)
|
c.Check(b.ValueForIndex(50), Equals, 84.0)
|
||||||
c.Check(b.ValueForIndex(99), Equals, 116.0)
|
c.Check(b.ValueForIndex(99), Equals, 116.0)
|
||||||
|
|
|
@ -1,17 +0,0 @@
|
||||||
// Copyright (c) 2012, Matt T. Proud
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// base.go provides fundamental interface expectations for the various metrics.
|
|
||||||
|
|
||||||
package metrics
|
|
||||||
|
|
||||||
// A Metric is something that can be exposed via the registry framework.
|
|
||||||
type Metric interface {
|
|
||||||
// Produce a human-consumable representation of the metric.
|
|
||||||
Humanize() string
|
|
||||||
// Produce a JSON-consumable representation of the metric.
|
|
||||||
Marshallable() map[string]interface{}
|
|
||||||
}
|
|
|
@ -1,32 +1,47 @@
|
||||||
// Copyright (c) 2012, Matt T. Proud
|
/*
|
||||||
// All rights reserved.
|
Copyright (c) 2012, Matt T. Proud
|
||||||
//
|
All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// bucket.go provides fundamental interface expectations for various bucket
|
Use of this source code is governed by a BSD-style
|
||||||
// types.
|
license that can be found in the LICENSE file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
bucket.go provides fundamental interface expectations for various bucket
|
||||||
|
types.
|
||||||
|
*/
|
||||||
package metrics
|
package metrics
|
||||||
|
|
||||||
// The Histogram class and associated types build buckets on their own.
|
/*
|
||||||
|
The Histogram class and associated types build buckets on their own.
|
||||||
|
*/
|
||||||
type BucketBuilder func() Bucket
|
type BucketBuilder func() Bucket
|
||||||
|
|
||||||
// This defines the base Bucket type. The exact behaviors of the bucket are
|
/*
|
||||||
// at the whim of the implementor.
|
This defines the base Bucket type. The exact behaviors of the bucket are
|
||||||
//
|
at the whim of the implementor.
|
||||||
// A Bucket is used as a container by Histogram as a collection for its
|
|
||||||
// accumulated samples.
|
A Bucket is used as a container by Histogram as a collection for its
|
||||||
|
accumulated samples.
|
||||||
|
*/
|
||||||
type Bucket interface {
|
type Bucket interface {
|
||||||
// Add a value to the bucket.
|
/*
|
||||||
|
Add a value to the bucket.
|
||||||
|
*/
|
||||||
Add(value float64)
|
Add(value float64)
|
||||||
// Provide a humanized representation hereof.
|
/*
|
||||||
|
Provide a humanized representation hereof.
|
||||||
|
*/
|
||||||
Humanize() string
|
Humanize() string
|
||||||
// Provide a count of observations throughout the bucket's lifetime.
|
/*
|
||||||
|
Provide a count of observations throughout the bucket's lifetime.
|
||||||
|
*/
|
||||||
Observations() int
|
Observations() int
|
||||||
// Provide the value from the given in-memory value cache or an estimate
|
/*
|
||||||
// thereof for the given index. The consumer of the bucket's data makes
|
Provide the value from the given in-memory value cache or an estimate
|
||||||
// no assumptions about the underlying storage mechanisms that the bucket
|
thereof for the given index. The consumer of the bucket's data makes
|
||||||
// employs.
|
no assumptions about the underlying storage mechanisms that the bucket
|
||||||
|
employs.
|
||||||
|
*/
|
||||||
ValueForIndex(index int) float64
|
ValueForIndex(index int) float64
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2012, Matt T. Proud
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Use of this source code is governed by a BSD-style
|
||||||
|
license that can be found in the LICENSE file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
constants.go provides package-level constants for metrics.
|
||||||
|
*/
|
||||||
|
package metrics
|
||||||
|
|
||||||
|
const (
|
||||||
|
valueKey = "value"
|
||||||
|
gaugeTypeValue = "gauge"
|
||||||
|
typeKey = "type"
|
||||||
|
histogramTypeValue = "histogram"
|
||||||
|
floatFormat = 'f'
|
||||||
|
floatPrecision = 6
|
||||||
|
floatBitCount = 64
|
||||||
|
)
|
|
@ -0,0 +1,51 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2012, Matt T. Proud
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Use of this source code is governed by a BSD-style
|
||||||
|
license that can be found in the LICENSE file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
The metrics package provides general descriptors for the concept of exportable
|
||||||
|
metrics.
|
||||||
|
|
||||||
|
accumulating_bucket.go provides a histogram bucket type that accumulates
|
||||||
|
elements until a given capacity and enacts a given eviction policy upon
|
||||||
|
such a condition.
|
||||||
|
|
||||||
|
accumulating_bucket_test.go provides a test complement for the
|
||||||
|
accumulating_bucket_go module.
|
||||||
|
|
||||||
|
eviction.go provides several histogram bucket eviction strategies.
|
||||||
|
|
||||||
|
eviction_test.go provides a test complement for the eviction.go module.
|
||||||
|
|
||||||
|
gauge.go provides a scalar metric that one can monitor. It is useful for
|
||||||
|
certain cases, such as instantaneous temperature.
|
||||||
|
|
||||||
|
gauge_test.go provides a test complement for the gauge.go module.
|
||||||
|
|
||||||
|
histogram.go provides a basic histogram metric, which can accumulate scalar
|
||||||
|
event values or samples. The underlying histogram implementation is designed
|
||||||
|
to be performant in that it accepts tolerable inaccuracies.
|
||||||
|
|
||||||
|
histogram_test.go provides a test complement for the histogram.go module.
|
||||||
|
|
||||||
|
metric.go provides fundamental interface expectations for the various metrics.
|
||||||
|
|
||||||
|
metrics_test.go provides a test suite for all tests in the metrics package
|
||||||
|
hierarchy. It employs the gocheck framework for test scaffolding.
|
||||||
|
|
||||||
|
tallying_bucket.go provides a histogram bucket type that aggregates tallies
|
||||||
|
of events that fall into its ranges versus a summary of the values
|
||||||
|
themselves.
|
||||||
|
|
||||||
|
tallying_bucket_test.go provides a test complement for the
|
||||||
|
tallying_bucket.go module.
|
||||||
|
|
||||||
|
timer.go provides a scalar metric that times how long a given event takes.
|
||||||
|
|
||||||
|
timer_test.go provides a test complement for the timer.go module.
|
||||||
|
*/
|
||||||
|
package metrics
|
|
@ -1,10 +1,10 @@
|
||||||
// Copyright (c) 2012, Matt T. Proud
|
/*
|
||||||
// All rights reserved.
|
Copyright (c) 2012, Matt T. Proud
|
||||||
//
|
All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// eviction.go provides several histogram bucket eviction strategies.
|
Use of this source code is governed by a BSD-style
|
||||||
|
license that can be found in the LICENSE file.
|
||||||
|
*/
|
||||||
|
|
||||||
package metrics
|
package metrics
|
||||||
|
|
||||||
|
@ -15,12 +15,16 @@ import (
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// EvictionPolicy implements some sort of garbage collection methodology for
|
/*
|
||||||
// an underlying heap.Interface. This is presently only used for
|
EvictionPolicy implements some sort of garbage collection methodology for
|
||||||
// AccumulatingBucket.
|
an underlying heap.Interface. This is presently only used for
|
||||||
|
AccumulatingBucket.
|
||||||
|
*/
|
||||||
type EvictionPolicy func(h heap.Interface)
|
type EvictionPolicy func(h heap.Interface)
|
||||||
|
|
||||||
// As the name implies, this evicts the oldest x objects from the heap.
|
/*
|
||||||
|
As the name implies, this evicts the oldest x objects from the heap.
|
||||||
|
*/
|
||||||
func EvictOldest(count int) EvictionPolicy {
|
func EvictOldest(count int) EvictionPolicy {
|
||||||
return func(h heap.Interface) {
|
return func(h heap.Interface) {
|
||||||
for i := 0; i < count; i++ {
|
for i := 0; i < count; i++ {
|
||||||
|
@ -29,10 +33,10 @@ func EvictOldest(count int) EvictionPolicy {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// This factory produces an EvictionPolicy that applies some standardized
|
/*
|
||||||
// reduction methodology on the to-be-terminated values.
|
This factory produces an EvictionPolicy that applies some standardized
|
||||||
//
|
reduction methodology on the to-be-terminated values.
|
||||||
// TODO(mtp): Parameterize the priority generation since these tools are useful.
|
*/
|
||||||
func EvictAndReplaceWith(count int, reducer maths.ReductionMethod) EvictionPolicy {
|
func EvictAndReplaceWith(count int, reducer maths.ReductionMethod) EvictionPolicy {
|
||||||
return func(h heap.Interface) {
|
return func(h heap.Interface) {
|
||||||
oldValues := make([]float64, count)
|
oldValues := make([]float64, count)
|
||||||
|
@ -45,6 +49,9 @@ func EvictAndReplaceWith(count int, reducer maths.ReductionMethod) EvictionPolic
|
||||||
|
|
||||||
heap.Push(h, &utility.Item{
|
heap.Push(h, &utility.Item{
|
||||||
Value: reduced,
|
Value: reduced,
|
||||||
|
/*
|
||||||
|
TODO(mtp): Parameterize the priority generation since these tools are useful.
|
||||||
|
*/
|
||||||
Priority: -1 * time.Now().UnixNano(),
|
Priority: -1 * time.Now().UnixNano(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
// Copyright (c) 2012, Matt T. Proud
|
/*
|
||||||
// All rights reserved.
|
Copyright (c) 2012, Matt T. Proud
|
||||||
//
|
All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// eviction_test.go provides a test complement for the eviction.go module.
|
Use of this source code is governed by a BSD-style
|
||||||
|
license that can be found in the LICENSE file.
|
||||||
|
*/
|
||||||
|
|
||||||
package metrics
|
package metrics
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
// Copyright (c) 2012, Matt T. Proud
|
/*
|
||||||
// All rights reserved.
|
Copyright (c) 2012, Matt T. Proud
|
||||||
//
|
All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// gauge.go provides a scalar metric that one can monitor. It is useful for
|
Use of this source code is governed by a BSD-style
|
||||||
// certain cases, such as instantaneous temperature.
|
license that can be found in the LICENSE file.
|
||||||
|
*/
|
||||||
|
|
||||||
package metrics
|
package metrics
|
||||||
|
|
||||||
|
@ -14,10 +13,12 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
// A gauge metric merely provides an instantaneous representation of a scalar
|
/*
|
||||||
// value or an accumulation. For instance, if one wants to expose the current
|
A gauge metric merely provides an instantaneous representation of a scalar
|
||||||
// temperature or the hitherto bandwidth used, this would be the metric for such
|
value or an accumulation. For instance, if one wants to expose the current
|
||||||
// circumstances.
|
temperature or the hitherto bandwidth used, this would be the metric for such
|
||||||
|
circumstances.
|
||||||
|
*/
|
||||||
type GaugeMetric struct {
|
type GaugeMetric struct {
|
||||||
value float64
|
value float64
|
||||||
mutex sync.RWMutex
|
mutex sync.RWMutex
|
||||||
|
@ -80,8 +81,8 @@ func (metric *GaugeMetric) Marshallable() map[string]interface{} {
|
||||||
|
|
||||||
v := make(map[string]interface{}, 2)
|
v := make(map[string]interface{}, 2)
|
||||||
|
|
||||||
v["value"] = metric.value
|
v[valueKey] = metric.value
|
||||||
v["type"] = "gauge"
|
v[typeKey] = gaugeTypeValue
|
||||||
|
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
// Copyright (c) 2012, Matt T. Proud
|
/*
|
||||||
// All rights reserved.
|
Copyright (c) 2012, Matt T. Proud
|
||||||
//
|
All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// gauge_test.go provides a test complement for the gauge.go module.
|
Use of this source code is governed by a BSD-style
|
||||||
|
license that can be found in the LICENSE file.
|
||||||
|
*/
|
||||||
|
|
||||||
package metrics
|
package metrics
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,10 @@
|
||||||
// Copyright (c) 2012, Matt T. Proud
|
/*
|
||||||
// All rights reserved.
|
Copyright (c) 2012, Matt T. Proud
|
||||||
//
|
All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// histogram.go provides a basic histogram metric, which can accumulate scalar
|
Use of this source code is governed by a BSD-style
|
||||||
// event values or samples. The underlying histogram implementation is designed
|
license that can be found in the LICENSE file.
|
||||||
// to be performant in that it accepts tolerable inaccuracies.
|
*/
|
||||||
|
|
||||||
package metrics
|
package metrics
|
||||||
|
|
||||||
|
@ -17,9 +15,11 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
// This generates count-buckets of equal size distributed along the open
|
/*
|
||||||
// interval of lower to upper. For instance, {lower=0, upper=10, count=5}
|
This generates count-buckets of equal size distributed along the open
|
||||||
// yields the following: [0, 2, 4, 6, 8].
|
interval of lower to upper. For instance, {lower=0, upper=10, count=5}
|
||||||
|
yields the following: [0, 2, 4, 6, 8].
|
||||||
|
*/
|
||||||
func EquallySizedBucketsFor(lower, upper float64, count int) []float64 {
|
func EquallySizedBucketsFor(lower, upper float64, count int) []float64 {
|
||||||
buckets := make([]float64, count)
|
buckets := make([]float64, count)
|
||||||
|
|
||||||
|
@ -33,8 +33,10 @@ func EquallySizedBucketsFor(lower, upper float64, count int) []float64 {
|
||||||
return buckets
|
return buckets
|
||||||
}
|
}
|
||||||
|
|
||||||
// This generates log2-sized buckets spanning from lower to upper inclusively
|
/*
|
||||||
// as well as values beyond it.
|
This generates log2-sized buckets spanning from lower to upper inclusively
|
||||||
|
as well as values beyond it.
|
||||||
|
*/
|
||||||
func LogarithmicSizedBucketsFor(lower, upper float64) []float64 {
|
func LogarithmicSizedBucketsFor(lower, upper float64) []float64 {
|
||||||
bucketCount := int(math.Ceil(math.Log2(upper)))
|
bucketCount := int(math.Ceil(math.Log2(upper)))
|
||||||
|
|
||||||
|
@ -47,36 +49,46 @@ func LogarithmicSizedBucketsFor(lower, upper float64) []float64 {
|
||||||
return buckets
|
return buckets
|
||||||
}
|
}
|
||||||
|
|
||||||
// A HistogramSpecification defines how a Histogram is to be built.
|
/*
|
||||||
|
A HistogramSpecification defines how a Histogram is to be built.
|
||||||
|
*/
|
||||||
type HistogramSpecification struct {
|
type HistogramSpecification struct {
|
||||||
Starts []float64
|
Starts []float64
|
||||||
BucketMaker BucketBuilder
|
BucketMaker BucketBuilder
|
||||||
ReportablePercentiles []float64
|
ReportablePercentiles []float64
|
||||||
}
|
}
|
||||||
|
|
||||||
// The histogram is an accumulator for samples. It merely routes into which
|
/*
|
||||||
// to bucket to capture an event and provides a percentile calculation
|
The histogram is an accumulator for samples. It merely routes into which
|
||||||
// mechanism.
|
to bucket to capture an event and provides a percentile calculation
|
||||||
//
|
mechanism.
|
||||||
// Histogram makes do without locking by employing the law of large numbers
|
|
||||||
// to presume a convergence toward a given bucket distribution. Locking
|
Histogram makes do without locking by employing the law of large numbers
|
||||||
// may be implemented in the buckets themselves, though.
|
to presume a convergence toward a given bucket distribution. Locking
|
||||||
|
may be implemented in the buckets themselves, though.
|
||||||
|
*/
|
||||||
type Histogram struct {
|
type Histogram struct {
|
||||||
// This represents the open interval's start at which values shall be added to
|
/*
|
||||||
// the bucket. The interval continues until the beginning of the next bucket
|
This represents the open interval's start at which values shall be added to
|
||||||
// exclusive or positive infinity.
|
the bucket. The interval continues until the beginning of the next bucket
|
||||||
//
|
exclusive or positive infinity.
|
||||||
// N.B.
|
|
||||||
// - bucketStarts should be sorted in ascending order;
|
N.B.
|
||||||
// - len(bucketStarts) must be equivalent to len(buckets);
|
- bucketStarts should be sorted in ascending order;
|
||||||
// - The index of a given bucketStarts' element is presumed to match
|
- len(bucketStarts) must be equivalent to len(buckets);
|
||||||
// correspond to the appropriate element in buckets.
|
- The index of a given bucketStarts' element is presumed to match
|
||||||
|
correspond to the appropriate element in buckets.
|
||||||
|
*/
|
||||||
bucketStarts []float64
|
bucketStarts []float64
|
||||||
// These are the buckets that capture samples as they are emitted to the
|
/*
|
||||||
// histogram. Please consult the reference interface and its implements for
|
These are the buckets that capture samples as they are emitted to the
|
||||||
// further details about behavior expectations.
|
histogram. Please consult the reference interface and its implements for
|
||||||
|
further details about behavior expectations.
|
||||||
|
*/
|
||||||
buckets []Bucket
|
buckets []Bucket
|
||||||
// These are the percentile values that will be reported on marshalling.
|
/*
|
||||||
|
These are the percentile values that will be reported on marshalling.
|
||||||
|
*/
|
||||||
reportablePercentiles []float64
|
reportablePercentiles []float64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,7 +120,9 @@ func (h *Histogram) Humanize() string {
|
||||||
return string(stringBuffer.Bytes())
|
return string(stringBuffer.Bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine the number of previous observations up to a given index.
|
/*
|
||||||
|
Determine the number of previous observations up to a given index.
|
||||||
|
*/
|
||||||
func previousCumulativeObservations(cumulativeObservations []int, bucketIndex int) int {
|
func previousCumulativeObservations(cumulativeObservations []int, bucketIndex int) int {
|
||||||
if bucketIndex == 0 {
|
if bucketIndex == 0 {
|
||||||
return 0
|
return 0
|
||||||
|
@ -117,12 +131,16 @@ func previousCumulativeObservations(cumulativeObservations []int, bucketIndex in
|
||||||
return cumulativeObservations[bucketIndex-1]
|
return cumulativeObservations[bucketIndex-1]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine the index for an element given a percentage of length.
|
/*
|
||||||
|
Determine the index for an element given a percentage of length.
|
||||||
|
*/
|
||||||
func prospectiveIndexForPercentile(percentile float64, totalObservations int) int {
|
func prospectiveIndexForPercentile(percentile float64, totalObservations int) int {
|
||||||
return int(percentile * float64(totalObservations-1))
|
return int(percentile * float64(totalObservations-1))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine the next bucket element when interim bucket intervals may be empty.
|
/*
|
||||||
|
Determine the next bucket element when interim bucket intervals may be empty.
|
||||||
|
*/
|
||||||
func (h *Histogram) nextNonEmptyBucketElement(currentIndex, bucketCount int, observationsByBucket []int) (*Bucket, int) {
|
func (h *Histogram) nextNonEmptyBucketElement(currentIndex, bucketCount int, observationsByBucket []int) (*Bucket, int) {
|
||||||
for i := currentIndex; i < bucketCount; i++ {
|
for i := currentIndex; i < bucketCount; i++ {
|
||||||
if observationsByBucket[i] == 0 {
|
if observationsByBucket[i] == 0 {
|
||||||
|
@ -135,18 +153,24 @@ func (h *Histogram) nextNonEmptyBucketElement(currentIndex, bucketCount int, obs
|
||||||
panic("Illegal Condition: There were no remaining buckets to provide a value.")
|
panic("Illegal Condition: There were no remaining buckets to provide a value.")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find what bucket and element index contains a given percentile value.
|
/*
|
||||||
// If a percentile is requested that results in a corresponding index that is no
|
Find what bucket and element index contains a given percentile value.
|
||||||
// longer contained by the bucket, the index of the last item is returned. This
|
If a percentile is requested that results in a corresponding index that is no
|
||||||
// may occur if the underlying bucket catalogs values and employs an eviction
|
longer contained by the bucket, the index of the last item is returned. This
|
||||||
// strategy.
|
may occur if the underlying bucket catalogs values and employs an eviction
|
||||||
|
strategy.
|
||||||
|
*/
|
||||||
func (h *Histogram) bucketForPercentile(percentile float64) (*Bucket, int) {
|
func (h *Histogram) bucketForPercentile(percentile float64) (*Bucket, int) {
|
||||||
bucketCount := len(h.buckets)
|
bucketCount := len(h.buckets)
|
||||||
|
|
||||||
// This captures the quantity of samples in a given bucket's range.
|
/*
|
||||||
|
This captures the quantity of samples in a given bucket's range.
|
||||||
|
*/
|
||||||
observationsByBucket := make([]int, bucketCount)
|
observationsByBucket := make([]int, bucketCount)
|
||||||
// This captures the cumulative quantity of observations from all preceding
|
/*
|
||||||
// buckets up and to the end of this bucket.
|
This captures the cumulative quantity of observations from all preceding
|
||||||
|
buckets up and to the end of this bucket.
|
||||||
|
*/
|
||||||
cumulativeObservationsByBucket := make([]int, bucketCount)
|
cumulativeObservationsByBucket := make([]int, bucketCount)
|
||||||
|
|
||||||
var totalObservations int = 0
|
var totalObservations int = 0
|
||||||
|
@ -158,9 +182,11 @@ func (h *Histogram) bucketForPercentile(percentile float64) (*Bucket, int) {
|
||||||
cumulativeObservationsByBucket[i] = totalObservations
|
cumulativeObservationsByBucket[i] = totalObservations
|
||||||
}
|
}
|
||||||
|
|
||||||
// This captures the index offset where the given percentile value would be
|
/*
|
||||||
// were all submitted samples stored and never down-/re-sampled nor deleted
|
This captures the index offset where the given percentile value would be
|
||||||
// and housed in a singular array.
|
were all submitted samples stored and never down-/re-sampled nor deleted
|
||||||
|
and housed in a singular array.
|
||||||
|
*/
|
||||||
prospectiveIndex := prospectiveIndexForPercentile(percentile, totalObservations)
|
prospectiveIndex := prospectiveIndexForPercentile(percentile, totalObservations)
|
||||||
|
|
||||||
for i, cumulativeObservation := range cumulativeObservationsByBucket {
|
for i, cumulativeObservation := range cumulativeObservationsByBucket {
|
||||||
|
@ -168,15 +194,21 @@ func (h *Histogram) bucketForPercentile(percentile float64) (*Bucket, int) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find the bucket that contains the given index.
|
/*
|
||||||
|
Find the bucket that contains the given index.
|
||||||
|
*/
|
||||||
if cumulativeObservation >= prospectiveIndex {
|
if cumulativeObservation >= prospectiveIndex {
|
||||||
var subIndex int
|
var subIndex int
|
||||||
// This calculates the index within the current bucket where the given
|
/*
|
||||||
// percentile may be found.
|
This calculates the index within the current bucket where the given
|
||||||
|
percentile may be found.
|
||||||
|
*/
|
||||||
subIndex = prospectiveIndex - previousCumulativeObservations(cumulativeObservationsByBucket, i)
|
subIndex = prospectiveIndex - previousCumulativeObservations(cumulativeObservationsByBucket, i)
|
||||||
|
|
||||||
// Sometimes the index may be the last item, in which case we need to
|
/*
|
||||||
// take this into account.
|
Sometimes the index may be the last item, in which case we need to
|
||||||
|
take this into account.
|
||||||
|
*/
|
||||||
if observationsByBucket[i] == subIndex {
|
if observationsByBucket[i] == subIndex {
|
||||||
return h.nextNonEmptyBucketElement(i+1, bucketCount, observationsByBucket)
|
return h.nextNonEmptyBucketElement(i+1, bucketCount, observationsByBucket)
|
||||||
}
|
}
|
||||||
|
@ -188,34 +220,42 @@ func (h *Histogram) bucketForPercentile(percentile float64) (*Bucket, int) {
|
||||||
return &h.buckets[0], 0
|
return &h.buckets[0], 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the histogram's estimate of the value for a given percentile of
|
/*
|
||||||
// collected samples. The requested percentile is expected to be a real
|
Return the histogram's estimate of the value for a given percentile of
|
||||||
// value within (0, 1.0].
|
collected samples. The requested percentile is expected to be a real
|
||||||
|
value within (0, 1.0].
|
||||||
|
*/
|
||||||
func (h *Histogram) Percentile(percentile float64) float64 {
|
func (h *Histogram) Percentile(percentile float64) float64 {
|
||||||
bucket, index := h.bucketForPercentile(percentile)
|
bucket, index := h.bucketForPercentile(percentile)
|
||||||
|
|
||||||
return (*bucket).ValueForIndex(index)
|
return (*bucket).ValueForIndex(index)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func formatFloat(value float64) string {
|
||||||
|
return strconv.FormatFloat(value, floatFormat, floatPrecision, floatBitCount)
|
||||||
|
}
|
||||||
|
|
||||||
func (h *Histogram) Marshallable() map[string]interface{} {
|
func (h *Histogram) Marshallable() map[string]interface{} {
|
||||||
numberOfPercentiles := len(h.reportablePercentiles)
|
numberOfPercentiles := len(h.reportablePercentiles)
|
||||||
result := make(map[string]interface{}, 2)
|
result := make(map[string]interface{}, 2)
|
||||||
|
|
||||||
result["type"] = "histogram"
|
result[typeKey] = histogramTypeValue
|
||||||
|
|
||||||
value := make(map[string]interface{}, numberOfPercentiles)
|
value := make(map[string]interface{}, numberOfPercentiles)
|
||||||
|
|
||||||
for _, percentile := range h.reportablePercentiles {
|
for _, percentile := range h.reportablePercentiles {
|
||||||
percentileString := strconv.FormatFloat(percentile, 'f', 6, 64)
|
percentileString := formatFloat(percentile)
|
||||||
value[percentileString] = strconv.FormatFloat(h.Percentile(percentile), 'f', 6, 64)
|
value[percentileString] = formatFloat(h.Percentile(percentile))
|
||||||
}
|
}
|
||||||
|
|
||||||
result["value"] = value
|
result[valueKey] = value
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
// Produce a histogram from a given specification.
|
/*
|
||||||
|
Produce a histogram from a given specification.
|
||||||
|
*/
|
||||||
func CreateHistogram(specification *HistogramSpecification) *Histogram {
|
func CreateHistogram(specification *HistogramSpecification) *Histogram {
|
||||||
bucketCount := len(specification.Starts)
|
bucketCount := len(specification.Starts)
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
// Copyright (c) 2012, Matt T. Proud
|
/*
|
||||||
// All rights reserved.
|
Copyright (c) 2012, Matt T. Proud
|
||||||
//
|
All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// histogram_test.go provides a test complement for the histogram.go module.
|
Use of this source code is governed by a BSD-style
|
||||||
|
license that can be found in the LICENSE file.
|
||||||
|
*/
|
||||||
|
|
||||||
package metrics
|
package metrics
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2012, Matt T. Proud
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Use of this source code is governed by a BSD-style
|
||||||
|
license that can be found in the LICENSE file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package metrics
|
||||||
|
|
||||||
|
/*
|
||||||
|
A Metric is something that can be exposed via the registry framework.
|
||||||
|
*/
|
||||||
|
type Metric interface {
|
||||||
|
/*
|
||||||
|
Produce a human-consumable representation of the metric.
|
||||||
|
*/
|
||||||
|
Humanize() string
|
||||||
|
/*
|
||||||
|
Produce a JSON-consumable representation of the metric.
|
||||||
|
*/
|
||||||
|
Marshallable() map[string]interface{}
|
||||||
|
}
|
|
@ -1,11 +1,10 @@
|
||||||
// Copyright (c) 2012, Matt T. Proud
|
/*
|
||||||
// All rights reserved.
|
Copyright (c) 2012, Matt T. Proud
|
||||||
//
|
All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// metrics_test.go provides a test suite for all tests in the metrics package
|
Use of this source code is governed by a BSD-style
|
||||||
// hierarchy. It employs the gocheck framework for test scaffolding.
|
license that can be found in the LICENSE file.
|
||||||
|
*/
|
||||||
|
|
||||||
package metrics
|
package metrics
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,10 @@
|
||||||
// Copyright (c) 2012, Matt T. Proud
|
/*
|
||||||
// All rights reserved.
|
Copyright (c) 2012, Matt T. Proud
|
||||||
//
|
All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// tallying_bucket.go provides a histogram bucket type that aggregates tallies
|
Use of this source code is governed by a BSD-style
|
||||||
// of events that fall into its ranges versus a summary of the values
|
license that can be found in the LICENSE file.
|
||||||
// themselves.
|
*/
|
||||||
|
|
||||||
package metrics
|
package metrics
|
||||||
|
|
||||||
|
@ -22,13 +20,17 @@ const (
|
||||||
upperThird = 2.0 * lowerThird
|
upperThird = 2.0 * lowerThird
|
||||||
)
|
)
|
||||||
|
|
||||||
// A TallyingIndexEstimator is responsible for estimating the value of index for
|
/*
|
||||||
// a given TallyingBucket, even though a TallyingBucket does not possess a
|
A TallyingIndexEstimator is responsible for estimating the value of index for
|
||||||
// collection of samples. There are a few strategies listed below for how
|
a given TallyingBucket, even though a TallyingBucket does not possess a
|
||||||
// this value should be approximated.
|
collection of samples. There are a few strategies listed below for how
|
||||||
|
this value should be approximated.
|
||||||
|
*/
|
||||||
type TallyingIndexEstimator func(minimum, maximum float64, index, observations int) float64
|
type TallyingIndexEstimator func(minimum, maximum float64, index, observations int) float64
|
||||||
|
|
||||||
// Provide a filter for handling empty buckets.
|
/*
|
||||||
|
Provide a filter for handling empty buckets.
|
||||||
|
*/
|
||||||
func emptyFilter(e TallyingIndexEstimator) TallyingIndexEstimator {
|
func emptyFilter(e TallyingIndexEstimator) TallyingIndexEstimator {
|
||||||
return func(minimum, maximum float64, index, observations int) float64 {
|
return func(minimum, maximum float64, index, observations int) float64 {
|
||||||
if observations == 0 {
|
if observations == 0 {
|
||||||
|
@ -39,23 +41,31 @@ func emptyFilter(e TallyingIndexEstimator) TallyingIndexEstimator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Report the smallest observed value in the bucket.
|
/*
|
||||||
|
Report the smallest observed value in the bucket.
|
||||||
|
*/
|
||||||
var Minimum TallyingIndexEstimator = emptyFilter(func(minimum, maximum float64, _, observations int) float64 {
|
var Minimum TallyingIndexEstimator = emptyFilter(func(minimum, maximum float64, _, observations int) float64 {
|
||||||
return minimum
|
return minimum
|
||||||
})
|
})
|
||||||
|
|
||||||
// Report the largest observed value in the bucket.
|
/*
|
||||||
|
Report the largest observed value in the bucket.
|
||||||
|
*/
|
||||||
var Maximum TallyingIndexEstimator = emptyFilter(func(minimum, maximum float64, _, observations int) float64 {
|
var Maximum TallyingIndexEstimator = emptyFilter(func(minimum, maximum float64, _, observations int) float64 {
|
||||||
return maximum
|
return maximum
|
||||||
})
|
})
|
||||||
|
|
||||||
// Report the average of the extrema.
|
/*
|
||||||
|
Report the average of the extrema.
|
||||||
|
*/
|
||||||
var Average TallyingIndexEstimator = emptyFilter(func(minimum, maximum float64, _, observations int) float64 {
|
var Average TallyingIndexEstimator = emptyFilter(func(minimum, maximum float64, _, observations int) float64 {
|
||||||
return maths.Average([]float64{minimum, maximum})
|
return maths.Average([]float64{minimum, maximum})
|
||||||
})
|
})
|
||||||
|
|
||||||
// Report the minimum value of the index is in the lower-third of observations,
|
/*
|
||||||
// the average if in the middle-third, and the maximum if in the largest third.
|
Report the minimum value of the index is in the lower-third of observations,
|
||||||
|
the average if in the middle-third, and the maximum if in the largest third.
|
||||||
|
*/
|
||||||
var Uniform TallyingIndexEstimator = emptyFilter(func(minimum, maximum float64, index, observations int) float64 {
|
var Uniform TallyingIndexEstimator = emptyFilter(func(minimum, maximum float64, index, observations int) float64 {
|
||||||
if observations == 1 {
|
if observations == 1 {
|
||||||
return minimum
|
return minimum
|
||||||
|
@ -72,9 +82,11 @@ var Uniform TallyingIndexEstimator = emptyFilter(func(minimum, maximum float64,
|
||||||
return maths.Average([]float64{minimum, maximum})
|
return maths.Average([]float64{minimum, maximum})
|
||||||
})
|
})
|
||||||
|
|
||||||
// A TallyingBucket is a Bucket that tallies when an object is added to it.
|
/*
|
||||||
// Upon insertion, an object is compared against collected extrema and noted
|
A TallyingBucket is a Bucket that tallies when an object is added to it.
|
||||||
// as a new minimum or maximum if appropriate.
|
Upon insertion, an object is compared against collected extrema and noted
|
||||||
|
as a new minimum or maximum if appropriate.
|
||||||
|
*/
|
||||||
type TallyingBucket struct {
|
type TallyingBucket struct {
|
||||||
observations int
|
observations int
|
||||||
smallestObserved float64
|
smallestObserved float64
|
||||||
|
@ -119,7 +131,9 @@ func (b *TallyingBucket) ValueForIndex(index int) float64 {
|
||||||
return b.estimator(b.smallestObserved, b.largestObserved, index, b.observations)
|
return b.estimator(b.smallestObserved, b.largestObserved, index, b.observations)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Produce a TallyingBucket with sane defaults.
|
/*
|
||||||
|
Produce a TallyingBucket with sane defaults.
|
||||||
|
*/
|
||||||
func DefaultTallyingBucket() TallyingBucket {
|
func DefaultTallyingBucket() TallyingBucket {
|
||||||
return TallyingBucket{
|
return TallyingBucket{
|
||||||
smallestObserved: math.MaxFloat64,
|
smallestObserved: math.MaxFloat64,
|
||||||
|
@ -136,7 +150,9 @@ func CustomTallyingBucket(estimator TallyingIndexEstimator) TallyingBucket {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is used strictly for testing.
|
/*
|
||||||
|
This is used strictly for testing.
|
||||||
|
*/
|
||||||
func TallyingBucketBuilder() Bucket {
|
func TallyingBucketBuilder() Bucket {
|
||||||
b := DefaultTallyingBucket()
|
b := DefaultTallyingBucket()
|
||||||
return &b
|
return &b
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
// Copyright (c) 2012, Matt T. Proud
|
/*
|
||||||
// All rights reserved.
|
Copyright (c) 2012, Matt T. Proud
|
||||||
//
|
All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// tallying_bucket_test.go provides a test complement for the
|
Use of this source code is governed by a BSD-style
|
||||||
// tallying_bucket.go module.
|
license that can be found in the LICENSE file.
|
||||||
|
*/
|
||||||
|
|
||||||
package metrics
|
package metrics
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
// Copyright (c) 2012, Matt T. Proud
|
/*
|
||||||
// All rights reserved.
|
Copyright (c) 2012, Matt T. Proud
|
||||||
//
|
All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// timer.go provides a scalar metric that times how long a given event takes.
|
Use of this source code is governed by a BSD-style
|
||||||
|
license that can be found in the LICENSE file.
|
||||||
|
*/
|
||||||
|
|
||||||
package metrics
|
package metrics
|
||||||
|
|
||||||
|
@ -12,20 +12,33 @@ import (
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// This callback is called upon the completion of the timer—i.e., when it stops.
|
/*
|
||||||
|
This callback is called upon the completion of the timer—i.e., when it stops.
|
||||||
|
*/
|
||||||
type CompletionCallback func(duration time.Duration)
|
type CompletionCallback func(duration time.Duration)
|
||||||
|
|
||||||
// This is meant to capture a function that a StopWatch can call for purposes
|
/*
|
||||||
// of instrumentation.
|
This is meant to capture a function that a StopWatch can call for purposes
|
||||||
|
of instrumentation.
|
||||||
|
*/
|
||||||
type InstrumentableCall func()
|
type InstrumentableCall func()
|
||||||
|
|
||||||
|
/*
|
||||||
|
StopWatch is the structure that captures instrumentation for durations.
|
||||||
|
|
||||||
|
N.B.(mtp): A major limitation hereof is that the StopWatch protocol cannot
|
||||||
|
retain instrumentation if a panic percolates within the context that is
|
||||||
|
being measured.
|
||||||
|
*/
|
||||||
type StopWatch struct {
|
type StopWatch struct {
|
||||||
startTime time.Time
|
startTime time.Time
|
||||||
endTime time.Time
|
endTime time.Time
|
||||||
onCompletion CompletionCallback
|
onCompletion CompletionCallback
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return a new StopWatch that is ready for instrumentation.
|
/*
|
||||||
|
Return a new StopWatch that is ready for instrumentation.
|
||||||
|
*/
|
||||||
func Start(onCompletion CompletionCallback) *StopWatch {
|
func Start(onCompletion CompletionCallback) *StopWatch {
|
||||||
return &StopWatch{
|
return &StopWatch{
|
||||||
startTime: time.Now(),
|
startTime: time.Now(),
|
||||||
|
@ -33,8 +46,10 @@ func Start(onCompletion CompletionCallback) *StopWatch {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stop the StopWatch returning the elapsed duration of its lifetime while
|
/*
|
||||||
// firing an optional CompletionCallback in the background.
|
Stop the StopWatch returning the elapsed duration of its lifetime while
|
||||||
|
firing an optional CompletionCallback in the background.
|
||||||
|
*/
|
||||||
func (s *StopWatch) Stop() time.Duration {
|
func (s *StopWatch) Stop() time.Duration {
|
||||||
s.endTime = time.Now()
|
s.endTime = time.Now()
|
||||||
duration := s.endTime.Sub(s.startTime)
|
duration := s.endTime.Sub(s.startTime)
|
||||||
|
@ -46,8 +61,10 @@ func (s *StopWatch) Stop() time.Duration {
|
||||||
return duration
|
return duration
|
||||||
}
|
}
|
||||||
|
|
||||||
// Provide a quick way of instrumenting a InstrumentableCall and emitting its
|
/*
|
||||||
// duration.
|
Provide a quick way of instrumenting a InstrumentableCall and emitting its
|
||||||
|
duration.
|
||||||
|
*/
|
||||||
func InstrumentCall(instrumentable InstrumentableCall, onCompletion CompletionCallback) time.Duration {
|
func InstrumentCall(instrumentable InstrumentableCall, onCompletion CompletionCallback) time.Duration {
|
||||||
s := Start(onCompletion)
|
s := Start(onCompletion)
|
||||||
instrumentable()
|
instrumentable()
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
// Copyright (c) 2012, Matt T. Proud
|
/*
|
||||||
// All rights reserved.
|
Copyright (c) 2012, Matt T. Proud
|
||||||
//
|
All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// timer_test.go provides a test complement for the timer.go module.
|
Use of this source code is governed by a BSD-style
|
||||||
|
license that can be found in the LICENSE file.
|
||||||
|
*/
|
||||||
|
|
||||||
package metrics
|
package metrics
|
||||||
|
|
||||||
|
|
84
registry.go
84
registry.go
|
@ -1,11 +1,10 @@
|
||||||
// Copyright (c) 2012, Matt T. Proud
|
/*
|
||||||
// All rights reserved.
|
Copyright (c) 2012, Matt T. Proud
|
||||||
//
|
All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// registry.go provides a container for centralized exposition of metrics to
|
Use of this source code is governed by a BSD-style license that can be found in
|
||||||
// their prospective consumers.
|
the LICENSE file.
|
||||||
|
*/
|
||||||
|
|
||||||
package registry
|
package registry
|
||||||
|
|
||||||
|
@ -20,9 +19,18 @@ import (
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Boilerplate metrics about the metrics reporting subservice. These are only
|
const (
|
||||||
// exposed if the DefaultRegistry's exporter is hooked into the HTTP request
|
jsonContentType = "application/json"
|
||||||
// handler.
|
contentType = "Content-Type"
|
||||||
|
jsonSuffix = ".json"
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
Boilerplate metrics about the metrics reporting subservice. These are only
|
||||||
|
exposed if the DefaultRegistry's exporter is hooked into the HTTP request
|
||||||
|
handler.
|
||||||
|
*/
|
||||||
|
|
||||||
var requestCount *metrics.GaugeMetric = &metrics.GaugeMetric{}
|
var requestCount *metrics.GaugeMetric = &metrics.GaugeMetric{}
|
||||||
var requestLatencyLogarithmicBuckets []float64 = metrics.LogarithmicSizedBucketsFor(0, 1000)
|
var requestLatencyLogarithmicBuckets []float64 = metrics.LogarithmicSizedBucketsFor(0, 1000)
|
||||||
var requestLatencyEqualBuckets []float64 = metrics.EquallySizedBucketsFor(0, 1000, 10)
|
var requestLatencyEqualBuckets []float64 = metrics.EquallySizedBucketsFor(0, 1000, 10)
|
||||||
|
@ -47,8 +55,10 @@ var requestLatencyEqualTallying *metrics.Histogram = metrics.CreateHistogram(&me
|
||||||
ReportablePercentiles: []float64{0.01, 0.05, 0.5, 0.9, 0.99},
|
ReportablePercentiles: []float64{0.01, 0.05, 0.5, 0.9, 0.99},
|
||||||
})
|
})
|
||||||
|
|
||||||
// This callback accumulates the microsecond duration of the reporting
|
/*
|
||||||
// framework's overhead such that it can be reported.
|
This callback accumulates the microsecond duration of the reporting framework's
|
||||||
|
overhead such that it can be reported.
|
||||||
|
*/
|
||||||
var requestLatencyAccumulator metrics.CompletionCallback = func(duration time.Duration) {
|
var requestLatencyAccumulator metrics.CompletionCallback = func(duration time.Duration) {
|
||||||
microseconds := float64(int64(duration) / 1E3)
|
microseconds := float64(int64(duration) / 1E3)
|
||||||
|
|
||||||
|
@ -58,33 +68,43 @@ var requestLatencyAccumulator metrics.CompletionCallback = func(duration time.Du
|
||||||
requestLatencyEqualTallying.Add(microseconds)
|
requestLatencyEqualTallying.Add(microseconds)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Registry is, as the name implies, a registrar where metrics are listed.
|
/*
|
||||||
//
|
Registry is, as the name implies, a registrar where metrics are listed.
|
||||||
// In most situations, using DefaultRegistry is sufficient versus creating
|
|
||||||
// one's own.
|
In most situations, using DefaultRegistry is sufficient versus creating one's
|
||||||
|
own.
|
||||||
|
*/
|
||||||
type Registry struct {
|
type Registry struct {
|
||||||
mutex sync.RWMutex
|
mutex sync.RWMutex
|
||||||
NameToMetric map[string]metrics.Metric
|
NameToMetric map[string]metrics.Metric
|
||||||
}
|
}
|
||||||
|
|
||||||
// This builds a new metric registry. It is not needed in the majority of
|
/*
|
||||||
// cases.
|
This builds a new metric registry. It is not needed in the majority of
|
||||||
|
cases.
|
||||||
|
*/
|
||||||
func NewRegistry() *Registry {
|
func NewRegistry() *Registry {
|
||||||
return &Registry{
|
return &Registry{
|
||||||
NameToMetric: make(map[string]metrics.Metric),
|
NameToMetric: make(map[string]metrics.Metric),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is the default registry with which Metric objects are associated. It
|
/*
|
||||||
// is primarily a read-only object after server instantiation.
|
This is the default registry with which Metric objects are associated. It
|
||||||
|
is primarily a read-only object after server instantiation.
|
||||||
|
*/
|
||||||
var DefaultRegistry = NewRegistry()
|
var DefaultRegistry = NewRegistry()
|
||||||
|
|
||||||
// Associate a Metric with the DefaultRegistry.
|
/*
|
||||||
|
Associate a Metric with the DefaultRegistry.
|
||||||
|
*/
|
||||||
func Register(name string, metric metrics.Metric) {
|
func Register(name string, metric metrics.Metric) {
|
||||||
DefaultRegistry.Register(name, metric)
|
DefaultRegistry.Register(name, metric)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register a metric with a given name. Name should be globally unique.
|
/*
|
||||||
|
Register a metric with a given name. Name should be globally unique.
|
||||||
|
*/
|
||||||
func (r *Registry) Register(name string, metric metrics.Metric) {
|
func (r *Registry) Register(name string, metric metrics.Metric) {
|
||||||
r.mutex.Lock()
|
r.mutex.Lock()
|
||||||
defer r.mutex.Unlock()
|
defer r.mutex.Unlock()
|
||||||
|
@ -97,17 +117,19 @@ func (r *Registry) Register(name string, metric metrics.Metric) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a http.HandlerFunc that is tied to r Registry such that requests
|
/*
|
||||||
// against it generate a representation of the housed metrics.
|
Create a http.HandlerFunc that is tied to r Registry such that requests
|
||||||
|
against it generate a representation of the housed metrics.
|
||||||
|
*/
|
||||||
func (registry *Registry) YieldExporter() http.HandlerFunc {
|
func (registry *Registry) YieldExporter() http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
var instrumentable metrics.InstrumentableCall = func() {
|
var instrumentable metrics.InstrumentableCall = func() {
|
||||||
requestCount.Increment()
|
requestCount.Increment()
|
||||||
url := r.URL
|
url := r.URL
|
||||||
|
|
||||||
if strings.HasSuffix(url.Path, ".json") {
|
if strings.HasSuffix(url.Path, jsonSuffix) {
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set(contentType, jsonContentType)
|
||||||
composite := make(map[string]interface{}, len(registry.NameToMetric))
|
composite := make(map[string]interface{}, len(registry.NameToMetric))
|
||||||
for name, metric := range registry.NameToMetric {
|
for name, metric := range registry.NameToMetric {
|
||||||
composite[name] = metric.Marshallable()
|
composite[name] = metric.Marshallable()
|
||||||
|
@ -126,9 +148,9 @@ func (registry *Registry) YieldExporter() http.HandlerFunc {
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
DefaultRegistry.Register("requests_total", requestCount)
|
DefaultRegistry.Register("requests_metrics_total", requestCount)
|
||||||
DefaultRegistry.Register("request_latency_logarithmic_accumulating_microseconds", requestLatencyLogarithmicAccumulating)
|
DefaultRegistry.Register("requests_metrics_latency_logarithmic_accumulating_microseconds", requestLatencyLogarithmicAccumulating)
|
||||||
DefaultRegistry.Register("request_latency_equal_accumulating_microseconds", requestLatencyEqualAccumulating)
|
DefaultRegistry.Register("requests_metrics_latency_equal_accumulating_microseconds", requestLatencyEqualAccumulating)
|
||||||
DefaultRegistry.Register("request_latency_logarithmic_tallying_microseconds", requestLatencyLogarithmicTallying)
|
DefaultRegistry.Register("requests_metrics_latency_logarithmic_tallying_microseconds", requestLatencyLogarithmicTallying)
|
||||||
DefaultRegistry.Register("request_latency_equal_tallying_microseconds", requestLatencyEqualTallying)
|
DefaultRegistry.Register("request_metrics_latency_equal_tallying_microseconds", requestLatencyEqualTallying)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2012, Matt T. Proud
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Use of this source code is governed by a BSD-style
|
||||||
|
license that can be found in the LICENSE file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
The utility package provides general purpose helpers to assist with this
|
||||||
|
library.
|
||||||
|
|
||||||
|
optional.go provides a mechanism for safely getting a set value or falling
|
||||||
|
back to defaults a la a Haskell and Scala Maybe or Guava Optional.
|
||||||
|
|
||||||
|
optional_test.go provides a test complement for the optional.go module.
|
||||||
|
|
||||||
|
priority_queue.go provides a simple priority queue.
|
||||||
|
|
||||||
|
priority_queue_test.go provides a test complement for the priority_queue.go
|
||||||
|
module.
|
||||||
|
|
||||||
|
test_helper.go provides a testing assistents for this package and its
|
||||||
|
dependents.
|
||||||
|
|
||||||
|
utility_test.go provides a test suite for all tests in the utility package
|
||||||
|
hierarchy. It employs the gocheck framework for test scaffolding.
|
||||||
|
*/
|
||||||
|
package documentation
|
|
@ -1,11 +1,10 @@
|
||||||
// Copyright (c) 2012, Matt T. Proud
|
/*
|
||||||
// All rights reserved.
|
Copyright (c) 2012, Matt T. Proud
|
||||||
//
|
All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// optional.go provides a mechanism for safely getting a set value or falling
|
Use of this source code is governed by a BSD-style
|
||||||
// back to defaults.
|
license that can be found in the LICENSE file.
|
||||||
|
*/
|
||||||
|
|
||||||
package utility
|
package utility
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
// Copyright (c) 2012, Matt T. Proud
|
/*
|
||||||
// All rights reserved.
|
Copyright (c) 2012, Matt T. Proud
|
||||||
//
|
All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// optional_test.go provides a test complement for the optional.go module.
|
Use of this source code is governed by a BSD-style
|
||||||
|
license that can be found in the LICENSE file.
|
||||||
|
*/
|
||||||
|
|
||||||
package utility
|
package utility
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
// Copyright (c) 2012, Matt T. Proud
|
/*
|
||||||
// All rights reserved.
|
Copyright (c) 2012, Matt T. Proud
|
||||||
//
|
All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// priority_queue.go provides a simple priority queue.
|
Use of this source code is governed by a BSD-style
|
||||||
|
license that can be found in the LICENSE file.
|
||||||
|
*/
|
||||||
|
|
||||||
package utility
|
package utility
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
// Copyright (c) 2012, Matt T. Proud
|
/*
|
||||||
// All rights reserved.
|
Copyright (c) 2012, Matt T. Proud
|
||||||
//
|
All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// priority_queue_test.go provides a test complement for the priority_queue.go
|
Use of this source code is governed by a BSD-style
|
||||||
// module.
|
license that can be found in the LICENSE file.
|
||||||
|
*/
|
||||||
|
|
||||||
package utility
|
package utility
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
// Copyright (c) 2012, Matt T. Proud
|
/*
|
||||||
// All rights reserved.
|
Copyright (c) 2012, Matt T. Proud
|
||||||
//
|
All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// test_helper.go provides a testing assistents for this package and its
|
Use of this source code is governed by a BSD-style
|
||||||
// dependents.
|
license that can be found in the LICENSE file.
|
||||||
|
*/
|
||||||
|
|
||||||
package utility
|
package utility
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
// Copyright (c) 2012, Matt T. Proud
|
/*
|
||||||
// All rights reserved.
|
Copyright (c) 2012, Matt T. Proud
|
||||||
//
|
All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// utility_test.go provides a test suite for all tests in the utility package
|
Use of this source code is governed by a BSD-style
|
||||||
// hierarchy. It employs the gocheck framework for test scaffolding.
|
license that can be found in the LICENSE file.
|
||||||
|
*/
|
||||||
|
|
||||||
package utility
|
package utility
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue