Rearrange file and package per convention.

WIP - Please review but do not merge.
This commit is contained in:
Matt T. Proud 2013-04-03 18:33:32 +02:00
parent a087e013a5
commit f320d28a6c
63 changed files with 716 additions and 677 deletions

View File

@ -1,8 +1,4 @@
language: go language: go
before_script:
- go get -v github.com/prometheus/client_golang
script: script:
- go build -a -v github.com/prometheus/client_golang/... - make -f Makefile.TRAVIS
- go test -v github.com/prometheus/client_golang/...

View File

@ -1,4 +1,4 @@
Copyright (c) 2012, Matt T. Proud Copyright (c) 2013, Prometheus Team
All rights reserved. All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
@ -19,4 +19,4 @@ ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -16,17 +16,20 @@ MAKE_ARTIFACTS = search_index
all: test all: test
build: build:
go build ./... $(MAKE) -C prometheus build
$(MAKE) -C examples build
test: build test: build
go test ./... $(GO_TEST_FLAGS) $(MAKE) -C prometheus test
$(MAKE) -C examples test
advice: test
$(MAKE) -C prometheus advice
$(MAKE) -C examples advice
format: format:
find . -iname '*.go' -exec gofmt -w -s=true '{}' ';' find . -iname '*.go' -exec gofmt -w -s=true '{}' ';'
advice:
go tool vet .
search_index: search_index:
godoc -index -write_index -index_files='search_index' godoc -index -write_index -index_files='search_index'
@ -34,6 +37,9 @@ documentation: search_index
godoc -http=:6060 -index -index_files='search_index' godoc -http=:6060 -index -index_files='search_index'
clean: clean:
$(MAKE) -C examples clean
rm -f $(MAKE_ARTIFACTS) rm -f $(MAKE_ARTIFACTS)
find . -iname '*~' -exec rm -f '{}' ';'
find . -iname '*#' -exec rm -f '{}' ';'
.PHONY: advice build clean documentation format test .PHONY: advice build clean documentation format test

28
Makefile.TRAVIS Normal file
View File

@ -0,0 +1,28 @@
# Copyright 2013 Prometheus Team
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
PROMETHEUS_TARGET := "${GOPATH}/src/github.com/prometheus"
all: test
preparation:
mkdir -vp $(PROMETHEUS_TARGET)
ln -sf "$(PWD)" $(PROMETHEUS_TARGET)
dependencies:
go get github.com/matttproud/gocheck
test: dependencies preparation
$(MAKE) test
.PHONY: dependencies preparation test

3
TODO
View File

@ -1,5 +1,2 @@
- Validate repository for Go code fluency and idiomatic adherence. - Validate repository for Go code fluency and idiomatic adherence.
- Decouple HTTP report handler from our project and incorporate into this
repository.
- Implement labeled metric support.
- Evaluate using atomic types versus locks. - Evaluate using atomic types versus locks.

View File

@ -1,11 +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.
// registry.go provides a container for centralized exposition of metrics to
// their prospective consumers.
//
// registry.Register("human_readable_metric_name", "metric docstring", map[string]string{"baseLabel": "baseLabelValue"}, metric)
package registry

View File

@ -1,8 +1,49 @@
// Copyright (c) 2013, Matt T. Proud // Copyright (c) 2013, Prometheus Team
// All rights reserved. // All rights reserved.
// //
// Use of this source code is governed by a BSD-style license that can be found // Use of this source code is governed by a BSD-style license that can be found
// in the LICENSE file. // in the LICENSE file.
// A repository of various Prometheus client documentation and advice. // A repository of various Prometheus client documentation and advice.
//
// 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 documentation package documentation

View File

@ -1,48 +0,0 @@
// Copyright (c) 2013, 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.
// 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 documentation

36
examples/Makefile Normal file
View File

@ -0,0 +1,36 @@
# Copyright 2013 Prometheus Team
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
all: test
build:
$(MAKE) -C delegator build
$(MAKE) -C random build
$(MAKE) -C simple build
test: build
$(MAKE) -C delegator test
$(MAKE) -C random test
$(MAKE) -C simple test
advice: test
$(MAKE) -C delegator advice
$(MAKE) -C random advice
$(MAKE) -C simple advice
clean:
$(MAKE) -C delegator clean
$(MAKE) -C random clean
$(MAKE) -C simple clean
.PHONY: advice build clean test

1
examples/delegator/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
delegator

View File

@ -0,0 +1,32 @@
# Copyright 2013 Prometheus Team
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
MAKE_ARTIFACTS = delegator
all: test
build: delegator
delegator:
go build .
test: build
go test . $(GO_TEST_FLAGS)
advice:
go tool vet .
clean:
rm -f $(MAKE_ARTIFACTS)
.PHONY: advice build clean test

View File

@ -1,4 +1,4 @@
// Copyright (c) 2012, Matt T. Proud // Copyright (c) 2013, Prometheus Team
// All rights reserved. // All rights reserved.
// //
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
@ -10,8 +10,8 @@ package main
import ( import (
"flag" "flag"
"github.com/prometheus/client_golang" "github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/exp" "github.com/prometheus/client_golang/prometheus/exp"
"net/http" "net/http"
) )
@ -48,7 +48,7 @@ func main() {
exp.HandleFunc("/hello", helloHandler) exp.HandleFunc("/hello", helloHandler)
exp.HandleFunc("/goodbye", goodbyeHandler) exp.HandleFunc("/goodbye", goodbyeHandler)
exp.HandleFunc("/teapot", teapotHandler) exp.HandleFunc("/teapot", teapotHandler)
exp.Handle(registry.ExpositionResource, registry.DefaultHandler) exp.Handle(prometheus.ExpositionResource, prometheus.DefaultHandler)
http.ListenAndServe(*listeningAddress, exp.DefaultCoarseMux) http.ListenAndServe(*listeningAddress, exp.DefaultCoarseMux)
} }

View File

@ -1,8 +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.
// Various Prometheus client examples.
package examples

1
examples/random/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
random

32
examples/random/Makefile Normal file
View File

@ -0,0 +1,32 @@
# Copyright 2013 Prometheus Team
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
MAKE_ARTIFACTS = random
all: test
build: random
random:
go build .
test: build
go test . $(GO_TEST_FLAGS)
advice:
go tool vet .
clean:
rm -f $(MAKE_ARTIFACTS)
.PHONY: advice clean build test

View File

