Merge pull request #9 from prometheus/refactor/user-experience/standardized-directories

Rearrange file and package per convention.
This commit is contained in:
Matt T. Proud 2013-04-28 10:43:02 -07:00
commit 4c1c13d109
64 changed files with 826 additions and 977 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

40
documentation/advice.md Normal file
View File

@ -0,0 +1,40 @@
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.

View File

@ -1,8 +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.
// A repository of various Prometheus client documentation and advice.
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)
}

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 metrics
import (
. "github.com/matttproud/gocheck"
"testing"
)
type S struct{}
var _ = Suite(&S{})
func TestMetrics(t *testing.T) {
TestingT(t)
}

View File

@ -1,62 +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 (
"time"
)
// This callback is called upon the completion of the timer—i.e., when it stops.
type CompletionCallback func(duration time.Duration)
// This is meant to capture a function that a StopWatch can call for purposes
// of instrumentation.
type InstrumentableCall func()
// StopWatch is the structure that captures instrumentation for durations.
// N.B.(mtp): A major limitation hereof is that the StopWatch protocol cannot
// retain instrumentation if a panic percolates within the context that is
// being measured.
type StopWatch interface {
Stop() time.Duration
}
type stopWatch struct {
endTime time.Time
onCompletion CompletionCallback
startTime time.Time
}
// Return a new StopWatch that is ready for instrumentation.
func Start(onCompletion CompletionCallback) StopWatch {
return &stopWatch{
onCompletion: onCompletion,
startTime: time.Now(),
}
}
// Stop the StopWatch returning the elapsed duration of its lifetime while
// firing an optional CompletionCallback in the background.
func (s *stopWatch) Stop() time.Duration {
s.endTime = time.Now()
duration := s.endTime.Sub(s.startTime)
if s.onCompletion != nil {
go s.onCompletion(duration)
}
return duration
}
// Provide a quick way of instrumenting a InstrumentableCall and emitting its
// duration.
func InstrumentCall(instrumentable InstrumentableCall, onCompletion CompletionCallback) time.Duration {
s := Start(onCompletion)
instrumentable()
return s.Stop()
}

View File

