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
before_script:
- go get -v github.com/prometheus/client_golang
script:
- go build -a -v github.com/prometheus/client_golang/...
- go test -v github.com/prometheus/client_golang/...
- make -f Makefile.TRAVIS

View File

@ -1,4 +1,4 @@
Copyright (c) 2012, Matt T. Proud
Copyright (c) 2013, Prometheus Team
All rights reserved.
Redistribution and use in source and binary forms, with or without

View File

@ -16,17 +16,20 @@ MAKE_ARTIFACTS = search_index
all: test
build:
go build ./...
$(MAKE) -C prometheus build
$(MAKE) -C examples 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:
find . -iname '*.go' -exec gofmt -w -s=true '{}' ';'
advice:
go tool vet .
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'
clean:
$(MAKE) -C examples clean
rm -f $(MAKE_ARTIFACTS)
find . -iname '*~' -exec rm -f '{}' ';'
find . -iname '*#' -exec rm -f '{}' ';'
.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.
- Decouple HTTP report handler from our project and incorporate into this
repository.
- Implement labeled metric support.
- 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.
//
// Use of this source code is governed by a BSD-style license that can be found
// in the LICENSE file.
// 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

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.
//
// Use of this source code is governed by a BSD-style
@ -10,8 +10,8 @@ package main
import (
"flag"
"github.com/prometheus/client_golang"
"github.com/prometheus/client_golang/exp"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/exp"
"net/http"
)
@ -48,7 +48,7 @@ func main() {
exp.HandleFunc("/hello", helloHandler)
exp.HandleFunc("/goodbye", goodbyeHandler)
exp.HandleFunc("/teapot", teapotHandler)
exp.Handle(registry.ExpositionResource, registry.DefaultHandler)
exp.Handle(prometheus.ExpositionResource, prometheus.DefaultHandler)
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.
//
// Use of this source code is governed by a BSD-style
@ -13,9 +13,7 @@ package main
import (
"flag"
"github.com/prometheus/client_golang"
"github.com/prometheus/client_golang/maths"
"github.com/prometheus/client_golang/metrics"
"github.com/prometheus/client_golang/prometheus"
"math/rand"
"net/http"
"time"
@ -28,27 +26,27 @@ var (
// Create a histogram to track fictitious interservice RPC latency for three
// distinct services.
rpcLatency = metrics.NewHistogram(&metrics.HistogramSpecification{
rpcLatency = prometheus.NewHistogram(&prometheus.HistogramSpecification{
// Four distinct histogram buckets for values:
// - equally-sized,
// - 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
// holds sample values subject to an eviction policy:
// - 50 elements are allowed per bucket.
// - Once 50 have been reached, the bucket empties 10 elements, averages the
// 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.
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
// registry for metrics, which is a really bad idea when using Prometheus-
// enabled library code, you can create your own.
customRegistry = registry.NewRegistry()
customRegistry = prometheus.NewRegistry()
)
func main() {
@ -69,13 +67,13 @@ func main() {
}
}()
http.Handle(registry.ExpositionResource, customRegistry.Handler())
http.Handle(prometheus.ExpositionResource, customRegistry.Handler())
http.ListenAndServe(*listeningAddress, nil)
}
func init() {
customRegistry.Register("rpc_latency_microseconds", "RPC latency.", registry.NilLabels, rpcLatency)
customRegistry.Register("rpc_calls_total", "RPC calls.", registry.NilLabels, rpcCalls)
customRegistry.Register("rpc_latency_microseconds", "RPC latency.", prometheus.NilLabels, rpcLatency)
customRegistry.Register("rpc_calls_total", "RPC calls.", prometheus.NilLabels, rpcCalls)
}
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.
//
// Use of this source code is governed by a BSD-style
@ -10,14 +10,14 @@ package main
import (
"flag"
"github.com/prometheus/client_golang"
"github.com/prometheus/client_golang/prometheus"
"net/http"
)
func main() {
flag.Parse()
http.Handle(registry.ExpositionResource, registry.DefaultHandler)
http.Handle(prometheus.ExpositionResource, prometheus.DefaultHandler)
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.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package metrics
package prometheus
import (
"bytes"
"container/heap"
"fmt"
"github.com/prometheus/client_golang/utility"
"math"
"sort"
"sync"
@ -18,7 +17,7 @@ import (
)
type AccumulatingBucket struct {
elements utility.PriorityQueue
elements priorityQueue
evictionPolicy EvictionPolicy
maximumSize int
mutex sync.RWMutex
@ -31,7 +30,7 @@ type AccumulatingBucket struct {
func AccumulatingBucketBuilder(evictionPolicy EvictionPolicy, maximumSize int) BucketBuilder {
return func() Bucket {
return &AccumulatingBucket{
elements: make(utility.PriorityQueue, 0, maximumSize),
elements: make(priorityQueue, 0, maximumSize),
evictionPolicy: evictionPolicy,
maximumSize: maximumSize,
}
@ -47,7 +46,7 @@ func (b *AccumulatingBucket) Add(value float64) {
b.observations++
size := len(b.elements)
v := utility.Item{
v := item{
Priority: -1 * time.Now().UnixNano(),
Value: value,
}

View File

@ -1,15 +1,13 @@
// Copyright (c) 2012, Matt T. Proud
// 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 metrics
package prometheus
import (
. "github.com/matttproud/gocheck"
"github.com/prometheus/client_golang/maths"
"github.com/prometheus/client_golang/utility"
"time"
)
@ -51,7 +49,7 @@ func (s *S) TestAccumulatingBucketBuilderWithEvictOldest(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))
@ -86,7 +84,7 @@ func (s *S) TestAccumulatingBucketBuilderWithEvictAndReplaceWithAverage(c *C) {
func (s *S) TestAccumulatingBucket(c *C) {
var b AccumulatingBucket = AccumulatingBucket{
elements: make(utility.PriorityQueue, 0, 10),
elements: make(priorityQueue, 0, 10),
maximumSize: 5,
}
@ -112,13 +110,13 @@ func (s *S) TestAccumulatingBucket(c *C) {
func (s *S) TestAccumulatingBucketValueForIndex(c *C) {
var b AccumulatingBucket = AccumulatingBucket{
elements: make(utility.PriorityQueue, 0, 100),
elements: make(priorityQueue, 0, 100),
maximumSize: 100,
evictionPolicy: EvictOldest(50),
}
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.

View File

@ -1,12 +1,10 @@
// Copyright (c) 2012, Matt T. Proud
// 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.
// bucket.go provides fundamental interface expectations for various bucket
// types.
package metrics
package prometheus
// The Histogram class and associated types build buckets on their own.
type BucketBuilder func() Bucket

View File

@ -1,10 +1,10 @@
// Copyright (c) 2013, Matt T. Proud
// 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 registry
package prometheus
var (
// NilLabels is a nil set of labels merely for end-user convenience.
@ -14,8 +14,7 @@ var (
// interface.
DefaultHandler = DefaultRegistry.Handler()
// 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.
DefaultRegistry = NewRegistry()
)
@ -39,4 +38,14 @@ const (
docstringKey = "docstring"
metricKey = "metric"
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.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package metrics
package prometheus
import (
"fmt"
"github.com/prometheus/client_golang/utility"
"sync"
)
@ -49,7 +48,7 @@ func (metric *counter) Set(labels map[string]string, value float64) float64 {
labels = map[string]string{}
}
signature := utility.LabelsToSignature(labels)
signature := labelsToSignature(labels)
if original, ok := metric.values[signature]; ok {
original.value = value
} else {
@ -91,7 +90,7 @@ func (metric *counter) IncrementBy(labels map[string]string, value float64) floa
labels = map[string]string{}
}
signature := utility.LabelsToSignature(labels)
signature := labelsToSignature(labels)
if original, ok := metric.values[signature]; ok {
original.value += value
} else {
@ -116,7 +115,7 @@ func (metric *counter) DecrementBy(labels map[string]string, value float64) floa
labels = map[string]string{}
}
signature := utility.LabelsToSignature(labels)
signature := labelsToSignature(labels)
if original, ok := metric.values[signature]; ok {
original.value -= value
} else {

View File

@ -1,18 +1,17 @@
// Copyright (c) 2012, Matt T. Proud
// 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 metrics
package prometheus
import (
"encoding/json"
"github.com/prometheus/client_golang/utility/test"
"testing"
)
func testCounter(t test.Tester) {
func testCounter(t tester) {
type input struct {
steps []func(g Counter)
}

View File

@ -1,17 +1,17 @@
// Copyright (c) 2012, Matt T. Proud
// 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 maths
package prometheus
import (
"math"
)
// Go's standard library does not offer a factorial function.
func Factorial(of int) int64 {
func factorial(of int) int64 {
if of <= 0 {
return 1
}
@ -28,8 +28,8 @@ func Factorial(of int) int64 {
// 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
// p is the probability.
func BinomialPDF(k, n int, p float64) float64 {
binomialCoefficient := float64(Factorial(n)) / float64(Factorial(k)*Factorial(n-k))
func binomialPDF(k, n int, p float64) float64 {
binomialCoefficient := float64(factorial(n)) / float64(factorial(k)*factorial(n-k))
intermediate := math.Pow(p, float64(k)) * math.Pow(1-p, float64(n-k))
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.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package metrics
package prometheus
import (
"container/heap"
"github.com/prometheus/client_golang/maths"
"github.com/prometheus/client_golang/utility"
"time"
)
@ -29,17 +27,17 @@ func EvictOldest(count int) EvictionPolicy {
// This factory produces an EvictionPolicy that applies some standardized
// 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) {
oldValues := make([]float64, count)
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)
heap.Push(h, &utility.Item{
heap.Push(h, &item{
Value: reduced,
// TODO(mtp): Parameterize the priority generation since these tools are
// 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.
//
// Use of this source code is governed by a BSD-style license that can be found in
@ -8,8 +8,7 @@ package exp
import (
"fmt"
"github.com/prometheus/client_golang"
"github.com/prometheus/client_golang/metrics"
"github.com/prometheus/client_golang/prometheus"
"net/http"
"strings"
"time"
@ -33,11 +32,11 @@ type (
)
var (
requestCounts = metrics.NewCounter()
requestDuration = metrics.NewCounter()
requestDurations = metrics.NewDefaultHistogram()
requestBytes = metrics.NewCounter()
responseBytes = metrics.NewCounter()
requestCounts = prometheus.NewCounter()
requestDuration = prometheus.NewCounter()
requestDurations = prometheus.NewDefaultHistogram()
requestBytes = prometheus.NewCounter()
responseBytes = prometheus.NewCounter()
// DefaultCoarseMux is a drop-in replacement for http.DefaultServeMux that
// provides standardized telemetry for Go's standard HTTP handler registration
@ -103,9 +102,9 @@ func HandleFunc(pattern string, handler http.HandlerFunc) {
}
func init() {
registry.Register("http_requests_total", "A counter of the total number of HTTP requests made against the default multiplexor.", registry.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)
registry.Register("http_request_durations_microseconds", "The amounts of time the default multiplexor has spent answering HTTP requests (microseconds).", registry.NilLabels, requestDurations)
registry.Register("http_request_bytes_total", "The total volume of content body sizes received (bytes).", registry.NilLabels, requestBytes)
registry.Register("http_response_bytes_total", "The total volume of response payloads emitted (bytes).", registry.NilLabels, responseBytes)
prometheus.Register("http_requests_total", "A counter of the total number of HTTP requests made against the default multiplexor.", prometheus.NilLabels, requestCounts)
prometheus.Register("http_request_durations_total_microseconds", "The total amount of time the default multiplexor has spent answering HTTP requests (microseconds).", prometheus.NilLabels, requestDuration)
prometheus.Register("http_request_durations_microseconds", "The amounts of time the default multiplexor has spent answering HTTP requests (microseconds).", prometheus.NilLabels, requestDurations)
prometheus.Register("http_request_bytes_total", "The total volume of content body sizes received (bytes).", prometheus.NilLabels, requestBytes)
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.
//
// 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.
//
// 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.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package metrics
package prometheus
import (
"fmt"
"github.com/prometheus/client_golang/utility"
"sync"
)
@ -56,7 +55,7 @@ func (metric *gauge) Set(labels map[string]string, value float64) float64 {
labels = map[string]string{}
}
signature := utility.LabelsToSignature(labels)
signature := labelsToSignature(labels)
if original, ok := metric.values[signature]; ok {
original.value = value

View File

@ -1,18 +1,17 @@
// Copyright (c) 2012, Matt T. Proud
// 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 metrics
package prometheus
import (
"encoding/json"
"github.com/prometheus/client_golang/utility/test"
"testing"
)
func testGauge(t test.Tester) {
func testGauge(t tester) {
type input struct {
steps []func(g Gauge)
}

View File

@ -1,10 +1,10 @@
// Copyright (c) 2012, Matt T. Proud
// 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 maths
package prometheus
import (
. "github.com/matttproud/gocheck"

View File

@ -1,16 +1,14 @@
// Copyright (c) 2012, Matt T. Proud
// 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 metrics
package prometheus
import (
"bytes"
"fmt"
"github.com/prometheus/client_golang/maths"
"github.com/prometheus/client_golang/utility"
"math"
"strconv"
"sync"
@ -96,7 +94,7 @@ func (h *histogram) Add(labels map[string]string, value float64) {
labels = map[string]string{}
}
signature := utility.LabelsToSignature(labels)
signature := labelsToSignature(labels)
var histogram *histogramVector = nil
if original, ok := h.values[signature]; ok {
histogram = original
@ -298,7 +296,7 @@ func NewDefaultHistogram() Histogram {
return NewHistogram(
&HistogramSpecification{
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},
},
)

View File

@ -1,9 +1,9 @@
// Copyright (c) 2013, Matt T. Proud
// 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 metrics
package prometheus
// 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.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package test
package prometheus
type Tester interface {
type tester interface {
Error(args ...interface{})
Errorf(format string, 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.
//
// Use of this source code is governed by a BSD-style
// 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.
type Metric interface {

View File

@ -1,44 +1,44 @@
// Copyright (c) 2012, Matt T. Proud
// 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 utility
package prometheus
type Item struct {
type item struct {
Priority int64
Value interface{}
index int
}
type PriorityQueue []*Item
type priorityQueue []*item
func (q PriorityQueue) Len() int {
func (q priorityQueue) Len() int {
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
}
func (q PriorityQueue) Swap(i, j int) {
func (q priorityQueue) Swap(i, j int) {
q[i], q[j] = q[j], q[i]
q[i].index = i
q[j].index = j
}
func (q *PriorityQueue) Push(x interface{}) {
func (q *priorityQueue) Push(x interface{}) {
queue := *q
size := len(queue)
queue = queue[0 : size+1]
item := x.(*Item)
item := x.(*item)
item.index = size
queue[size] = item
*q = queue
}
func (q *PriorityQueue) Pop() interface{} {
func (q *priorityQueue) Pop() interface{} {
queue := *q
size := len(queue)
item := queue[size-1]

View File

@ -1,10 +1,10 @@
// Copyright (c) 2012, Matt T. Proud
// 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 utility
package prometheus
import (
"container/heap"
@ -12,16 +12,16 @@ import (
)
func (s *S) TestPriorityQueueSort(c *C) {
q := make(PriorityQueue, 0, 6)
q := make(priorityQueue, 0, 6)
c.Check(len(q), Equals, 0)
heap.Push(&q, &Item{Value: "newest", Priority: -100})
heap.Push(&q, &Item{Value: "older", Priority: 90})
heap.Push(&q, &Item{Value: "oldest", Priority: 100})
heap.Push(&q, &Item{Value: "newer", Priority: -90})
heap.Push(&q, &Item{Value: "new", Priority: -80})
heap.Push(&q, &Item{Value: "old", Priority: 80})
heap.Push(&q, &item{Value: "newest", Priority: -100})
heap.Push(&q, &item{Value: "older", Priority: 90})
heap.Push(&q, &item{Value: "oldest", Priority: 100})
heap.Push(&q, &item{Value: "newer", Priority: -90})
heap.Push(&q, &item{Value: "new", Priority: -80})
heap.Push(&q, &item{Value: "old", Priority: 80})
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.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package metrics
package prometheus
import (
. "github.com/matttproud/gocheck"
@ -15,6 +15,6 @@ type S struct{}
var _ = Suite(&S{})
func TestMetrics(t *testing.T) {
func TestPrometheus(t *testing.T) {
TestingT(t)
}

View File

@ -1,10 +1,10 @@
// Copyright (c) 2012, Matt T. Proud
// 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 registry
package prometheus
import (
"compress/gzip"
@ -12,8 +12,6 @@ import (
"encoding/json"
"flag"
"fmt"
"github.com/prometheus/client_golang/metrics"
"github.com/prometheus/client_golang/utility"
"io"
"log"
"net/http"
@ -46,7 +44,7 @@ var (
type container struct {
baseLabels map[string]string
docstring string
metric metrics.Metric
metric Metric
name string
}
@ -61,7 +59,7 @@ type registry struct {
// own.
type Registry interface {
// 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
// against it generate a representation of the housed metrics.
Handler() http.HandlerFunc
@ -79,7 +77,7 @@ func NewRegistry() Registry {
}
// 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)
}
@ -110,7 +108,7 @@ func (r registry) isValidCandidate(name string, baseLabels map[string]string) (s
}
baseLabels[nameLabel] = name
signature = utility.LabelsToSignature(baseLabels)
signature = labelsToSignature(baseLabels)
if _, contains := r.signatureContainers[signature]; contains {
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
}
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()
defer r.mutex.Unlock()
@ -271,7 +269,7 @@ func (registry registry) YieldExporter() http.HandlerFunc {
func (registry registry) Handler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var instrumentable metrics.InstrumentableCall = func() {
var instrumentable InstrumentableCall = func() {
requestCount.Increment(nil)
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.
//
// Use of this source code is governed by a BSD-style license that can be found
// in the LICENSE file.
package registry
package prometheus
import (
"bytes"
"fmt"
"github.com/prometheus/client_golang/metrics"
"github.com/prometheus/client_golang/utility/test"
"io"
"net/http"
"testing"
)
func testRegister(t test.Tester) {
func testRegister(t tester) {
var oldState = struct {
abortOnMisuse bool
debugRegistration bool
@ -181,7 +179,7 @@ func (r *fakeResponseWriter) Write(d []byte) (l int, err error) {
func (r *fakeResponseWriter) WriteHeader(c int) {
}
func testDecorateWriter(t test.Tester) {
func testDecorateWriter(t tester) {
type input struct {
headers map[string]string
body []byte
@ -266,9 +264,9 @@ func BenchmarkDecorateWriter(b *testing.B) {
}
}
func testDumpToWriter(t test.Tester) {
func testDumpToWriter(t tester) {
type input struct {
metrics map[string]metrics.Metric
metrics map[string]Metric
}
var scenarios = []struct {
@ -280,17 +278,17 @@ func testDumpToWriter(t test.Tester) {
},
{
in: input{
metrics: map[string]metrics.Metric{
"foo": metrics.NewCounter(),
metrics: map[string]Metric{
"foo": NewCounter(),
},
},
out: []byte("[{\"baseLabels\":{\"label_foo\":\"foo\",\"name\":\"foo\"},\"docstring\":\"metric foo\",\"metric\":{\"type\":\"counter\",\"value\":[]}}]"),
},
{
in: input{
metrics: map[string]metrics.Metric{
"foo": metrics.NewCounter(),
"bar": metrics.NewCounter(),
metrics: map[string]Metric{
"foo": 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\":[]}}]"),

View File

@ -1,10 +1,10 @@
// Copyright (c) 2013, Matt T. Proud
// 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 utility
package prometheus
import (
"bytes"
@ -17,7 +17,7 @@ const (
// LabelsToSignature provides a way of building a unique signature
// (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
// do not occur in less-than-diligent environments.
cardinality := len(labels)

View File

@ -1,17 +1,16 @@
// Copyright (c) 2013, Matt T. Proud
// 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 utility
package prometheus
import (
"github.com/prometheus/client_golang/utility/test"
"testing"
)
func testLabelsToSignature(t test.Tester) {
func testLabelsToSignature(t tester) {
var scenarios = []struct {
in map[string]string
out string
@ -24,7 +23,7 @@ func testLabelsToSignature(t test.Tester) {
}
for i, scenario := range scenarios {
actual := LabelsToSignature(scenario.in)
actual := labelsToSignature(scenario.in)
if actual != scenario.out {
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.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package maths
package prometheus
import (
"math"
@ -14,11 +14,32 @@ import (
// 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.
// ReductionMethod provides a method for reducing metrics into a scalar value.
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
sum := 0.0
@ -34,11 +55,10 @@ var Average ReductionMethod = func(input []float64) float64 {
return sum / count
}
// Extract the first modal value.
var FirstMode ReductionMethod = func(input []float64) float64 {
func firstModeReducer(input []float64) float64 {
valuesToFrequency := map[float64]int64{}
var largestTally int64 = math.MinInt64
var largestTallyValue float64 = math.NaN()
largestTally := int64(math.MinInt64)
largestTallyValue := math.NaN()
for _, v := range input {
presentCount, _ := valuesToFrequency[v]
@ -56,7 +76,7 @@ var FirstMode ReductionMethod = func(input []float64) float64 {
}
// 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)
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.
func NearestRankReducer(percentile float64) ReductionMethod {
return func(input []float64) float64 {
return NearestRank(input, percentile)
return nearestRank(input, percentile)
}
}
var Median ReductionMethod = NearestRankReducer(50)
var Minimum ReductionMethod = func(input []float64) float64 {
var minimum float64 = math.MaxFloat64
func minimumReducer(input []float64) float64 {
minimum := math.MaxFloat64
for _, v := range input {
minimum = math.Min(minimum, v)
@ -97,8 +115,8 @@ var Minimum ReductionMethod = func(input []float64) float64 {
return minimum
}
var Maximum ReductionMethod = func(input []float64) float64 {
var maximum float64 = math.SmallestNonzeroFloat64
func maximumReducer(input []float64) float64 {
maximum := math.SmallestNonzeroFloat64
for _, v := range input {
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.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package maths
package prometheus
import (
. "github.com/matttproud/gocheck"
@ -12,42 +12,42 @@ import (
func (s *S) TestAverageOnEmpty(c *C) {
empty := []float64{}
var v float64 = Average(empty)
var v float64 = AverageReducer(empty)
c.Assert(v, IsNaN)
}
func (s *S) TestAverageForSingleton(c *C) {
input := []float64{5}
var v float64 = Average(input)
var v float64 = AverageReducer(input)
c.Check(v, Equals, 5.0)
}
func (s *S) TestAverage(c *C) {
input := []float64{5, 15}
var v float64 = Average(input)
var v float64 = AverageReducer(input)
c.Check(v, Equals, 10.0)
}
func (s *S) TestFirstModeOnEmpty(c *C) {
input := []float64{}
var v float64 = FirstMode(input)
var v float64 = FirstModeReducer(input)
c.Assert(v, IsNaN)
}
func (s *S) TestFirstModeForSingleton(c *C) {
input := []float64{5}
var v float64 = FirstMode(input)
var v float64 = FirstModeReducer(input)
c.Check(v, Equals, 5.0)
}
func (s *S) TestFirstModeForUnimodal(c *C) {
input := []float64{1, 2, 3, 4, 3}
var v float64 = FirstMode(input)
var v float64 = FirstModeReducer(input)
c.Check(v, Equals, 3.0)
}
@ -55,25 +55,25 @@ func (s *S) TestFirstModeForUnimodal(c *C) {
func (s *S) TestNearestRankForEmpty(c *C) {
input := []float64{}
c.Assert(NearestRank(input, 0), IsNaN)
c.Assert(NearestRank(input, 50), IsNaN)
c.Assert(NearestRank(input, 100), IsNaN)
c.Assert(nearestRank(input, 0), IsNaN)
c.Assert(nearestRank(input, 50), IsNaN)
c.Assert(nearestRank(input, 100), IsNaN)
}
func (s *S) TestNearestRankForSingleton(c *C) {
input := []float64{5}
c.Check(NearestRank(input, 0), Equals, 5.0)
c.Check(NearestRank(input, 50), Equals, 5.0)
c.Check(NearestRank(input, 100), Equals, 5.0)
c.Check(nearestRank(input, 0), Equals, 5.0)
c.Check(nearestRank(input, 50), Equals, 5.0)
c.Check(nearestRank(input, 100), Equals, 5.0)
}
func (s *S) TestNearestRankForDouble(c *C) {
input := []float64{5, 5}
c.Check(NearestRank(input, 0), Equals, 5.0)
c.Check(NearestRank(input, 50), Equals, 5.0)
c.Check(NearestRank(input, 100), Equals, 5.0)
c.Check(nearestRank(input, 0), Equals, 5.0)
c.Check(nearestRank(input, 50), Equals, 5.0)
c.Check(nearestRank(input, 100), Equals, 5.0)
}
func (s *S) TestNearestRankFor100(c *C) {
@ -83,9 +83,9 @@ func (s *S) TestNearestRankFor100(c *C) {
input[i] = float64(i + 1)
}
c.Check(NearestRank(input, 0), Equals, 1.0)
c.Check(NearestRank(input, 50), Equals, 51.0)
c.Check(NearestRank(input, 100), Equals, 100.0)
c.Check(nearestRank(input, 0), Equals, 1.0)
c.Check(nearestRank(input, 50), Equals, 51.0)
c.Check(nearestRank(input, 100), Equals, 100.0)
}
func (s *S) TestNearestRankFor101(c *C) {
@ -95,25 +95,25 @@ func (s *S) TestNearestRankFor101(c *C) {
input[i] = float64(i + 1)
}
c.Check(NearestRank(input, 0), Equals, 1.0)
c.Check(NearestRank(input, 50), Equals, 51.0)
c.Check(NearestRank(input, 100), Equals, 101.0)
c.Check(nearestRank(input, 0), Equals, 1.0)
c.Check(nearestRank(input, 50), Equals, 51.0)
c.Check(nearestRank(input, 100), Equals, 101.0)
}
func (s *S) TestMedianReducer(c *C) {
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) {
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) {
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.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package metrics
package prometheus
import (
"fmt"
"github.com/prometheus/client_golang/maths"
"math"
"sync"
)
@ -35,38 +34,49 @@ func emptyFilter(e TallyingIndexEstimator) TallyingIndexEstimator {
}
}
// Report the smallest observed value in the bucket.
var Minimum TallyingIndexEstimator = 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 {
var (
minimumEstimator = emptyFilter(func(minimum, maximum float64, _, observations int) float64 {
return minimum
}
})
location := float64(index) / float64(observations)
if location > upperThird {
maximumEstimator = emptyFilter(func(minimum, maximum float64, _, observations int) float64 {
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.
// 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.
func DefaultTallyingBucket() TallyingBucket {
return TallyingBucket{
estimator: Minimum,
estimator: MinimumEstimator,
largestObserved: math.SmallestNonzeroFloat64,
smallestObserved: math.MaxFloat64,
}

View File

@ -1,40 +1,39 @@
// Copyright (c) 2012, Matt T. Proud
// 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 metrics
package prometheus
import (
. "github.com/matttproud/gocheck"
"github.com/prometheus/client_golang/maths"
)
func (s *S) TestTallyingPercentileEstimatorMinimum(c *C) {
c.Assert(Minimum(-2, -1, 0, 0), maths.IsNaN)
c.Check(Minimum(-2, -1, 0, 1), Equals, -2.0)
c.Assert(MinimumEstimator(-2, -1, 0, 0), IsNaN)
c.Check(MinimumEstimator(-2, -1, 0, 1), Equals, -2.0)
}
func (s *S) TestTallyingPercentileEstimatorMaximum(c *C) {
c.Assert(Maximum(-2, -1, 0, 0), maths.IsNaN)
c.Check(Maximum(-2, -1, 0, 1), Equals, -1.0)
c.Assert(MaximumEstimator(-2, -1, 0, 0), IsNaN)
c.Check(MaximumEstimator(-2, -1, 0, 1), Equals, -1.0)
}
func (s *S) TestTallyingPercentilesEstimatorAverage(c *C) {
c.Assert(Average(-2, -1, 0, 0), maths.IsNaN)
c.Check(Average(-2, -2, 0, 1), Equals, -2.0)
c.Check(Average(-1, -1, 0, 1), Equals, -1.0)
c.Check(Average(1, 1, 0, 2), Equals, 1.0)
c.Check(Average(2, 1, 0, 2), Equals, 1.5)
c.Assert(AverageEstimator(-2, -1, 0, 0), IsNaN)
c.Check(AverageEstimator(-2, -2, 0, 1), Equals, -2.0)
c.Check(AverageEstimator(-1, -1, 0, 1), Equals, -1.0)
c.Check(AverageEstimator(1, 1, 0, 2), Equals, 1.0)
c.Check(AverageEstimator(2, 1, 0, 2), Equals, 1.5)
}
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(Uniform(-5, 5, 1, 2), Equals, 0.0)
c.Check(Uniform(-5, 5, 2, 2), Equals, 5.0)
c.Check(UniformEstimator(-5, 5, 0, 2), Equals, -5.0)
c.Check(UniformEstimator(-5, 5, 1, 2), Equals, 0.0)
c.Check(UniformEstimator(-5, 5, 2, 2), Equals, 5.0)
}
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.
//
// Use of this source code is governed by a BSD-style license that can be found
// in the LICENSE file.
//
package registry
package prometheus
import (
"github.com/prometheus/client_golang/maths"
"github.com/prometheus/client_golang/metrics"
"time"
)
@ -17,18 +15,18 @@ import (
// exposed if the DefaultRegistry's exporter is hooked into the HTTP request
// handler.
var (
marshalErrorCount = metrics.NewCounter()
dumpErrorCount = metrics.NewCounter()
marshalErrorCount = NewCounter()
dumpErrorCount = NewCounter()
requestCount = metrics.NewCounter()
requestLatencyBuckets = metrics.LogarithmicSizedBucketsFor(0, 1000)
requestLatency = metrics.NewHistogram(&metrics.HistogramSpecification{
requestCount = NewCounter()
requestLatencyBuckets = LogarithmicSizedBucketsFor(0, 1000)
requestLatency = NewHistogram(&HistogramSpecification{
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},
})
startTime = metrics.NewGauge()
startTime = NewGauge()
)
func init() {
@ -42,7 +40,7 @@ func init() {
// 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 CompletionCallback = func(duration time.Duration) {
microseconds := float64(duration / time.Microsecond)
requestLatency.Add(nil, microseconds)

View File

@ -1,10 +1,10 @@
// Copyright (c) 2012, Matt T. Proud
// 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 utility
package prometheus
import (
. "github.com/matttproud/gocheck"
@ -19,7 +19,7 @@ var ValueEquals Checker = &valueEqualsChecker{
}
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]
return actual == expected, ""

View File

@ -1,10 +1,10 @@
// Copyright (c) 2012, Matt T. Proud
// 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 metrics
package prometheus
import (
"time"

View File

@ -1,10 +1,10 @@
// Copyright (c) 2012, Matt T. Proud
// 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 metrics
package prometheus
import (
. "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)
}