@ -1,4 +1,4 @@
// Copyright (c) 2012, Matt T. Proud // Copyright (c) 2013, Prometheus Team
// All rights reserved. // All rights reserved.
// //
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
@ -13,9 +13,7 @@ package main
import ( import (
"flag" "flag"
"github.com/prometheus/client_golang" "github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/maths"
"github.com/prometheus/client_golang/metrics"
"math/rand" "math/rand"
"net/http" "net/http"
"time" "time"
@ -28,27 +26,27 @@ var (
// Create a histogram to track fictitious interservice RPC latency for three // Create a histogram to track fictitious interservice RPC latency for three
// distinct services. // distinct services.
rpcLatency = metrics.NewHistogram(&metrics.HistogramSpecification{ rpcLatency = prometheus.NewHistogram(&prometheus.HistogramSpecification{
// Four distinct histogram buckets for values: // Four distinct histogram buckets for values:
// - equally-sized, // - equally-sized,
// - 0 to 50, 50 to 100, 100 to 150, and 150 to 200. // - 0 to 50, 50 to 100, 100 to 150, and 150 to 200.
Starts: metrics.EquallySizedBucketsFor(0, 200, 4), Starts: prometheus.EquallySizedBucketsFor(0, 200, 4),
// Create histogram buckets using an accumulating bucket, a bucket that // Create histogram buckets using an accumulating bucket, a bucket that
// holds sample values subject to an eviction policy: // holds sample values subject to an eviction policy:
// - 50 elements are allowed per bucket. // - 50 elements are allowed per bucket.
// - Once 50 have been reached, the bucket empties 10 elements, averages the // - Once 50 have been reached, the bucket empties 10 elements, averages the
// evicted elements, and re-appends that back to the bucket. // evicted elements, and re-appends that back to the bucket.
BucketBuilder: metrics.AccumulatingBucketBuilder(metrics.EvictAndReplaceWith(10, maths.Average), 50), BucketBuilder: prometheus.AccumulatingBucketBuilder(prometheus.EvictAndReplaceWith(10, prometheus.AverageReducer), 50),
// The histogram reports percentiles 1, 5, 50, 90, and 99. // The histogram reports percentiles 1, 5, 50, 90, and 99.
ReportablePercentiles: []float64{0.01, 0.05, 0.5, 0.90, 0.99}, ReportablePercentiles: []float64{0.01, 0.05, 0.5, 0.90, 0.99},
}) })
rpcCalls = metrics.NewCounter() rpcCalls = prometheus.NewCounter()
// If for whatever reason you are resistant to the idea of having a static // If for whatever reason you are resistant to the idea of having a static
// registry for metrics, which is a really bad idea when using Prometheus- // registry for metrics, which is a really bad idea when using Prometheus-
// enabled library code, you can create your own. // enabled library code, you can create your own.
customRegistry = registry.NewRegistry() customRegistry = prometheus.NewRegistry()
) )
func main() { func main() {
@ -69,13 +67,13 @@ func main() {
} }
}() }()
http.Handle(registry.ExpositionResource, customRegistry.Handler()) http.Handle(prometheus.ExpositionResource, customRegistry.Handler())
http.ListenAndServe(*listeningAddress, nil) http.ListenAndServe(*listeningAddress, nil)
} }
func init() { func init() {
customRegistry.Register("rpc_latency_microseconds", "RPC latency.", registry.NilLabels, rpcLatency) customRegistry.Register("rpc_latency_microseconds", "RPC latency.", prometheus.NilLabels, rpcLatency)
customRegistry.Register("rpc_calls_total", "RPC calls.", registry.NilLabels, rpcCalls) customRegistry.Register("rpc_calls_total", "RPC calls.", prometheus.NilLabels, rpcCalls)
} }
var ( var (

1
examples/simple/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
simple

32
examples/simple/Makefile Normal file
View File

@ -0,0 +1,32 @@
# Copyright 2013 Prometheus Team
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
MAKE_ARTIFACTS = simple
all: test
build: simple
simple:
go build .
test: build
go test . $(GO_TEST_FLAGS)
advice:
go tool vet .
clean:
rm -f $(MAKE_ARTIFACTS)
.PHONY: advice build clean test

View File

@ -1,4 +1,4 @@
// Copyright (c) 2012, Matt T. Proud // Copyright (c) 2013, Prometheus Team
// All rights reserved. // All rights reserved.
// //
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
@ -10,14 +10,14 @@ package main
import ( import (
"flag" "flag"
"github.com/prometheus/client_golang" "github.com/prometheus/client_golang/prometheus"
"net/http" "net/http"
) )
func main() { func main() {
flag.Parse() flag.Parse()
http.Handle(registry.ExpositionResource, registry.DefaultHandler) http.Handle(prometheus.ExpositionResource, prometheus.DefaultHandler)
http.ListenAndServe(*listeningAddress, nil) http.ListenAndServe(*listeningAddress, nil)
} }

View File

@ -1,22 +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.
// 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

View File

@ -1,20 +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.
package maths
import (
. "github.com/matttproud/gocheck"
"testing"
)
type S struct{}
var _ = Suite(&S{})
func TestMaths(t *testing.T) {
TestingT(t)
}

View File

@ -1,20 +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.
// constants.go provides package-level constants for metrics.
package metrics
const (
counterTypeValue = "counter"
floatBitCount = 64
floatFormat = 'f'
floatPrecision = 6
gaugeTypeValue = "gauge"
histogramTypeValue = "histogram"
typeKey = "type"
valueKey = "value"
labelsKey = "labels"
)

View File

@ -1,48 +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.
// 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

View File

@ -1,179 +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.
package metrics
import (
"container/heap"
. "github.com/matttproud/gocheck"
"github.com/prometheus/client_golang/maths"
"github.com/prometheus/client_golang/utility"
)
func (s *S) TestEvictOldest(c *C) {
q := make(utility.PriorityQueue, 0, 10)
heap.Init(&q)
var e EvictionPolicy = EvictOldest(5)
for i := 0; i < 10; i++ {
var item utility.Item = utility.Item{
Priority: int64(i),
Value: float64(i),
}
heap.Push(&q, &item)
}
c.Check(q, HasLen, 10)
e(&q)
c.Check(q, HasLen, 5)
c.Check(heap.Pop(&q), utility.ValueEquals, 4.0)
c.Check(heap.Pop(&q), utility.ValueEquals, 3.0)
c.Check(heap.Pop(&q), utility.ValueEquals, 2.0)
c.Check(heap.Pop(&q), utility.ValueEquals, 1.0)
c.Check(heap.Pop(&q), utility.ValueEquals, 0.0)
}
func (s *S) TestEvictAndReplaceWithAverage(c *C) {
q := make(utility.PriorityQueue, 0, 10)
heap.Init(&q)
var e EvictionPolicy = EvictAndReplaceWith(5, maths.Average)
for i := 0; i < 10; i++ {
var item utility.Item = utility.Item{
Priority: int64(i),
Value: float64(i),
}
heap.Push(&q, &item)
}
c.Check(q, HasLen, 10)
e(&q)
c.Check(q, HasLen, 6)
c.Check(heap.Pop(&q), utility.ValueEquals, 4.0)
c.Check(heap.Pop(&q), utility.ValueEquals, 3.0)
c.Check(heap.Pop(&q), utility.ValueEquals, 2.0)
c.Check(heap.Pop(&q), utility.ValueEquals, 1.0)
c.Check(heap.Pop(&q), utility.ValueEquals, 0.0)
c.Check(heap.Pop(&q), utility.ValueEquals, 7.0)
}
func (s *S) TestEvictAndReplaceWithMedian(c *C) {
q := make(utility.PriorityQueue, 0, 10)
heap.Init(&q)
var e EvictionPolicy = EvictAndReplaceWith(5, maths.Median)
for i := 0; i < 10; i++ {
var item utility.Item = utility.Item{
Priority: int64(i),
Value: float64(i),
}
heap.Push(&q, &item)
}
c.Check(q, HasLen, 10)
e(&q)
c.Check(q, HasLen, 6)
c.Check(heap.Pop(&q), utility.ValueEquals, 4.0)
c.Check(heap.Pop(&q), utility.ValueEquals, 3.0)
c.Check(heap.Pop(&q), utility.ValueEquals, 2.0)
c.Check(heap.Pop(&q), utility.ValueEquals, 1.0)
c.Check(heap.Pop(&q), utility.ValueEquals, 0.0)
c.Check(heap.Pop(&q), utility.ValueEquals, 7.0)
}
func (s *S) TestEvictAndReplaceWithFirstMode(c *C) {
q := make(utility.PriorityQueue, 0, 10)
heap.Init(&q)
e := EvictAndReplaceWith(5, maths.FirstMode)
for i := 0; i < 10; i++ {
heap.Push(&q, &utility.Item{
Priority: int64(i),
Value: float64(i),
})
}
c.Check(q, HasLen, 10)
e(&q)
c.Check(q, HasLen, 6)
c.Check(heap.Pop(&q), utility.ValueEquals, 4.0)
c.Check(heap.Pop(&q), utility.ValueEquals, 3.0)
c.Check(heap.Pop(&q), utility.ValueEquals, 2.0)
c.Check(heap.Pop(&q), utility.ValueEquals, 1.0)
c.Check(heap.Pop(&q), utility.ValueEquals, 0.0)
c.Check(heap.Pop(&q), utility.ValueEquals, 9.0)
}
func (s *S) TestEvictAndReplaceWithMinimum(c *C) {
q := make(utility.PriorityQueue, 0, 10)
heap.Init(&q)
var e EvictionPolicy = EvictAndReplaceWith(5, maths.Minimum)
for i := 0; i < 10; i++ {
var item utility.Item = utility.Item{
Priority: int64(i),
Value: float64(i),
}
heap.Push(&q, &item)
}
c.Check(q, HasLen, 10)
e(&q)
c.Check(q, HasLen, 6)
c.Check(heap.Pop(&q), utility.ValueEquals, 4.0)
c.Check(heap.Pop(&q), utility.ValueEquals, 3.0)
c.Check(heap.Pop(&q), utility.ValueEquals, 2.0)
c.Check(heap.Pop(&q), utility.ValueEquals, 1.0)
c.Check(heap.Pop(&q), utility.ValueEquals, 0.0)
c.Check(heap.Pop(&q), utility.ValueEquals, 5.0)
}
func (s *S) TestEvictAndReplaceWithMaximum(c *C) {
q := make(utility.PriorityQueue, 0, 10)
heap.Init(&q)
var e EvictionPolicy = EvictAndReplaceWith(5, maths.Maximum)
for i := 0; i < 10; i++ {
var item utility.Item = utility.Item{
Priority: int64(i),
Value: float64(i),
}
heap.Push(&q, &item)
}
c.Check(q, HasLen, 10)
e(&q)
c.Check(q, HasLen, 6)
c.Check(heap.Pop(&q), utility.ValueEquals, 4.0)
c.Check(heap.Pop(&q), utility.ValueEquals, 3.0)
c.Check(heap.Pop(&q), utility.ValueEquals, 2.0)
c.Check(heap.Pop(&q), utility.ValueEquals, 1.0)
c.Check(heap.Pop(&q), utility.ValueEquals, 0.0)
c.Check(heap.Pop(&q), utility.ValueEquals, 9.0)
}

1
prometheus/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
command-line-arguments.test

25
prometheus/Makefile Normal file
View File

@ -0,0 +1,25 @@
# Copyright 2013 Prometheus Team
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
all: test
build:
go build ./...
test: build
go test ./... $(GO_TEST_FLAGS)
advice:
go tool vet .
.PHONY: advice build test

View File

@ -1,16 +1,15 @@
// Copyright (c) 2012, Matt T. Proud // Copyright (c) 2013, Prometheus Team
// All rights reserved. // All rights reserved.
// //
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package metrics package prometheus
import ( import (
"bytes" "bytes"
"container/heap" "container/heap"
"fmt" "fmt"
"github.com/prometheus/client_golang/utility"
"math" "math"
"sort" "sort"
"sync" "sync"
@ -18,7 +17,7 @@ import (
) )
type AccumulatingBucket struct { type AccumulatingBucket struct {
elements utility.PriorityQueue elements priorityQueue
evictionPolicy EvictionPolicy evictionPolicy EvictionPolicy
maximumSize int maximumSize int
mutex sync.RWMutex mutex sync.RWMutex
@ -31,7 +30,7 @@ type AccumulatingBucket struct {
func AccumulatingBucketBuilder(evictionPolicy EvictionPolicy, maximumSize int) BucketBuilder { func AccumulatingBucketBuilder(evictionPolicy EvictionPolicy, maximumSize int) BucketBuilder {
return func() Bucket { return func() Bucket {
return &AccumulatingBucket{ return &AccumulatingBucket{
elements: make(utility.PriorityQueue, 0, maximumSize), elements: make(priorityQueue, 0, maximumSize),
evictionPolicy: evictionPolicy, evictionPolicy: evictionPolicy,
maximumSize: maximumSize, maximumSize: maximumSize,
} }
@ -47,7 +46,7 @@ func (b *AccumulatingBucket) Add(value float64) {
b.observations++ b.observations++
size := len(b.elements) size := len(b.elements)
v := utility.Item{ v := item{
Priority: -1 * time.Now().UnixNano(), Priority: -1 * time.Now().UnixNano(),
Value: value, Value: value,
} }

View File

@ -1,15 +1,13 @@
// Copyright (c) 2012, Matt T. Proud // Copyright (c) 2013, Prometheus Team
// All rights reserved. // All rights reserved.
// //
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package metrics package prometheus
import ( import (
. "github.com/matttproud/gocheck" . "github.com/matttproud/gocheck"
"github.com/prometheus/client_golang/maths"
"github.com/prometheus/client_golang/utility"
"time" "time"
) )
@ -51,7 +49,7 @@ func (s *S) TestAccumulatingBucketBuilderWithEvictOldest(c *C) {
} }
func (s *S) TestAccumulatingBucketBuilderWithEvictAndReplaceWithAverage(c *C) { func (s *S) TestAccumulatingBucketBuilderWithEvictAndReplaceWithAverage(c *C) {
var evictAndReplaceWithAverage EvictionPolicy = EvictAndReplaceWith(3, maths.Average) var evictAndReplaceWithAverage EvictionPolicy = EvictAndReplaceWith(3, AverageReducer)
c.Assert(evictAndReplaceWithAverage, Not(IsNil)) c.Assert(evictAndReplaceWithAverage, Not(IsNil))
@ -86,7 +84,7 @@ func (s *S) TestAccumulatingBucketBuilderWithEvictAndReplaceWithAverage(c *C) {
func (s *S) TestAccumulatingBucket(c *C) { func (s *S) TestAccumulatingBucket(c *C) {
var b AccumulatingBucket = AccumulatingBucket{ var b AccumulatingBucket = AccumulatingBucket{
elements: make(utility.PriorityQueue, 0, 10), elements: make(priorityQueue, 0, 10),
maximumSize: 5, maximumSize: 5,
} }
@ -112,13 +110,13 @@ func (s *S) TestAccumulatingBucket(c *C) {
func (s *S) TestAccumulatingBucketValueForIndex(c *C) { func (s *S) TestAccumulatingBucketValueForIndex(c *C) {
var b AccumulatingBucket = AccumulatingBucket{ var b AccumulatingBucket = AccumulatingBucket{
elements: make(utility.PriorityQueue, 0, 100), elements: make(priorityQueue, 0, 100),
maximumSize: 100, maximumSize: 100,
evictionPolicy: EvictOldest(50), evictionPolicy: EvictOldest(50),
} }
for i := 0; i <= 100; i++ { for i := 0; i <= 100; i++ {
c.Assert(b.ValueForIndex(i), maths.IsNaN) c.Assert(b.ValueForIndex(i), 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.

View File

@ -1,12 +1,10 @@
// Copyright (c) 2012, Matt T. Proud // Copyright (c) 2013, Prometheus Team
// All rights reserved. // All rights reserved.
// //
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// bucket.go provides fundamental interface expectations for various bucket package prometheus
// types.
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

View File

@ -1,10 +1,10 @@
// Copyright (c) 2013, Matt T. Proud // Copyright (c) 2013, Prometheus Team
// All rights reserved. // All rights reserved.
// //
// Use of this source code is governed by a BSD-style license that can be found // Use of this source code is governed by a BSD-style license that can be found
// in the LICENSE file. // in the LICENSE file.
package registry package prometheus
var ( var (
// NilLabels is a nil set of labels merely for end-user convenience. // NilLabels is a nil set of labels merely for end-user convenience.
@ -14,8 +14,7 @@ var (
// interface. // interface.
DefaultHandler = DefaultRegistry.Handler() DefaultHandler = DefaultRegistry.Handler()
// This is the default registry with which Metric objects are associated. It // This is the default registry with which Metric objects are associated.
// is primarily a read-only object after server instantiation.
DefaultRegistry = NewRegistry() DefaultRegistry = NewRegistry()
) )
@ -39,4 +38,14 @@ const (
docstringKey = "docstring" docstringKey = "docstring"
metricKey = "metric" metricKey = "metric"
nameLabel = "name" nameLabel = "name"
counterTypeValue = "counter"
floatBitCount = 64
floatFormat = 'f'
floatPrecision = 6
gaugeTypeValue = "gauge"
histogramTypeValue = "histogram"
typeKey = "type"
valueKey = "value"
labelsKey = "labels"
) )

View File

@ -1,14 +1,13 @@
// Copyright (c) 2012, Matt T. Proud // Copyright (c) 2013, Prometheus Team
// All rights reserved. // All rights reserved.
// //
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package metrics package prometheus
import ( import (
"fmt" "fmt"
"github.com/prometheus/client_golang/utility"
"sync" "sync"
) )
@ -49,7 +48,7 @@ func (metric *counter) Set(labels map[string]string, value float64) float64 {
labels = map[string]string{} labels = map[string]string{}
} }
signature := utility.LabelsToSignature(labels) signature := labelsToSignature(labels)
if original, ok := metric.values[signature]; ok { if original, ok := metric.values[signature]; ok {
original.value = value original.value = value
} else { } else {
@ -91,7 +90,7 @@ func (metric *counter) IncrementBy(labels map[string]string, value float64) floa
labels = map[string]string{} labels = map[string]string{}
} }
signature := utility.LabelsToSignature(labels) signature := labelsToSignature(labels)
if original, ok := metric.values[signature]; ok { if original, ok := metric.values[signature]; ok {
original.value += value original.value += value
} else { } else {
@ -116,7 +115,7 @@ func (metric *counter) DecrementBy(labels map[string]string, value float64) floa
labels = map[string]string{} labels = map[string]string{}
} }
signature := utility.LabelsToSignature(labels) signature := labelsToSignature(labels)
if original, ok := metric.values[signature]; ok { if original, ok := metric.values[signature]; ok {
original.value -= value original.value -= value
} else { } else {

View File

@ -1,18 +1,17 @@
// Copyright (c) 2012, Matt T. Proud // Copyright (c) 2013, Prometheus Team
// All rights reserved. // All rights reserved.
// //
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package metrics package prometheus
import ( import (
"encoding/json" "encoding/json"
"github.com/prometheus/client_golang/utility/test"
"testing" "testing"
) )
func testCounter(t test.Tester) { func testCounter(t tester) {
type input struct { type input struct {
steps []func(g Counter) steps []func(g Counter)
} }

View File

@ -1,17 +1,17 @@
// Copyright (c) 2012, Matt T. Proud // Copyright (c) 2013, Prometheus Team
// All rights reserved. // All rights reserved.
// //
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package maths package prometheus
import ( 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,8 +28,8 @@ func Factorial(of int) int64 {
// Calculate the value of a probability density for a given binomial statistic, // 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 subjects, and // where k is the target count of true cases, n is the number of subjects, and
// p is the probability. // 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))
return binomialCoefficient * intermediate return binomialCoefficient * intermediate

View File

@ -0,0 +1,16 @@
// Copyright (c) 2013, Prometheus Team
// All rights reserved.
//
// Use of this source code is governed by a BSD-style license that can be found
// in the LICENSE file.
// Prometheus' client side metric primitives and telemetry exposition framework.
//
// This package provides both metric primitives and tools for their exposition
// to the Prometheus time series collection and computation framework.
//
// prometheus.Register("human_readable_metric_name", "metric docstring", map[string]string{"baseLabel": "baseLabelValue"}, metric)
//
// The examples under github.com/prometheus/client_golang/examples should be
// consulted.
package prometheus

View File

@ -1,15 +1,13 @@
// Copyright (c) 2012, Matt T. Proud // Copyright (c) 2013, Prometheus Team
// All rights reserved. // All rights reserved.
// //
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package metrics package prometheus
import ( import (
"container/heap" "container/heap"
"github.com/prometheus/client_golang/maths"
"github.com/prometheus/client_golang/utility"
"time" "time"
) )
@ -29,17 +27,17 @@ func EvictOldest(count int) EvictionPolicy {
// This factory produces an EvictionPolicy that applies some standardized // This factory produces an EvictionPolicy that applies some standardized
// reduction methodology on the to-be-terminated values. // reduction methodology on the to-be-terminated values.
func EvictAndReplaceWith(count int, reducer maths.ReductionMethod) EvictionPolicy { func EvictAndReplaceWith(count int, reducer ReductionMethod) EvictionPolicy {
return func(h heap.Interface) { return func(h heap.Interface) {
oldValues := make([]float64, count) oldValues := make([]float64, count)
for i := 0; i < count; i++ { for i := 0; i < count; i++ {
oldValues[i] = heap.Pop(h).(*utility.Item).Value.(float64) oldValues[i] = heap.Pop(h).(*item).Value.(float64)
} }
reduced := reducer(oldValues) reduced := reducer(oldValues)
heap.Push(h, &utility.Item{ heap.Push(h, &item{
Value: reduced, Value: reduced,
// TODO(mtp): Parameterize the priority generation since these tools are // TODO(mtp): Parameterize the priority generation since these tools are
// useful. // useful.

177
prometheus/eviction_test.go Normal file
View File

@ -0,0 +1,177 @@
// Copyright (c) 2013, Prometheus Team
// All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package prometheus
import (
"container/heap"
. "github.com/matttproud/gocheck"
)
func (s *S) TestEvictOldest(c *C) {
q := make(priorityQueue, 0, 10)
heap.Init(&q)
var e EvictionPolicy = EvictOldest(5)
for i := 0; i < 10; i++ {
var item item = item{
Priority: int64(i),
Value: float64(i),
}
heap.Push(&q, &item)
}
c.Check(q, HasLen, 10)
e(&q)
c.Check(q, HasLen, 5)
c.Check(heap.Pop(&q), ValueEquals, 4.0)
c.Check(heap.Pop(&q), ValueEquals, 3.0)
c.Check(heap.Pop(&q), ValueEquals, 2.0)
c.Check(heap.Pop(&q), ValueEquals, 1.0)
c.Check(heap.Pop(&q), ValueEquals, 0.0)
}
func (s *S) TestEvictAndReplaceWithAverage(c *C) {
q := make(priorityQueue, 0, 10)
heap.Init(&q)
var e EvictionPolicy = EvictAndReplaceWith(5, AverageReducer)
for i := 0; i < 10; i++ {
var item item = item{
Priority: int64(i),
Value: float64(i),
}
heap.Push(&q, &item)
}
c.Check(q, HasLen, 10)
e(&q)
c.Check(q, HasLen, 6)
c.Check(heap.Pop(&q), ValueEquals, 4.0)
c.Check(heap.Pop(&q), ValueEquals, 3.0)
c.Check(heap.Pop(&q), ValueEquals, 2.0)
c.Check(heap.Pop(&q), ValueEquals, 1.0)
c.Check(heap.Pop(&q), ValueEquals, 0.0)
c.Check(heap.Pop(&q), ValueEquals, 7.0)
}
func (s *S) TestEvictAndReplaceWithMedian(c *C) {
q := make(priorityQueue, 0, 10)
heap.Init(&q)
var e EvictionPolicy = EvictAndReplaceWith(5, MedianReducer)
for i := 0; i < 10; i++ {
var item item = item{
Priority: int64(i),
Value: float64(i),
}
heap.Push(&q, &item)
}
c.Check(q, HasLen, 10)
e(&q)
c.Check(q, HasLen, 6)
c.Check(heap.Pop(&q), ValueEquals, 4.0)
c.Check(heap.Pop(&q), ValueEquals, 3.0)
c.Check(heap.Pop(&q), ValueEquals, 2.0)
c.Check(heap.Pop(&q), ValueEquals, 1.0)
c.Check(heap.Pop(&q), ValueEquals, 0.0)
c.Check(heap.Pop(&q), ValueEquals, 7.0)
}
func (s *S) TestEvictAndReplaceWithFirstMode(c *C) {
q := make(priorityQueue, 0, 10)
heap.Init(&q)
e := EvictAndReplaceWith(5, FirstModeReducer)
for i := 0; i < 10; i++ {
heap.Push(&q, &item{
Priority: int64(i),
Value: float64(i),
})
}
c.Check(q, HasLen, 10)
e(&q)
c.Check(q, HasLen, 6)
c.Check(heap.Pop(&q), ValueEquals, 4.0)
c.Check(heap.Pop(&q), ValueEquals, 3.0)
c.Check(heap.Pop(&q), ValueEquals, 2.0)
c.Check(heap.Pop(&q), ValueEquals, 1.0)
c.Check(heap.Pop(&q), ValueEquals, 0.0)
c.Check(heap.Pop(&q), ValueEquals, 9.0)
}
func (s *S) TestEvictAndReplaceWithMinimum(c *C) {
q := make(priorityQueue, 0, 10)
heap.Init(&q)
var e EvictionPolicy = EvictAndReplaceWith(5, MinimumReducer)
for i := 0; i < 10; i++ {
var item item = item{
Priority: int64(i),
Value: float64(i),
}
heap.Push(&q, &item)
}
c.Check(q, HasLen, 10)
e(&q)
c.Check(q, HasLen, 6)
c.Check(heap.Pop(&q), ValueEquals, 4.0)
c.Check(heap.Pop(&q), ValueEquals, 3.0)
c.Check(heap.Pop(&q), ValueEquals, 2.0)
c.Check(heap.Pop(&q), ValueEquals, 1.0)
c.Check(heap.Pop(&q), ValueEquals, 0.0)
c.Check(heap.Pop(&q), ValueEquals, 5.0)
}
func (s *S) TestEvictAndReplaceWithMaximum(c *C) {
q := make(priorityQueue, 0, 10)
heap.Init(&q)
var e EvictionPolicy = EvictAndReplaceWith(5, MaximumReducer)
for i := 0; i < 10; i++ {
var item item = item{
Priority: int64(i),
Value: float64(i),
}
heap.Push(&q, &item)
}
c.Check(q, HasLen, 10)
e(&q)
c.Check(q, HasLen, 6)
c.Check(heap.Pop(&q), ValueEquals, 4.0)
c.Check(heap.Pop(&q), ValueEquals, 3.0)
c.Check(heap.Pop(&q), ValueEquals, 2.0)
c.Check(heap.Pop(&q), ValueEquals, 1.0)
c.Check(heap.Pop(&q), ValueEquals, 0.0)
c.Check(heap.Pop(&q), ValueEquals, 9.0)
}

View File

@ -1,4 +1,4 @@
// Copyright (c) 2013, Matt T. Proud // Copyright (c) 2013, Prometheus Team
// All rights reserved. // All rights reserved.
// //
// Use of this source code is governed by a BSD-style license that can be found in // Use of this source code is governed by a BSD-style license that can be found in
@ -8,8 +8,7 @@ package exp
import ( import (
"fmt" "fmt"
"github.com/prometheus/client_golang" "github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/metrics"
"net/http" "net/http"
"strings" "strings"
"time" "time"
@ -33,11 +32,11 @@ type (
) )
var ( var (
requestCounts = metrics.NewCounter() requestCounts = prometheus.NewCounter()
requestDuration = metrics.NewCounter() requestDuration = prometheus.NewCounter()
requestDurations = metrics.NewDefaultHistogram() requestDurations = prometheus.NewDefaultHistogram()
requestBytes = metrics.NewCounter() requestBytes = prometheus.NewCounter()
responseBytes = metrics.NewCounter() responseBytes = prometheus.NewCounter()
// DefaultCoarseMux is a drop-in replacement for http.DefaultServeMux that // DefaultCoarseMux is a drop-in replacement for http.DefaultServeMux that
// provides standardized telemetry for Go's standard HTTP handler registration // provides standardized telemetry for Go's standard HTTP handler registration
@ -103,9 +102,9 @@ func HandleFunc(pattern string, handler http.HandlerFunc) {
} }
func init() { func init() {
registry.Register("http_requests_total", "A counter of the total number of HTTP requests made against the default multiplexor.", registry.NilLabels, requestCounts) prometheus.Register("http_requests_total", "A counter of the total number of HTTP requests made against the default multiplexor.", prometheus.NilLabels, requestCounts)
registry.Register("http_request_durations_total_microseconds", "The total amount of time the default multiplexor has spent answering HTTP requests (microseconds).", registry.NilLabels, requestDuration) prometheus.Register("http_request_durations_total_microseconds", "The total amount of time the default multiplexor has spent answering HTTP requests (microseconds).", prometheus.NilLabels, requestDuration)
registry.Register("http_request_durations_microseconds", "The amounts of time the default multiplexor has spent answering HTTP requests (microseconds).", registry.NilLabels, requestDurations) prometheus.Register("http_request_durations_microseconds", "The amounts of time the default multiplexor has spent answering HTTP requests (microseconds).", prometheus.NilLabels, requestDurations)
registry.Register("http_request_bytes_total", "The total volume of content body sizes received (bytes).", registry.NilLabels, requestBytes) prometheus.Register("http_request_bytes_total", "The total volume of content body sizes received (bytes).", prometheus.NilLabels, requestBytes)
registry.Register("http_response_bytes_total", "The total volume of response payloads emitted (bytes).", registry.NilLabels, responseBytes) prometheus.Register("http_response_bytes_total", "The total volume of response payloads emitted (bytes).", prometheus.NilLabels, responseBytes)
} }

View File

@ -1,4 +1,4 @@
// Copyright (c) 2013, Matt T. Proud // Copyright (c) 2013, Prometheus Team
// All rights reserved. // All rights reserved.
// //
// Use of this source code is governed by a BSD-style license that can be found in // Use of this source code is governed by a BSD-style license that can be found in

View File

@ -1,4 +1,4 @@
// Copyright (c) 2013, Matt T. Proud // Copyright (c) 2013, Prometheus Team
// All rights reserved. // All rights reserved.
// //
// Use of this source code is governed by a BSD-style license that can be found in // Use of this source code is governed by a BSD-style license that can be found in

View File

@ -1,14 +1,13 @@
// Copyright (c) 2012, Matt T. Proud // Copyright (c) 2013, Prometheus Team
// All rights reserved. // All rights reserved.
// //
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package metrics package prometheus
import ( import (
"fmt" "fmt"
"github.com/prometheus/client_golang/utility"
"sync" "sync"
) )
@ -56,7 +55,7 @@ func (metric *gauge) Set(labels map[string]string, value float64) float64 {
labels = map[string]string{} labels = map[string]string{}
} }
signature := utility.LabelsToSignature(labels) signature := labelsToSignature(labels)
if original, ok := metric.values[signature]; ok { if original, ok := metric.values[signature]; ok {
original.value = value original.value = value

View File

@ -1,18 +1,17 @@
// Copyright (c) 2012, Matt T. Proud // Copyright (c) 2013, Prometheus Team
// All rights reserved. // All rights reserved.
// //
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package metrics package prometheus
import ( import (
"encoding/json" "encoding/json"
"github.com/prometheus/client_golang/utility/test"
"testing" "testing"
) )
func testGauge(t test.Tester) { func testGauge(t tester) {
type input struct { type input struct {
steps []func(g Gauge) steps []func(g Gauge)
} }

View File

@ -1,10 +1,10 @@
// Copyright (c) 2012, Matt T. Proud // Copyright (c) 2013, Prometheus Team
// All rights reserved. // All rights reserved.
// //
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package maths package prometheus
import ( import (
. "github.com/matttproud/gocheck" . "github.com/matttproud/gocheck"

View File

@ -1,16 +1,14 @@
// Copyright (c) 2012, Matt T. Proud // Copyright (c) 2013, Prometheus Team
// All rights reserved. // All rights reserved.
// //
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package metrics package prometheus
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"github.com/prometheus/client_golang/maths"
"github.com/prometheus/client_golang/utility"
"math" "math"
"strconv" "strconv"
"sync" "sync"
@ -96,7 +94,7 @@ func (h *histogram) Add(labels map[string]string, value float64) {
labels = map[string]string{} labels = map[string]string{}
} }
signature := utility.LabelsToSignature(labels) signature := labelsToSignature(labels)
var histogram *histogramVector = nil var histogram *histogramVector = nil
if original, ok := h.values[signature]; ok { if original, ok := h.values[signature]; ok {
histogram = original histogram = original
@ -298,7 +296,7 @@ func NewDefaultHistogram() Histogram {
return NewHistogram( return NewHistogram(
&HistogramSpecification{ &HistogramSpecification{
Starts: LogarithmicSizedBucketsFor(0, 4096), Starts: LogarithmicSizedBucketsFor(0, 4096),
BucketBuilder: AccumulatingBucketBuilder(EvictAndReplaceWith(10, maths.Average), 50), BucketBuilder: AccumulatingBucketBuilder(EvictAndReplaceWith(10, AverageReducer), 50),
ReportablePercentiles: []float64{0.01, 0.05, 0.5, 0.90, 0.99}, ReportablePercentiles: []float64{0.01, 0.05, 0.5, 0.90, 0.99},
}, },
) )

View File

@ -1,9 +1,9 @@
// Copyright (c) 2013, Matt T. Proud // Copyright (c) 2013, Prometheus Team
// All rights reserved. // All rights reserved.
// //
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package metrics package prometheus
// TODO(matt): Re-Add tests for this type. // TODO(matt): Re-Add tests for this type.

View File

@ -1,12 +1,12 @@
// Copyright (c) 2013, Matt T. Proud // Copyright (c) 2013, Prometheus Team
// All rights reserved. // All rights reserved.
// //
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package test package prometheus
type Tester interface { type tester interface {
Error(args ...interface{}) Error(args ...interface{})
Errorf(format string, args ...interface{}) Errorf(format string, args ...interface{})
Fatal(args ...interface{}) Fatal(args ...interface{})

View File

@ -1,10 +1,10 @@
// Copyright (c) 2012, Matt T. Proud // Copyright (c) 2013, Prometheus Team
// All rights reserved. // All rights reserved.
// //
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package metrics package prometheus
// A Metric is something that can be exposed via the registry framework. // A Metric is something that can be exposed via the registry framework.
type Metric interface { type Metric interface {

View File

@ -1,44 +1,44 @@
// Copyright (c) 2012, Matt T. Proud // Copyright (c) 2013, Prometheus Team
// All rights reserved. // All rights reserved.
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package utility package prometheus
type Item struct { type item struct {
Priority int64 Priority int64
Value interface{} Value interface{}
index int index int
} }
type PriorityQueue []*Item type priorityQueue []*item
func (q PriorityQueue) Len() int { func (q priorityQueue) Len() int {
return len(q) return len(q)
} }
func (q PriorityQueue) Less(i, j int) bool { func (q priorityQueue) Less(i, j int) bool {
return q[i].Priority > q[j].Priority return q[i].Priority > q[j].Priority
} }
func (q PriorityQueue) Swap(i, j int) { func (q priorityQueue) Swap(i, j int) {
q[i], q[j] = q[j], q[i] q[i], q[j] = q[j], q[i]
q[i].index = i q[i].index = i
q[j].index = j q[j].index = j
} }
func (q *PriorityQueue) Push(x interface{}) { func (q *priorityQueue) Push(x interface{}) {
queue := *q queue := *q
size := len(queue) size := len(queue)
queue = queue[0 : size+1] queue = queue[0 : size+1]
item := x.(*Item) item := x.(*item)
item.index = size item.index = size
queue[size] = item queue[size] = item
*q = queue *q = queue
} }
func (q *PriorityQueue) Pop() interface{} { func (q *priorityQueue) Pop() interface{} {
queue := *q queue := *q
size := len(queue) size := len(queue)
item := queue[size-1] item := queue[size-1]

View File

@ -1,10 +1,10 @@
// Copyright (c) 2012, Matt T. Proud // Copyright (c) 2013, Prometheus Team
// All rights reserved. // All rights reserved.
// //
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package utility package prometheus
import ( import (
"container/heap" "container/heap"
@ -12,16 +12,16 @@ import (
) )
func (s *S) TestPriorityQueueSort(c *C) { func (s *S) TestPriorityQueueSort(c *C) {
q := make(PriorityQueue, 0, 6) q := make(priorityQueue, 0, 6)
c.Check(len(q), Equals, 0) c.Check(len(q), Equals, 0)
heap.Push(&q, &Item{Value: "newest", Priority: -100}) heap.Push(&q, &item{Value: "newest", Priority: -100})
heap.Push(&q, &Item{Value: "older", Priority: 90}) heap.Push(&q, &item{Value: "older", Priority: 90})
heap.Push(&q, &Item{Value: "oldest", Priority: 100}) heap.Push(&q, &item{Value: "oldest", Priority: 100})
heap.Push(&q, &Item{Value: "newer", Priority: -90}) heap.Push(&q, &item{Value: "newer", Priority: -90})
heap.Push(&q, &Item{Value: "new", Priority: -80}) heap.Push(&q, &item{Value: "new", Priority: -80})
heap.Push(&q, &Item{Value: "old", Priority: 80}) heap.Push(&q, &item{Value: "old", Priority: 80})
c.Check(len(q), Equals, 6) c.Check(len(q), Equals, 6)

View File

@ -1,10 +1,10 @@
// Copyright (c) 2012, Matt T. Proud // Copyright (c) 2013, Prometheus Team
// All rights reserved. // All rights reserved.
// //
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package metrics package prometheus
import ( import (
. "github.com/matttproud/gocheck" . "github.com/matttproud/gocheck"
@ -15,6 +15,6 @@ type S struct{}
var _ = Suite(&S{}) var _ = Suite(&S{})
func TestMetrics(t *testing.T) { func TestPrometheus(t *testing.T) {
TestingT(t) TestingT(t)
} }

View File

@ -1,10 +1,10 @@
// Copyright (c) 2012, Matt T. Proud // Copyright (c) 2013, Prometheus Team
// All rights reserved. // All rights reserved.
// //
// Use of this source code is governed by a BSD-style license that can be found // Use of this source code is governed by a BSD-style license that can be found
// in the LICENSE file. // in the LICENSE file.
package registry package prometheus
import ( import (
"compress/gzip" "compress/gzip"
@ -12,8 +12,6 @@ import (
"encoding/json" "encoding/json"
"flag" "flag"
"fmt" "fmt"
"github.com/prometheus/client_golang/metrics"
"github.com/prometheus/client_golang/utility"
"io" "io"
"log" "log"
"net/http" "net/http"
@ -46,7 +44,7 @@ var (
type container struct { type container struct {
baseLabels map[string]string baseLabels map[string]string
docstring string docstring string
metric metrics.Metric metric Metric
name string name string
} }
@ -61,7 +59,7 @@ type registry struct {
// own. // own.
type Registry interface { type Registry interface {
// Register a metric with a given name. Name should be globally unique. // Register a metric with a given name. Name should be globally unique.
Register(name, docstring string, baseLabels map[string]string, metric metrics.Metric) error Register(name, docstring string, baseLabels map[string]string, metric Metric) error
// Create a http.HandlerFunc that is tied to a Registry such that requests // Create a http.HandlerFunc that is tied to a Registry such that requests
// against it generate a representation of the housed metrics. // against it generate a representation of the housed metrics.
Handler() http.HandlerFunc Handler() http.HandlerFunc
@ -79,7 +77,7 @@ func NewRegistry() Registry {
} }
// Associate a Metric with the DefaultRegistry. // Associate a Metric with the DefaultRegistry.
func Register(name, docstring string, baseLabels map[string]string, metric metrics.Metric) error { func Register(name, docstring string, baseLabels map[string]string, metric Metric) error {
return DefaultRegistry.Register(name, docstring, baseLabels, metric) return DefaultRegistry.Register(name, docstring, baseLabels, metric)
} }
@ -110,7 +108,7 @@ func (r registry) isValidCandidate(name string, baseLabels map[string]string) (s
} }
baseLabels[nameLabel] = name baseLabels[nameLabel] = name
signature = utility.LabelsToSignature(baseLabels) signature = labelsToSignature(baseLabels)
if _, contains := r.signatureContainers[signature]; contains { if _, contains := r.signatureContainers[signature]; contains {
err = fmt.Errorf("metric named %s with baseLabels %s is already registered", name, baseLabels) err = fmt.Errorf("metric named %s with baseLabels %s is already registered", name, baseLabels)
@ -141,7 +139,7 @@ func (r registry) isValidCandidate(name string, baseLabels map[string]string) (s
return return
} }
func (r registry) Register(name, docstring string, baseLabels map[string]string, metric metrics.Metric) (err error) { func (r registry) Register(name, docstring string, baseLabels map[string]string, metric Metric) (err error) {
r.mutex.Lock() r.mutex.Lock()
defer r.mutex.Unlock() defer r.mutex.Unlock()
@ -271,7 +269,7 @@ func (registry registry) YieldExporter() http.HandlerFunc {
func (registry registry) Handler() http.HandlerFunc { func (registry registry) Handler() 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 InstrumentableCall = func() {
requestCount.Increment(nil) requestCount.Increment(nil)
url := r.URL url := r.URL
@ -293,7 +291,7 @@ func (registry registry) Handler() http.HandlerFunc {
} }
} }
metrics.InstrumentCall(instrumentable, requestLatencyAccumulator) InstrumentCall(instrumentable, requestLatencyAccumulator)
} }
} }

View File

@ -1,22 +1,20 @@
// Copyright (c) 2013, Matt T. Proud // Copyright (c) 2013, Prometheus Team
// All rights reserved. // All rights reserved.
// //
// Use of this source code is governed by a BSD-style license that can be found // Use of this source code is governed by a BSD-style license that can be found
// in the LICENSE file. // in the LICENSE file.
package registry package prometheus
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"github.com/prometheus/client_golang/metrics"
"github.com/prometheus/client_golang/utility/test"
"io" "io"
"net/http" "net/http"
"testing" "testing"
) )
func testRegister(t test.Tester) { func testRegister(t tester) {
var oldState = struct { var oldState = struct {
abortOnMisuse bool abortOnMisuse bool
debugRegistration bool debugRegistration bool
@ -181,7 +179,7 @@ func (r *fakeResponseWriter) Write(d []byte) (l int, err error) {
func (r *fakeResponseWriter) WriteHeader(c int) { func (r *fakeResponseWriter) WriteHeader(c int) {
} }
func testDecorateWriter(t test.Tester) { func testDecorateWriter(t tester) {
type input struct { type input struct {
headers map[string]string headers map[string]string
body []byte body []byte
@ -266,9 +264,9 @@ func BenchmarkDecorateWriter(b *testing.B) {
} }
} }
func testDumpToWriter(t test.Tester) { func testDumpToWriter(t tester) {
type input struct { type input struct {
metrics map[string]metrics.Metric metrics map[string]Metric
} }
var scenarios = []struct { var scenarios = []struct {
@ -280,17 +278,17 @@ func testDumpToWriter(t test.Tester) {
}, },
{ {
in: input{ in: input{
metrics: map[string]metrics.Metric{ metrics: map[string]Metric{
"foo": metrics.NewCounter(), "foo": NewCounter(),
}, },
}, },
out: []byte("[{\"baseLabels\":{\"label_foo\":\"foo\",\"name\":\"foo\"},\"docstring\":\"metric foo\",\"metric\":{\"type\":\"counter\",\"value\":[]}}]"), out: []byte("[{\"baseLabels\":{\"label_foo\":\"foo\",\"name\":\"foo\"},\"docstring\":\"metric foo\",\"metric\":{\"type\":\"counter\",\"value\":[]}}]"),
}, },
{ {
in: input{ in: input{
metrics: map[string]metrics.Metric{ metrics: map[string]Metric{
"foo": metrics.NewCounter(), "foo": NewCounter(),
"bar": metrics.NewCounter(), "bar": NewCounter(),
}, },
}, },
out: []byte("[{\"baseLabels\":{\"label_bar\":\"bar\",\"name\":\"bar\"},\"docstring\":\"metric bar\",\"metric\":{\"type\":\"counter\",\"value\":[]}},{\"baseLabels\":{\"label_foo\":\"foo\",\"name\":\"foo\"},\"docstring\":\"metric foo\",\"metric\":{\"type\":\"counter\",\"value\":[]}}]"), out: []byte("[{\"baseLabels\":{\"label_bar\":\"bar\",\"name\":\"bar\"},\"docstring\":\"metric bar\",\"metric\":{\"type\":\"counter\",\"value\":[]}},{\"baseLabels\":{\"label_foo\":\"foo\",\"name\":\"foo\"},\"docstring\":\"metric foo\",\"metric\":{\"type\":\"counter\",\"value\":[]}}]"),

View File

@ -1,10 +1,10 @@
// Copyright (c) 2013, Matt T. Proud // Copyright (c) 2013, Prometheus Team
// All rights reserved. // All rights reserved.
// //
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package utility package prometheus
import ( import (
"bytes" "bytes"
@ -17,7 +17,7 @@ const (
// LabelsToSignature provides a way of building a unique signature // LabelsToSignature provides a way of building a unique signature
// (i.e., fingerprint) for a given label set sequence. // (i.e., fingerprint) for a given label set sequence.
func LabelsToSignature(labels map[string]string) string { func labelsToSignature(labels map[string]string) string {
// TODO(matt): This is a wart, and we'll want to validate that collisions // TODO(matt): This is a wart, and we'll want to validate that collisions
// do not occur in less-than-diligent environments. // do not occur in less-than-diligent environments.
cardinality := len(labels) cardinality := len(labels)

View File

@ -1,17 +1,16 @@
// Copyright (c) 2013, Matt T. Proud // Copyright (c) 2013, Prometheus Team
// All rights reserved. // All rights reserved.
// //
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package utility package prometheus
import ( import (
"github.com/prometheus/client_golang/utility/test"
"testing" "testing"
) )
func testLabelsToSignature(t test.Tester) { func testLabelsToSignature(t tester) {
var scenarios = []struct { var scenarios = []struct {
in map[string]string in map[string]string
out string out string
@ -24,7 +23,7 @@ func testLabelsToSignature(t test.Tester) {
} }
for i, scenario := range scenarios { for i, scenario := range scenarios {
actual := LabelsToSignature(scenario.in) actual := labelsToSignature(scenario.in)
if actual != scenario.out { if actual != scenario.out {
t.Errorf("%d. expected %s, got %s", i, scenario.out, actual) t.Errorf("%d. expected %s, got %s", i, scenario.out, actual)

View File

@ -1,10 +1,10 @@
// Copyright (c) 2012, Matt T. Proud // Copyright (c) 2013, Prometheus Team
// All rights reserved. // All rights reserved.
// //
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package maths package prometheus
import ( import (
"math" "math"
@ -14,11 +14,32 @@ import (
// TODO(mtp): Split this out into a summary statistics file once moving/rolling // TODO(mtp): Split this out into a summary statistics file once moving/rolling
// averages are calculated. // averages are calculated.
// ReductionMethod provides a method for reducing metrics into a given scalar // ReductionMethod provides a method for reducing metrics into a scalar value.
// value.
type ReductionMethod func([]float64) float64 type ReductionMethod func([]float64) float64
var Average ReductionMethod = func(input []float64) float64 { var (
medianReducer = NearestRankReducer(50)
)
// These are the canned ReductionMethods.
var (
// Reduce to the average of the set.
AverageReducer = averageReducer
// Extract the first modal value.
FirstModeReducer = firstModeReducer
// Reduce to the maximum of the set.
MaximumReducer = maximumReducer
// Reduce to the median of the set.
MedianReducer = medianReducer
// Reduce to the minimum of the set.
MinimumReducer = minimumReducer
)
func averageReducer(input []float64) float64 {
count := 0.0 count := 0.0
sum := 0.0 sum := 0.0
@ -34,11 +55,10 @@ var Average ReductionMethod = func(input []float64) float64 {
return sum / count return sum / count
} }
// Extract the first modal value. func firstModeReducer(input []float64) float64 {
var FirstMode ReductionMethod = func(input []float64) float64 {
valuesToFrequency := map[float64]int64{} valuesToFrequency := map[float64]int64{}
var largestTally int64 = math.MinInt64 largestTally := int64(math.MinInt64)
var largestTallyValue float64 = math.NaN() largestTallyValue := math.NaN()
for _, v := range input { for _, v := range input {
presentCount, _ := valuesToFrequency[v] presentCount, _ := valuesToFrequency[v]
@ -56,7 +76,7 @@ var FirstMode ReductionMethod = func(input []float64) float64 {
} }
// 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)
if inputSize == 0 { if inputSize == 0 {
@ -81,14 +101,12 @@ func NearestRank(input []float64, percentile float64) float64 {
// Generate a ReductionMethod based off of extracting a given percentile value. // Generate a ReductionMethod based off of extracting a given percentile value.
func NearestRankReducer(percentile float64) ReductionMethod { func NearestRankReducer(percentile float64) ReductionMethod {
return func(input []float64) float64 { return func(input []float64) float64 {
return NearestRank(input, percentile) return nearestRank(input, percentile)
} }
} }
var Median ReductionMethod = NearestRankReducer(50) func minimumReducer(input []float64) float64 {
minimum := math.MaxFloat64
var Minimum ReductionMethod = func(input []float64) float64 {
var minimum float64 = math.MaxFloat64
for _, v := range input { for _, v := range input {
minimum = math.Min(minimum, v) minimum = math.Min(minimum, v)
@ -97,8 +115,8 @@ var Minimum ReductionMethod = func(input []float64) float64 {
return minimum return minimum
} }
var Maximum ReductionMethod = func(input []float64) float64 { func maximumReducer(input []float64) float64 {
var maximum float64 = math.SmallestNonzeroFloat64 maximum := math.SmallestNonzeroFloat64
for _, v := range input { for _, v := range input {
maximum = math.Max(maximum, v) maximum = math.Max(maximum, v)

View File

@ -1,10 +1,10 @@
// Copyright (c) 2012, Matt T. Proud // Copyright (c) 2013, Prometheus Team
// All rights reserved. // All rights reserved.
// //
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package maths package prometheus
import ( import (
. "github.com/matttproud/gocheck" . "github.com/matttproud/gocheck"
@ -12,42 +12,42 @@ import (
func (s *S) TestAverageOnEmpty(c *C) { func (s *S) TestAverageOnEmpty(c *C) {
empty := []float64{} empty := []float64{}
var v float64 = Average(empty) var v float64 = AverageReducer(empty)
c.Assert(v, IsNaN) c.Assert(v, IsNaN)
} }
func (s *S) TestAverageForSingleton(c *C) { func (s *S) TestAverageForSingleton(c *C) {
input := []float64{5} input := []float64{5}
var v float64 = Average(input) var v float64 = AverageReducer(input)
c.Check(v, Equals, 5.0) c.Check(v, Equals, 5.0)
} }
func (s *S) TestAverage(c *C) { func (s *S) TestAverage(c *C) {
input := []float64{5, 15} input := []float64{5, 15}
var v float64 = Average(input) var v float64 = AverageReducer(input)
c.Check(v, Equals, 10.0) c.Check(v, Equals, 10.0)
} }
func (s *S) TestFirstModeOnEmpty(c *C) { func (s *S) TestFirstModeOnEmpty(c *C) {
input := []float64{} input := []float64{}
var v float64 = FirstMode(input) var v float64 = FirstModeReducer(input)
c.Assert(v, IsNaN) c.Assert(v, IsNaN)
} }
func (s *S) TestFirstModeForSingleton(c *C) { func (s *S) TestFirstModeForSingleton(c *C) {
input := []float64{5} input := []float64{5}
var v float64 = FirstMode(input) var v float64 = FirstModeReducer(input)
c.Check(v, Equals, 5.0) c.Check(v, Equals, 5.0)
} }
func (s *S) TestFirstModeForUnimodal(c *C) { func (s *S) TestFirstModeForUnimodal(c *C) {
input := []float64{1, 2, 3, 4, 3} input := []float64{1, 2, 3, 4, 3}
var v float64 = FirstMode(input) var v float64 = FirstModeReducer(input)
c.Check(v, Equals, 3.0) c.Check(v, Equals, 3.0)
} }
@ -55,25 +55,25 @@ func (s *S) TestFirstModeForUnimodal(c *C) {
func (s *S) TestNearestRankForEmpty(c *C) { func (s *S) TestNearestRankForEmpty(c *C) {
input := []float64{} input := []float64{}
c.Assert(NearestRank(input, 0), IsNaN) c.Assert(nearestRank(input, 0), IsNaN)
c.Assert(NearestRank(input, 50), IsNaN) c.Assert(nearestRank(input, 50), IsNaN)
c.Assert(NearestRank(input, 100), IsNaN) c.Assert(nearestRank(input, 100), IsNaN)
} }
func (s *S) TestNearestRankForSingleton(c *C) { func (s *S) TestNearestRankForSingleton(c *C) {
input := []float64{5} input := []float64{5}
c.Check(NearestRank(input, 0), Equals, 5.0) c.Check(nearestRank(input, 0), Equals, 5.0)
c.Check(NearestRank(input, 50), Equals, 5.0) c.Check(nearestRank(input, 50), Equals, 5.0)
c.Check(NearestRank(input, 100), Equals, 5.0) c.Check(nearestRank(input, 100), Equals, 5.0)
} }
func (s *S) TestNearestRankForDouble(c *C) { func (s *S) TestNearestRankForDouble(c *C) {
input := []float64{5, 5} input := []float64{5, 5}
c.Check(NearestRank(input, 0), Equals, 5.0) c.Check(nearestRank(input, 0), Equals, 5.0)
c.Check(NearestRank(input, 50), Equals, 5.0) c.Check(nearestRank(input, 50), Equals, 5.0)
c.Check(NearestRank(input, 100), Equals, 5.0) c.Check(nearestRank(input, 100), Equals, 5.0)
} }
func (s *S) TestNearestRankFor100(c *C) { func (s *S) TestNearestRankFor100(c *C) {
@ -83,9 +83,9 @@ func (s *S) TestNearestRankFor100(c *C) {
input[i] = float64(i + 1) input[i] = float64(i + 1)
} }
c.Check(NearestRank(input, 0), Equals, 1.0) c.Check(nearestRank(input, 0), Equals, 1.0)
c.Check(NearestRank(input, 50), Equals, 51.0) c.Check(nearestRank(input, 50), Equals, 51.0)
c.Check(NearestRank(input, 100), Equals, 100.0) c.Check(nearestRank(input, 100), Equals, 100.0)
} }
func (s *S) TestNearestRankFor101(c *C) { func (s *S) TestNearestRankFor101(c *C) {
@ -95,25 +95,25 @@ func (s *S) TestNearestRankFor101(c *C) {
input[i] = float64(i + 1) input[i] = float64(i + 1)
} }
c.Check(NearestRank(input, 0), Equals, 1.0) c.Check(nearestRank(input, 0), Equals, 1.0)
c.Check(NearestRank(input, 50), Equals, 51.0) c.Check(nearestRank(input, 50), Equals, 51.0)
c.Check(NearestRank(input, 100), Equals, 101.0) c.Check(nearestRank(input, 100), Equals, 101.0)
} }
func (s *S) TestMedianReducer(c *C) { func (s *S) TestMedianReducer(c *C) {
input := []float64{1, 2, 3} input := []float64{1, 2, 3}
c.Check(Median(input), Equals, 2.0) c.Check(MedianReducer(input), Equals, 2.0)
} }
func (s *S) TestMinimum(c *C) { func (s *S) TestMinimum(c *C) {
input := []float64{5, 1, 10, 1.1, 4} input := []float64{5, 1, 10, 1.1, 4}
c.Check(Minimum(input), Equals, 1.0) c.Check(MinimumReducer(input), Equals, 1.0)
} }
func (s *S) TestMaximum(c *C) { func (s *S) TestMaximum(c *C) {
input := []float64{5, 1, 10, 1.1, 4} input := []float64{5, 1, 10, 1.1, 4}
c.Check(Maximum(input), Equals, 10.0) c.Check(MaximumReducer(input), Equals, 10.0)
} }

View File

@ -1,14 +1,13 @@
// Copyright (c) 2012, Matt T. Proud // Copyright (c) 2013, Prometheus Team
// All rights reserved. // All rights reserved.
// //
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package metrics package prometheus
import ( import (
"fmt" "fmt"
"github.com/prometheus/client_golang/maths"
"math" "math"
"sync" "sync"
) )
@ -35,38 +34,49 @@ func emptyFilter(e TallyingIndexEstimator) TallyingIndexEstimator {
} }
} }
// Report the smallest observed value in the bucket. var (
var Minimum TallyingIndexEstimator = emptyFilter(func(minimum, maximum float64, _, observations int) float64 { minimumEstimator = emptyFilter(func(minimum, maximum float64, _, observations int) float64 {
return minimum
})
// Report the largest observed value in the bucket.
var Maximum TallyingIndexEstimator = emptyFilter(func(minimum, maximum float64, _, observations int) float64 {
return maximum
})
// Report the average of the extrema.
var Average TallyingIndexEstimator = emptyFilter(func(minimum, maximum float64, _, observations int) float64 {
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.
var Uniform TallyingIndexEstimator = emptyFilter(func(minimum, maximum float64, index, observations int) float64 {
if observations == 1 {
return minimum return minimum
} })
location := float64(index) / float64(observations) maximumEstimator = emptyFilter(func(minimum, maximum float64, _, observations int) float64 {
if location > upperThird {
return maximum return maximum
} else if location < lowerThird { })
return minimum
}
return maths.Average([]float64{minimum, maximum}) averageEstimator = emptyFilter(func(minimum, maximum float64, _, observations int) float64 {
}) return AverageReducer([]float64{minimum, maximum})
})
uniformEstimator = emptyFilter(func(minimum, maximum float64, index, observations int) float64 {
if observations == 1 {
return minimum
}
location := float64(index) / float64(observations)
if location > upperThird {
return maximum
} else if location < lowerThird {
return minimum
}
return AverageReducer([]float64{minimum, maximum})
})
)
// These are the canned TallyingIndexEstimators.
var (
// Report the smallest observed value in the bucket.
MinimumEstimator = minimumEstimator
// Report the largest observed value in the bucket.
MaximumEstimator = maximumEstimator
// Report the average of the extrema.
AverageEstimator = averageEstimator
// Report the minimum value of the index if it is in the lower-third of
// observations, the average if in the middle-third, and the maximum if in
// the largest third
UniformEstimator = uniformEstimator
)
// A TallyingBucket is a Bucket that tallies when an object is added to it. // 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 // Upon insertion, an object is compared against collected extrema and noted
@ -127,7 +137,7 @@ func (b *TallyingBucket) Reset() {
// Produce a TallyingBucket with sane defaults. // Produce a TallyingBucket with sane defaults.
func DefaultTallyingBucket() TallyingBucket { func DefaultTallyingBucket() TallyingBucket {
return TallyingBucket{ return TallyingBucket{
estimator: Minimum, estimator: MinimumEstimator,
largestObserved: math.SmallestNonzeroFloat64, largestObserved: math.SmallestNonzeroFloat64,
smallestObserved: math.MaxFloat64, smallestObserved: math.MaxFloat64,
} }

View File

@ -1,40 +1,39 @@
// Copyright (c) 2012, Matt T. Proud // Copyright (c) 2013, Prometheus Team
// All rights reserved. // All rights reserved.
// //
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package metrics package prometheus
import ( import (
. "github.com/matttproud/gocheck" . "github.com/matttproud/gocheck"
"github.com/prometheus/client_golang/maths"
) )
func (s *S) TestTallyingPercentileEstimatorMinimum(c *C) { func (s *S) TestTallyingPercentileEstimatorMinimum(c *C) {
c.Assert(Minimum(-2, -1, 0, 0), maths.IsNaN) c.Assert(MinimumEstimator(-2, -1, 0, 0), IsNaN)
c.Check(Minimum(-2, -1, 0, 1), Equals, -2.0) c.Check(MinimumEstimator(-2, -1, 0, 1), Equals, -2.0)
} }
func (s *S) TestTallyingPercentileEstimatorMaximum(c *C) { func (s *S) TestTallyingPercentileEstimatorMaximum(c *C) {
c.Assert(Maximum(-2, -1, 0, 0), maths.IsNaN) c.Assert(MaximumEstimator(-2, -1, 0, 0), IsNaN)
c.Check(Maximum(-2, -1, 0, 1), Equals, -1.0) c.Check(MaximumEstimator(-2, -1, 0, 1), Equals, -1.0)
} }
func (s *S) TestTallyingPercentilesEstimatorAverage(c *C) { func (s *S) TestTallyingPercentilesEstimatorAverage(c *C) {
c.Assert(Average(-2, -1, 0, 0), maths.IsNaN) c.Assert(AverageEstimator(-2, -1, 0, 0), IsNaN)
c.Check(Average(-2, -2, 0, 1), Equals, -2.0) c.Check(AverageEstimator(-2, -2, 0, 1), Equals, -2.0)
c.Check(Average(-1, -1, 0, 1), Equals, -1.0) c.Check(AverageEstimator(-1, -1, 0, 1), Equals, -1.0)
c.Check(Average(1, 1, 0, 2), Equals, 1.0) c.Check(AverageEstimator(1, 1, 0, 2), Equals, 1.0)
c.Check(Average(2, 1, 0, 2), Equals, 1.5) c.Check(AverageEstimator(2, 1, 0, 2), Equals, 1.5)
} }
func (s *S) TestTallyingPercentilesEstimatorUniform(c *C) { func (s *S) TestTallyingPercentilesEstimatorUniform(c *C) {
c.Assert(Uniform(-5, 5, 0, 0), maths.IsNaN) c.Assert(UniformEstimator(-5, 5, 0, 0), IsNaN)
c.Check(Uniform(-5, 5, 0, 2), Equals, -5.0) c.Check(UniformEstimator(-5, 5, 0, 2), Equals, -5.0)
c.Check(Uniform(-5, 5, 1, 2), Equals, 0.0) c.Check(UniformEstimator(-5, 5, 1, 2), Equals, 0.0)
c.Check(Uniform(-5, 5, 2, 2), Equals, 5.0) c.Check(UniformEstimator(-5, 5, 2, 2), Equals, 5.0)
} }
func (s *S) TestTallyingBucketBuilder(c *C) { func (s *S) TestTallyingBucketBuilder(c *C) {

View File

@ -1,15 +1,13 @@
// Copyright (c) 2013, Matt T. Proud // Copyright (c) 2013, Prometheus Team
// All rights reserved. // All rights reserved.
// //
// Use of this source code is governed by a BSD-style license that can be found // Use of this source code is governed by a BSD-style license that can be found
// in the LICENSE file. // in the LICENSE file.
// //
package registry package prometheus
import ( import (
"github.com/prometheus/client_golang/maths"
"github.com/prometheus/client_golang/metrics"
"time" "time"
) )
@ -17,18 +15,18 @@ import (
// exposed if the DefaultRegistry's exporter is hooked into the HTTP request // exposed if the DefaultRegistry's exporter is hooked into the HTTP request
// handler. // handler.
var ( var (
marshalErrorCount = metrics.NewCounter() marshalErrorCount = NewCounter()
dumpErrorCount = metrics.NewCounter() dumpErrorCount = NewCounter()
requestCount = metrics.NewCounter() requestCount = NewCounter()
requestLatencyBuckets = metrics.LogarithmicSizedBucketsFor(0, 1000) requestLatencyBuckets = LogarithmicSizedBucketsFor(0, 1000)
requestLatency = metrics.NewHistogram(&metrics.HistogramSpecification{ requestLatency = NewHistogram(&HistogramSpecification{
Starts: requestLatencyBuckets, Starts: requestLatencyBuckets,
BucketBuilder: metrics.AccumulatingBucketBuilder(metrics.EvictAndReplaceWith(50, maths.Average), 1000), BucketBuilder: AccumulatingBucketBuilder(EvictAndReplaceWith(50, AverageReducer), 1000),
ReportablePercentiles: []float64{0.01, 0.05, 0.5, 0.9, 0.99}, ReportablePercentiles: []float64{0.01, 0.05, 0.5, 0.9, 0.99},
}) })
startTime = metrics.NewGauge() startTime = NewGauge()
) )
func init() { func init() {
@ -42,7 +40,7 @@ func init() {
// This callback accumulates the microsecond duration of the reporting // This callback accumulates the microsecond duration of the reporting
// framework's overhead such that it can be reported. // framework's overhead such that it can be reported.
var requestLatencyAccumulator metrics.CompletionCallback = func(duration time.Duration) { var requestLatencyAccumulator CompletionCallback = func(duration time.Duration) {
microseconds := float64(duration / time.Microsecond) microseconds := float64(duration / time.Microsecond)
requestLatency.Add(nil, microseconds) requestLatency.Add(nil, microseconds)

View File

@ -1,10 +1,10 @@
// Copyright (c) 2012, Matt T. Proud // Copyright (c) 2013, Prometheus Team
// All rights reserved. // All rights reserved.
// //
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package utility package prometheus
import ( import (
. "github.com/matttproud/gocheck" . "github.com/matttproud/gocheck"
@ -19,7 +19,7 @@ var ValueEquals Checker = &valueEqualsChecker{
} }
func (checker *valueEqualsChecker) Check(params []interface{}, names []string) (result bool, error string) { func (checker *valueEqualsChecker) Check(params []interface{}, names []string) (result bool, error string) {
actual := params[0].(*Item).Value actual := params[0].(*item).Value
expected := params[1] expected := params[1]
return actual == expected, "" return actual == expected, ""

View File

@ -1,10 +1,10 @@
// Copyright (c) 2012, Matt T. Proud // Copyright (c) 2013, Prometheus Team
// All rights reserved. // All rights reserved.
// //
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package metrics package prometheus
import ( import (
"time" "time"

View File

@ -1,10 +1,10 @@
// Copyright (c) 2012, Matt T. Proud // Copyright (c) 2013, Prometheus Team
// All rights reserved. // All rights reserved.
// //
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package metrics package prometheus
import ( import (
. "github.com/matttproud/gocheck" . "github.com/matttproud/gocheck"

View File

@ -1,20 +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.
//
// The utility package provides general purpose helpers to assist with this
// library.
//
// 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

View File

@ -1,20 +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.
package utility
import (
. "github.com/matttproud/gocheck"
"testing"
)
type S struct{}
var _ = Suite(&S{})
func TestUtility(t *testing.T) {
TestingT(t)
}