@ -1,69 +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 (
. "github.com/matttproud/gocheck"
"time"
)
func (s *S) TestTimerStart(c *C) {
stopWatch, ok := Start(nil).(*stopWatch)
if !ok {
c.Check(ok, Equals, true)
}
c.Assert(stopWatch, Not(IsNil))
c.Assert(stopWatch.startTime, Not(IsNil))
}
func (s *S) TestTimerStop(c *C) {
done := make(chan bool)
var callbackInvoked bool = false
var complete CompletionCallback = func(duration time.Duration) {
callbackInvoked = true
done <- true
}
stopWatch := Start(complete)
c.Check(callbackInvoked, Equals, false)
d := stopWatch.Stop()
<-done
c.Assert(d, Not(IsNil))
c.Check(callbackInvoked, Equals, true)
}
func (s *S) TestInstrumentCall(c *C) {
var callbackInvoked bool = false
var instrumentableInvoked bool = false
done := make(chan bool, 2)
var complete CompletionCallback = func(duration time.Duration) {
callbackInvoked = true
done <- true
}
var instrumentable InstrumentableCall = func() {
instrumentableInvoked = true
done <- true
}
d := InstrumentCall(instrumentable, complete)
c.Assert(d, Not(IsNil))
<-done
<-done
c.Check(instrumentableInvoked, Equals, true)
c.Check(callbackInvoked, Equals, true)
}

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,33 +1,32 @@
// 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"
"fmt"
"github.com/prometheus/client_golang/utility"
"sync"
)
// TODO(matt): Refactor to de-duplicate behaviors.
type Counter interface {
AsMarshallable() map[string]interface{}
Metric
Decrement(labels map[string]string) float64
DecrementBy(labels map[string]string, value float64) float64
Increment(labels map[string]string) float64
IncrementBy(labels map[string]string, value float64) float64
ResetAll()
Set(labels map[string]string, value float64) float64
String() string
}
type counterVector struct {
labels map[string]string
value float64
Labels map[string]string `json:"labels"`
Value float64 `json:"value"`
}
func NewCounter() Counter {
@ -49,13 +48,13 @@ 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
original.Value = value
} else {
metric.values[signature] = &counterVector{
labels: labels,
value: value,
Labels: labels,
Value: value,
}
}
@ -67,8 +66,8 @@ func (metric *counter) ResetAll() {
defer metric.mutex.Unlock()
for key, value := range metric.values {
for label := range value.labels {
delete(value.labels, label)
for label := range value.Labels {
delete(value.Labels, label)
}
delete(metric.values, key)
}
@ -91,13 +90,13 @@ 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
original.Value += value
} else {
metric.values[signature] = &counterVector{
labels: labels,
value: value,
Labels: labels,
Value: value,
}
}
@ -116,13 +115,13 @@ 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
original.Value -= value
} else {
metric.values[signature] = &counterVector{
labels: labels,
value: -1 * value,
Labels: labels,
Value: -1 * value,
}
}
@ -133,20 +132,18 @@ func (metric *counter) Decrement(labels map[string]string) float64 {
return metric.DecrementBy(labels, 1)
}
func (metric counter) AsMarshallable() map[string]interface{} {
func (metric counter) MarshalJSON() ([]byte, error) {
metric.mutex.RLock()
defer metric.mutex.RUnlock()
values := make([]map[string]interface{}, 0, len(metric.values))
values := make([]*counterVector, 0, len(metric.values))
for _, value := range metric.values {
values = append(values, map[string]interface{}{
labelsKey: value.labels,
valueKey: value.value,
})
values = append(values, value)
}
return map[string]interface{}{
return json.Marshal(map[string]interface{}{
valueKey: values,
typeKey: counterTypeValue,
}
})
}

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)
}
@ -29,7 +28,7 @@ func testCounter(t test.Tester) {
steps: []func(g Counter){},
},
out: output{
value: "{\"type\":\"counter\",\"value\":[]}",
value: `{"type":"counter","value":[]}`,
},
},
{
@ -41,7 +40,7 @@ func testCounter(t test.Tester) {
},
},
out: output{
value: "{\"type\":\"counter\",\"value\":[{\"labels\":{},\"value\":1}]}",
value: `{"type":"counter","value":[{"labels":{},"value":1}]}`,
},
},
{
@ -53,7 +52,7 @@ func testCounter(t test.Tester) {
},
},
out: output{
value: "{\"type\":\"counter\",\"value\":[{\"labels\":{},\"value\":2}]}",
value: `{"type":"counter","value":[{"labels":{},"value":2}]}`,
},
},
{
@ -68,7 +67,7 @@ func testCounter(t test.Tester) {
},
},
out: output{
value: "{\"type\":\"counter\",\"value\":[{\"labels\":{},\"value\":5}]}",
value: `{"type":"counter","value":[{"labels":{},"value":5}]}`,
},
},
{
@ -86,7 +85,7 @@ func testCounter(t test.Tester) {
},
},
out: output{
value: "{\"type\":\"counter\",\"value\":[]}",
value: `{"type":"counter","value":[]}`,
},
},
{
@ -98,7 +97,7 @@ func testCounter(t test.Tester) {
},
},
out: output{
value: "{\"type\":\"counter\",\"value\":[{\"labels\":{\"handler\":\"/foo\"},\"value\":19}]}",
value: `{"type":"counter","value":[{"labels":{"handler":"/foo"},"value":19}]}`,
},
},
{
@ -113,7 +112,7 @@ func testCounter(t test.Tester) {
},
},
out: output{
value: "{\"type\":\"counter\",\"value\":[{\"labels\":{\"handler\":\"/foo\"},\"value\":24}]}",
value: `{"type":"counter","value":[{"labels":{"handler":"/foo"},"value":24}]}`,
},
},
{
@ -125,7 +124,7 @@ func testCounter(t test.Tester) {
},
},
out: output{
value: "{\"type\":\"counter\",\"value\":[{\"labels\":{\"handler\":\"/foo\"},\"value\":1}]}",
value: `{"type":"counter","value":[{"labels":{"handler":"/foo"},"value":1}]}`,
},
},
{
@ -137,7 +136,7 @@ func testCounter(t test.Tester) {
},
},
out: output{
value: "{\"type\":\"counter\",\"value\":[{\"labels\":{\"handler\":\"/foo\"},\"value\":-1}]}",
value: `{"type":"counter","value":[{"labels":{"handler":"/foo"},"value":-1}]}`,
},
},
{
@ -152,7 +151,7 @@ func testCounter(t test.Tester) {
},
},
out: output{
value: "{\"type\":\"counter\",\"value\":[{\"labels\":{\"handler\":\"/foo\"},\"value\":28}]}",
value: `{"type":"counter","value":[{"labels":{"handler":"/foo"},"value":28}]}`,
},
},
{
@ -167,7 +166,7 @@ func testCounter(t test.Tester) {
},
},
out: output{
value: "{\"type\":\"counter\",\"value\":[{\"labels\":{\"handler\":\"/foo\"},\"value\":36}]}",
value: `{"type":"counter","value":[{"labels":{"handler":"/foo"},"value":36}]}`,
},
},
{
@ -182,7 +181,7 @@ func testCounter(t test.Tester) {
},
},
out: output{
value: "{\"type\":\"counter\",\"value\":[{\"labels\":{\"handler\":\"/foo\"},\"value\":27}]}",
value: `{"type":"counter","value":[{"labels":{"handler":"/foo"},"value":27}]}`,
},
},
}
@ -194,9 +193,7 @@ func testCounter(t test.Tester) {
step(counter)
}
marshallable := counter.AsMarshallable()
bytes, err := json.Marshal(marshallable)
bytes, err := json.Marshal(counter)
if err != nil {
t.Errorf("%d. could not marshal into JSON %s", i, err)
continue

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,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 (
"encoding/json"
"fmt"
"github.com/prometheus/client_golang/utility"
"sync"
)
@ -17,15 +17,13 @@ import (
// temperature or the hitherto bandwidth used, this would be the metric for such
// circumstances.
type Gauge interface {
AsMarshallable() map[string]interface{}
ResetAll()
Metric
Set(labels map[string]string, value float64) float64
String() string
}
type gaugeVector struct {
labels map[string]string
value float64
Labels map[string]string `json:"labels"`
Value float64 `json:"value"`
}
func NewGauge() Gauge {
@ -56,14 +54,14 @@ 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
original.Value = value
} else {
metric.values[signature] = &gaugeVector{
labels: labels,
value: value,
Labels: labels,
Value: value,
}
}
@ -75,27 +73,24 @@ func (metric *gauge) ResetAll() {
defer metric.mutex.Unlock()
for key, value := range metric.values {
for label := range value.labels {
delete(value.labels, label)
for label := range value.Labels {
delete(value.Labels, label)
}
delete(metric.values, key)
}
}
func (metric gauge) AsMarshallable() map[string]interface{} {
func (metric gauge) MarshalJSON() ([]byte, error) {
metric.mutex.RLock()
defer metric.mutex.RUnlock()
values := make([]map[string]interface{}, 0, len(metric.values))
values := make([]*gaugeVector, 0, len(metric.values))
for _, value := range metric.values {
values = append(values, map[string]interface{}{
labelsKey: value.labels,
valueKey: value.value,
})
values = append(values, value)
}
return map[string]interface{}{
return json.Marshal(map[string]interface{}{
typeKey: gaugeTypeValue,
valueKey: values,
}
})
}

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)
}
@ -29,7 +28,7 @@ func testGauge(t test.Tester) {
steps: []func(g Gauge){},
},
out: output{
value: "{\"type\":\"gauge\",\"value\":[]}",
value: `{"type":"gauge","value":[]}`,
},
},
{
@ -41,7 +40,7 @@ func testGauge(t test.Tester) {
},
},
out: output{
value: "{\"type\":\"gauge\",\"value\":[{\"labels\":{},\"value\":1}]}",
value: `{"type":"gauge","value":[{"labels":{},"value":1}]}`,
},
},
{
@ -53,7 +52,7 @@ func testGauge(t test.Tester) {
},
},
out: output{
value: "{\"type\":\"gauge\",\"value\":[{\"labels\":{},\"value\":2}]}",
value: `{"type":"gauge","value":[{"labels":{},"value":2}]}`,
},
},
{
@ -68,7 +67,7 @@ func testGauge(t test.Tester) {
},
},
out: output{
value: "{\"type\":\"gauge\",\"value\":[{\"labels\":{},\"value\":5}]}",
value: `{"type":"gauge","value":[{"labels":{},"value":5}]}`,
},
},
{
@ -86,7 +85,7 @@ func testGauge(t test.Tester) {
},
},
out: output{
value: "{\"type\":\"gauge\",\"value\":[]}",
value: `{"type":"gauge","value":[]}`,
},
},
{
@ -98,7 +97,7 @@ func testGauge(t test.Tester) {
},
},
out: output{
value: "{\"type\":\"gauge\",\"value\":[{\"labels\":{\"handler\":\"/foo\"},\"value\":19}]}",
value: `{"type":"gauge","value":[{"labels":{"handler":"/foo"},"value":19}]}`,
},
},
}
@ -110,9 +109,7 @@ func testGauge(t test.Tester) {
step(gauge)
}
marshallable := gauge.AsMarshallable()
bytes, err := json.Marshal(marshallable)
bytes, err := json.Marshal(gauge)
if err != nil {
t.Errorf("%d. could not marshal into JSON %s", i, err)
continue

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,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"
"encoding/json"
"fmt"
"github.com/prometheus/client_golang/maths"
"github.com/prometheus/client_golang/utility"
"math"
"strconv"
"sync"
@ -54,10 +53,8 @@ type HistogramSpecification struct {
}
type Histogram interface {
Metric
Add(labels map[string]string, value float64)
AsMarshallable() map[string]interface{}
ResetAll()
String() string
}
// The histogram is an accumulator for samples. It merely routes into which
@ -96,7 +93,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
@ -242,29 +239,30 @@ func formatFloat(value float64) string {
return strconv.FormatFloat(value, floatFormat, floatPrecision, floatBitCount)
}
func (h histogram) AsMarshallable() map[string]interface{} {
func (h histogram) MarshalJSON() ([]byte, error) {
h.mutex.RLock()
defer h.mutex.RUnlock()
result := make(map[string]interface{}, 2)
result[typeKey] = histogramTypeValue
values := make([]map[string]interface{}, 0, len(h.values))
for signature, value := range h.values {
metricContainer := map[string]interface{}{}
metricContainer[labelsKey] = value.labels
intermediate := map[string]interface{}{}
percentiles := make(map[string]float64, len(h.reportablePercentiles))
for _, percentile := range h.reportablePercentiles {
formatted := formatFloat(percentile)
intermediate[formatted] = h.percentile(signature, percentile)
}
metricContainer[valueKey] = intermediate
values = append(values, metricContainer)
percentiles[formatted] = h.percentile(signature, percentile)
}
result[valueKey] = values
values = append(values, map[string]interface{}{
labelsKey: value.labels,
valueKey: percentiles,
})
}
return result
return json.Marshal(map[string]interface{}{
typeKey: histogramTypeValue,
valueKey: values,
})
}
func (h *histogram) ResetAll() {
@ -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,15 +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"
// A Metric is something that can be exposed via the registry framework.
type Metric interface {
// Produce a JSON-consumable representation of the metric.
AsMarshallable() map[string]interface{}
// Produce a JSON representation of the metric.
json.Marshaler
// Reset the parent metrics and delete all child metrics.
ResetAll()
// Produce a human-consumable representation of the metric.

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 utility
package prometheus
import (
. "github.com/matttproud/gocheck"
@ -15,6 +15,6 @@ type S struct{}
var _ = Suite(&S{})
func TestUtility(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,14 +12,13 @@ import (
"encoding/json"
"flag"
"fmt"
"github.com/prometheus/client_golang/metrics"
"github.com/prometheus/client_golang/utility"
"io"
"log"
"net/http"
"sort"
"strings"
"sync"
"time"
)
const (
@ -44,9 +43,9 @@ var (
// container represents a top-level registered metric that encompasses its
// static metadata.
type container struct {
baseLabels map[string]string
docstring string
metric metrics.Metric
BaseLabels map[string]string `json:"baseLabels"`
Docstring string `json:"docstring"`
Metric Metric `json:"metric"`
name string
}
@ -61,7 +60,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,10 +78,28 @@ 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)
}
// Implements json.Marshaler
func (r registry) MarshalJSON() (_ []byte, err error) {
metrics := make([]interface{}, 0, len(r.signatureContainers))
keys := make([]string, 0, len(metrics))
for key := range r.signatureContainers {
keys = append(keys, key)
}
sort.Strings(keys)
for _, key := range keys {
metrics = append(metrics, r.signatureContainers[key])
}
return json.Marshal(metrics)
}
// isValidCandidate returns true if the candidate is acceptable for use. In the
// event of any apparent incorrect use it will report the problem, invalidate
// the candidate, or outright abort.
@ -110,7 +127,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)
@ -126,7 +143,7 @@ func (r registry) isValidCandidate(name string, baseLabels map[string]string) (s
if useAggressiveSanityChecks {
for _, container := range r.signatureContainers {
if container.name == name {
err = fmt.Errorf("metric named %s with baseLabels %s is already registered as %s and risks causing confusion", name, baseLabels, container.baseLabels)
err = fmt.Errorf("metric named %s with baseLabels %s is already registered as %s and risks causing confusion", name, baseLabels, container.BaseLabels)
if abortOnMisuse {
panic(err)
} else if debugRegistration {
@ -141,7 +158,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()
@ -155,9 +172,9 @@ func (r registry) Register(name, docstring string, baseLabels map[string]string,
}
r.signatureContainers[signature] = container{
baseLabels: baseLabels,
docstring: docstring,
metric: metric,
BaseLabels: baseLabels,
Docstring: docstring,
Metric: metric,
name: name,
}
@ -195,61 +212,6 @@ func (register registry) YieldBasicAuthExporter(username, password string) http.
})
}
func (registry registry) dumpToWriter(writer io.Writer) (err error) {
defer func() {
if err != nil {
dumpErrorCount.Increment(nil)
}
}()
numberOfMetrics := len(registry.signatureContainers)
keys := make([]string, 0, numberOfMetrics)
for key := range registry.signatureContainers {
keys = append(keys, key)
}
sort.Strings(keys)
_, err = writer.Write([]byte("["))
if err != nil {
return
}
index := 0
for _, key := range keys {
container := registry.signatureContainers[key]
intermediate := map[string]interface{}{
baseLabelsKey: container.baseLabels,
docstringKey: container.docstring,
metricKey: container.metric.AsMarshallable(),
}
marshaled, err := json.Marshal(intermediate)
if err != nil {
marshalErrorCount.Increment(nil)
index++
continue
}
if index > 0 && index < numberOfMetrics {
_, err = writer.Write([]byte(","))
if err != nil {
return err
}
}
_, err = writer.Write(marshaled)
if err != nil {
return err
}
index++
}
_, err = writer.Write([]byte("]"))
return
}
// decorateWriter annotates the response writer to handle any other behaviors
// that might be beneficial to the client---e.g., GZIP encoding.
func decorateWriter(request *http.Request, writer http.ResponseWriter) io.Writer {
@ -271,7 +233,8 @@ 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() {
defer requestLatencyAccumulator(time.Now())
requestCount.Increment(nil)
url := r.URL
@ -286,14 +249,10 @@ func (registry registry) Handler() http.HandlerFunc {
defer closer.Close()
}
registry.dumpToWriter(writer)
json.NewEncoder(writer).Encode(registry)
} else {
w.WriteHeader(http.StatusNotFound)
}
}
metrics.InstrumentCall(instrumentable, requestLatencyAccumulator)
}
}

View File

@ -1,22 +1,21 @@
// 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"
"encoding/json"
"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 +180,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 +265,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,20 +279,20 @@ 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\":[]}}]"),
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\":[]}}]"),
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":[]}}]`),
},
}
@ -307,15 +306,14 @@ func testDumpToWriter(t test.Tester) {
}
}
actual := &bytes.Buffer{}
actual, err := json.Marshal(registry)
err := registry.dumpToWriter(actual)
if err != nil {
t.Errorf("%d. encountered error while dumping %s", i, err)
}
if !bytes.Equal(scenario.out, actual.Bytes()) {
t.Errorf("%d. expected %q for dumping, got %q", i, scenario.out, actual.Bytes())
if !bytes.Equal(scenario.out, actual) {
t.Errorf("%d. expected %q for dumping, got %q", i, scenario.out, actual)
}
}
}

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,24 +34,20 @@ func emptyFilter(e TallyingIndexEstimator) TallyingIndexEstimator {
}
}
// Report the smallest observed value in the bucket.
var Minimum TallyingIndexEstimator = emptyFilter(func(minimum, maximum float64, _, observations int) float64 {
var (
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 {
maximumEstimator = 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})
averageEstimator = emptyFilter(func(minimum, maximum float64, _, observations int) float64 {
return AverageReducer([]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 {
uniformEstimator = emptyFilter(func(minimum, maximum float64, index, observations int) float64 {
if observations == 1 {
return minimum
}
@ -65,8 +60,23 @@ var Uniform TallyingIndexEstimator = emptyFilter(func(minimum, maximum float64,
return minimum
}
return maths.Average([]float64{minimum, maximum})
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,15 @@ import (
// exposed if the DefaultRegistry's exporter is hooked into the HTTP request
// handler.
var (
marshalErrorCount = metrics.NewCounter()
dumpErrorCount = metrics.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,8 +37,8 @@ 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) {
microseconds := float64(duration / time.Microsecond)
var requestLatencyAccumulator = func(began time.Time) {
microseconds := float64(time.Since(began) / 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,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