Merge pull request #9 from prometheus/refactor/user-experience/standardized-directories
Rearrange file and package per convention.
This commit is contained in:
commit
4c1c13d109
|
@ -1,8 +1,4 @@
|
||||||
language: go
|
language: go
|
||||||
|
|
||||||
before_script:
|
|
||||||
- go get -v github.com/prometheus/client_golang
|
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- go build -a -v github.com/prometheus/client_golang/...
|
- make -f Makefile.TRAVIS
|
||||||
- go test -v github.com/prometheus/client_golang/...
|
|
||||||
|
|
2
LICENSE
2
LICENSE
|
@ -1,4 +1,4 @@
|
||||||
Copyright (c) 2012, Matt T. Proud
|
Copyright (c) 2013, Prometheus Team
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
|
16
Makefile
16
Makefile
|
@ -16,17 +16,20 @@ MAKE_ARTIFACTS = search_index
|
||||||
all: test
|
all: test
|
||||||
|
|
||||||
build:
|
build:
|
||||||
go build ./...
|
$(MAKE) -C prometheus build
|
||||||
|
$(MAKE) -C examples build
|
||||||
|
|
||||||
test: build
|
test: build
|
||||||
go test ./... $(GO_TEST_FLAGS)
|
$(MAKE) -C prometheus test
|
||||||
|
$(MAKE) -C examples test
|
||||||
|
|
||||||
|
advice: test
|
||||||
|
$(MAKE) -C prometheus advice
|
||||||
|
$(MAKE) -C examples advice
|
||||||
|
|
||||||
format:
|
format:
|
||||||
find . -iname '*.go' -exec gofmt -w -s=true '{}' ';'
|
find . -iname '*.go' -exec gofmt -w -s=true '{}' ';'
|
||||||
|
|
||||||
advice:
|
|
||||||
go tool vet .
|
|
||||||
|
|
||||||
search_index:
|
search_index:
|
||||||
godoc -index -write_index -index_files='search_index'
|
godoc -index -write_index -index_files='search_index'
|
||||||
|
|
||||||
|
@ -34,6 +37,9 @@ documentation: search_index
|
||||||
godoc -http=:6060 -index -index_files='search_index'
|
godoc -http=:6060 -index -index_files='search_index'
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
|
$(MAKE) -C examples clean
|
||||||
rm -f $(MAKE_ARTIFACTS)
|
rm -f $(MAKE_ARTIFACTS)
|
||||||
|
find . -iname '*~' -exec rm -f '{}' ';'
|
||||||
|
find . -iname '*#' -exec rm -f '{}' ';'
|
||||||
|
|
||||||
.PHONY: advice build clean documentation format test
|
.PHONY: advice build clean documentation format test
|
||||||
|
|
|
@ -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
3
TODO
|
@ -1,5 +1,2 @@
|
||||||
- Validate repository for Go code fluency and idiomatic adherence.
|
- Validate repository for Go code fluency and idiomatic adherence.
|
||||||
- Decouple HTTP report handler from our project and incorporate into this
|
|
||||||
repository.
|
|
||||||
- Implement labeled metric support.
|
|
||||||
- Evaluate using atomic types versus locks.
|
- Evaluate using atomic types versus locks.
|
||||||
|
|
|
@ -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
|
|
|
@ -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.
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -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
|
|
@ -0,0 +1 @@
|
||||||
|
delegator
|
|
@ -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
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright (c) 2012, Matt T. Proud
|
// Copyright (c) 2013, Prometheus Team
|
||||||
// All rights reserved.
|
// All rights reserved.
|
||||||
//
|
//
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
|
@ -10,8 +10,8 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
"github.com/prometheus/client_golang"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
"github.com/prometheus/client_golang/exp"
|
"github.com/prometheus/client_golang/prometheus/exp"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -48,7 +48,7 @@ func main() {
|
||||||
exp.HandleFunc("/hello", helloHandler)
|
exp.HandleFunc("/hello", helloHandler)
|
||||||
exp.HandleFunc("/goodbye", goodbyeHandler)
|
exp.HandleFunc("/goodbye", goodbyeHandler)
|
||||||
exp.HandleFunc("/teapot", teapotHandler)
|
exp.HandleFunc("/teapot", teapotHandler)
|
||||||
exp.Handle(registry.ExpositionResource, registry.DefaultHandler)
|
exp.Handle(prometheus.ExpositionResource, prometheus.DefaultHandler)
|
||||||
|
|
||||||
http.ListenAndServe(*listeningAddress, exp.DefaultCoarseMux)
|
http.ListenAndServe(*listeningAddress, exp.DefaultCoarseMux)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
|
|
@ -0,0 +1 @@
|
||||||
|
random
|
|
@ -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
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright (c) 2012, Matt T. Proud
|
// Copyright (c) 2013, Prometheus Team
|
||||||
// All rights reserved.
|
// All rights reserved.
|
||||||
//
|
//
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
|
@ -13,9 +13,7 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
"github.com/prometheus/client_golang"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
"github.com/prometheus/client_golang/maths"
|
|
||||||
"github.com/prometheus/client_golang/metrics"
|
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
@ -28,27 +26,27 @@ var (
|
||||||
|
|
||||||
// Create a histogram to track fictitious interservice RPC latency for three
|
// Create a histogram to track fictitious interservice RPC latency for three
|
||||||
// distinct services.
|
// distinct services.
|
||||||
rpcLatency = metrics.NewHistogram(&metrics.HistogramSpecification{
|
rpcLatency = prometheus.NewHistogram(&prometheus.HistogramSpecification{
|
||||||
// Four distinct histogram buckets for values:
|
// Four distinct histogram buckets for values:
|
||||||
// - equally-sized,
|
// - equally-sized,
|
||||||
// - 0 to 50, 50 to 100, 100 to 150, and 150 to 200.
|
// - 0 to 50, 50 to 100, 100 to 150, and 150 to 200.
|
||||||
Starts: metrics.EquallySizedBucketsFor(0, 200, 4),
|
Starts: prometheus.EquallySizedBucketsFor(0, 200, 4),
|
||||||
// Create histogram buckets using an accumulating bucket, a bucket that
|
// Create histogram buckets using an accumulating bucket, a bucket that
|
||||||
// holds sample values subject to an eviction policy:
|
// holds sample values subject to an eviction policy:
|
||||||
// - 50 elements are allowed per bucket.
|
// - 50 elements are allowed per bucket.
|
||||||
// - Once 50 have been reached, the bucket empties 10 elements, averages the
|
// - Once 50 have been reached, the bucket empties 10 elements, averages the
|
||||||
// evicted elements, and re-appends that back to the bucket.
|
// evicted elements, and re-appends that back to the bucket.
|
||||||
BucketBuilder: metrics.AccumulatingBucketBuilder(metrics.EvictAndReplaceWith(10, maths.Average), 50),
|
BucketBuilder: prometheus.AccumulatingBucketBuilder(prometheus.EvictAndReplaceWith(10, prometheus.AverageReducer), 50),
|
||||||
// The histogram reports percentiles 1, 5, 50, 90, and 99.
|
// The histogram reports percentiles 1, 5, 50, 90, and 99.
|
||||||
ReportablePercentiles: []float64{0.01, 0.05, 0.5, 0.90, 0.99},
|
ReportablePercentiles: []float64{0.01, 0.05, 0.5, 0.90, 0.99},
|
||||||
})
|
})
|
||||||
|
|
||||||
rpcCalls = metrics.NewCounter()
|
rpcCalls = prometheus.NewCounter()
|
||||||
|
|
||||||
// If for whatever reason you are resistant to the idea of having a static
|
// If for whatever reason you are resistant to the idea of having a static
|
||||||
// registry for metrics, which is a really bad idea when using Prometheus-
|
// registry for metrics, which is a really bad idea when using Prometheus-
|
||||||
// enabled library code, you can create your own.
|
// enabled library code, you can create your own.
|
||||||
customRegistry = registry.NewRegistry()
|
customRegistry = prometheus.NewRegistry()
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
@ -69,13 +67,13 @@ func main() {
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
http.Handle(registry.ExpositionResource, customRegistry.Handler())
|
http.Handle(prometheus.ExpositionResource, customRegistry.Handler())
|
||||||
http.ListenAndServe(*listeningAddress, nil)
|
http.ListenAndServe(*listeningAddress, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
customRegistry.Register("rpc_latency_microseconds", "RPC latency.", registry.NilLabels, rpcLatency)
|
customRegistry.Register("rpc_latency_microseconds", "RPC latency.", prometheus.NilLabels, rpcLatency)
|
||||||
customRegistry.Register("rpc_calls_total", "RPC calls.", registry.NilLabels, rpcCalls)
|
customRegistry.Register("rpc_calls_total", "RPC calls.", prometheus.NilLabels, rpcCalls)
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
simple
|
|
@ -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
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright (c) 2012, Matt T. Proud
|
// Copyright (c) 2013, Prometheus Team
|
||||||
// All rights reserved.
|
// All rights reserved.
|
||||||
//
|
//
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
|
@ -10,14 +10,14 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
"github.com/prometheus/client_golang"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
http.Handle(registry.ExpositionResource, registry.DefaultHandler)
|
http.Handle(prometheus.ExpositionResource, prometheus.DefaultHandler)
|
||||||
http.ListenAndServe(*listeningAddress, nil)
|
http.ListenAndServe(*listeningAddress, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
|
|
@ -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)
|
|
||||||
}
|
|
|
@ -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"
|
|
||||||
)
|
|
|
@ -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
|
|
|
@ -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,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)
|
|
||||||
}
|
|
|
@ -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()
|
|
||||||
}
|
|
|
@ -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)
|
|
||||||
}
|
|
|
@ -0,0 +1 @@
|
||||||
|
command-line-arguments.test
|
|
@ -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
|
|
@ -1,16 +1,15 @@
|
||||||
// Copyright (c) 2012, Matt T. Proud
|
// Copyright (c) 2013, Prometheus Team
|
||||||
// All rights reserved.
|
// All rights reserved.
|
||||||
//
|
//
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
package metrics
|
package prometheus
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"container/heap"
|
"container/heap"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/prometheus/client_golang/utility"
|
|
||||||
"math"
|
"math"
|
||||||
"sort"
|
"sort"
|
||||||
"sync"
|
"sync"
|
||||||
|
@ -18,7 +17,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type AccumulatingBucket struct {
|
type AccumulatingBucket struct {
|
||||||
elements utility.PriorityQueue
|
elements priorityQueue
|
||||||
evictionPolicy EvictionPolicy
|
evictionPolicy EvictionPolicy
|
||||||
maximumSize int
|
maximumSize int
|
||||||
mutex sync.RWMutex
|
mutex sync.RWMutex
|
||||||
|
@ -31,7 +30,7 @@ type AccumulatingBucket struct {
|
||||||
func AccumulatingBucketBuilder(evictionPolicy EvictionPolicy, maximumSize int) BucketBuilder {
|
func AccumulatingBucketBuilder(evictionPolicy EvictionPolicy, maximumSize int) BucketBuilder {
|
||||||
return func() Bucket {
|
return func() Bucket {
|
||||||
return &AccumulatingBucket{
|
return &AccumulatingBucket{
|
||||||
elements: make(utility.PriorityQueue, 0, maximumSize),
|
elements: make(priorityQueue, 0, maximumSize),
|
||||||
evictionPolicy: evictionPolicy,
|
evictionPolicy: evictionPolicy,
|
||||||
maximumSize: maximumSize,
|
maximumSize: maximumSize,
|
||||||
}
|
}
|
||||||
|
@ -47,7 +46,7 @@ func (b *AccumulatingBucket) Add(value float64) {
|
||||||
b.observations++
|
b.observations++
|
||||||
size := len(b.elements)
|
size := len(b.elements)
|
||||||
|
|
||||||
v := utility.Item{
|
v := item{
|
||||||
Priority: -1 * time.Now().UnixNano(),
|
Priority: -1 * time.Now().UnixNano(),
|
||||||
Value: value,
|
Value: value,
|
||||||
}
|
}
|
|
@ -1,15 +1,13 @@
|
||||||
// Copyright (c) 2012, Matt T. Proud
|
// Copyright (c) 2013, Prometheus Team
|
||||||
// All rights reserved.
|
// All rights reserved.
|
||||||
//
|
//
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
package metrics
|
package prometheus
|
||||||
|
|
||||||
import (
|
import (
|
||||||
. "github.com/matttproud/gocheck"
|
. "github.com/matttproud/gocheck"
|
||||||
"github.com/prometheus/client_golang/maths"
|
|
||||||
"github.com/prometheus/client_golang/utility"
|
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -51,7 +49,7 @@ func (s *S) TestAccumulatingBucketBuilderWithEvictOldest(c *C) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *S) TestAccumulatingBucketBuilderWithEvictAndReplaceWithAverage(c *C) {
|
func (s *S) TestAccumulatingBucketBuilderWithEvictAndReplaceWithAverage(c *C) {
|
||||||
var evictAndReplaceWithAverage EvictionPolicy = EvictAndReplaceWith(3, maths.Average)
|
var evictAndReplaceWithAverage EvictionPolicy = EvictAndReplaceWith(3, AverageReducer)
|
||||||
|
|
||||||
c.Assert(evictAndReplaceWithAverage, Not(IsNil))
|
c.Assert(evictAndReplaceWithAverage, Not(IsNil))
|
||||||
|
|
||||||
|
@ -86,7 +84,7 @@ func (s *S) TestAccumulatingBucketBuilderWithEvictAndReplaceWithAverage(c *C) {
|
||||||
|
|
||||||
func (s *S) TestAccumulatingBucket(c *C) {
|
func (s *S) TestAccumulatingBucket(c *C) {
|
||||||
var b AccumulatingBucket = AccumulatingBucket{
|
var b AccumulatingBucket = AccumulatingBucket{
|
||||||
elements: make(utility.PriorityQueue, 0, 10),
|
elements: make(priorityQueue, 0, 10),
|
||||||
maximumSize: 5,
|
maximumSize: 5,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,13 +110,13 @@ func (s *S) TestAccumulatingBucket(c *C) {
|
||||||
|
|
||||||
func (s *S) TestAccumulatingBucketValueForIndex(c *C) {
|
func (s *S) TestAccumulatingBucketValueForIndex(c *C) {
|
||||||
var b AccumulatingBucket = AccumulatingBucket{
|
var b AccumulatingBucket = AccumulatingBucket{
|
||||||
elements: make(utility.PriorityQueue, 0, 100),
|
elements: make(priorityQueue, 0, 100),
|
||||||
maximumSize: 100,
|
maximumSize: 100,
|
||||||
evictionPolicy: EvictOldest(50),
|
evictionPolicy: EvictOldest(50),
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i <= 100; i++ {
|
for i := 0; i <= 100; i++ {
|
||||||
c.Assert(b.ValueForIndex(i), maths.IsNaN)
|
c.Assert(b.ValueForIndex(i), IsNaN)
|
||||||
}
|
}
|
||||||
|
|
||||||
// The bucket has only observed one item and contains now one item.
|
// The bucket has only observed one item and contains now one item.
|
|
@ -1,12 +1,10 @@
|
||||||
// Copyright (c) 2012, Matt T. Proud
|
// Copyright (c) 2013, Prometheus Team
|
||||||
// All rights reserved.
|
// All rights reserved.
|
||||||
//
|
//
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
// bucket.go provides fundamental interface expectations for various bucket
|
package prometheus
|
||||||
// types.
|
|
||||||
package metrics
|
|
||||||
|
|
||||||
// The Histogram class and associated types build buckets on their own.
|
// The Histogram class and associated types build buckets on their own.
|
||||||
type BucketBuilder func() Bucket
|
type BucketBuilder func() Bucket
|
|
@ -1,10 +1,10 @@
|
||||||
// Copyright (c) 2013, Matt T. Proud
|
// Copyright (c) 2013, Prometheus Team
|
||||||
// All rights reserved.
|
// All rights reserved.
|
||||||
//
|
//
|
||||||
// Use of this source code is governed by a BSD-style license that can be found
|
// Use of this source code is governed by a BSD-style license that can be found
|
||||||
// in the LICENSE file.
|
// in the LICENSE file.
|
||||||
|
|
||||||
package registry
|
package prometheus
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// NilLabels is a nil set of labels merely for end-user convenience.
|
// NilLabels is a nil set of labels merely for end-user convenience.
|
||||||
|
@ -14,8 +14,7 @@ var (
|
||||||
// interface.
|
// interface.
|
||||||
DefaultHandler = DefaultRegistry.Handler()
|
DefaultHandler = DefaultRegistry.Handler()
|
||||||
|
|
||||||
// This is the default registry with which Metric objects are associated. It
|
// This is the default registry with which Metric objects are associated.
|
||||||
// is primarily a read-only object after server instantiation.
|
|
||||||
DefaultRegistry = NewRegistry()
|
DefaultRegistry = NewRegistry()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -39,4 +38,14 @@ const (
|
||||||
docstringKey = "docstring"
|
docstringKey = "docstring"
|
||||||
metricKey = "metric"
|
metricKey = "metric"
|
||||||
nameLabel = "name"
|
nameLabel = "name"
|
||||||
|
|
||||||
|
counterTypeValue = "counter"
|
||||||
|
floatBitCount = 64
|
||||||
|
floatFormat = 'f'
|
||||||
|
floatPrecision = 6
|
||||||
|
gaugeTypeValue = "gauge"
|
||||||
|
histogramTypeValue = "histogram"
|
||||||
|
typeKey = "type"
|
||||||
|
valueKey = "value"
|
||||||
|
labelsKey = "labels"
|
||||||
)
|
)
|
|
@ -1,33 +1,32 @@
|
||||||
// Copyright (c) 2012, Matt T. Proud
|
// Copyright (c) 2013, Prometheus Team
|
||||||
// All rights reserved.
|
// All rights reserved.
|
||||||
//
|
//
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
package metrics
|
package prometheus
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/prometheus/client_golang/utility"
|
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO(matt): Refactor to de-duplicate behaviors.
|
// TODO(matt): Refactor to de-duplicate behaviors.
|
||||||
|
|
||||||
type Counter interface {
|
type Counter interface {
|
||||||
AsMarshallable() map[string]interface{}
|
Metric
|
||||||
|
|
||||||
Decrement(labels map[string]string) float64
|
Decrement(labels map[string]string) float64
|
||||||
DecrementBy(labels map[string]string, value float64) float64
|
DecrementBy(labels map[string]string, value float64) float64
|
||||||
Increment(labels map[string]string) float64
|
Increment(labels map[string]string) float64
|
||||||
IncrementBy(labels map[string]string, value float64) float64
|
IncrementBy(labels map[string]string, value float64) float64
|
||||||
ResetAll()
|
|
||||||
Set(labels map[string]string, value float64) float64
|
Set(labels map[string]string, value float64) float64
|
||||||
String() string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type counterVector struct {
|
type counterVector struct {
|
||||||
labels map[string]string
|
Labels map[string]string `json:"labels"`
|
||||||
value float64
|
Value float64 `json:"value"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewCounter() Counter {
|
func NewCounter() Counter {
|
||||||
|
@ -49,13 +48,13 @@ func (metric *counter) Set(labels map[string]string, value float64) float64 {
|
||||||
labels = map[string]string{}
|
labels = map[string]string{}
|
||||||
}
|
}
|
||||||
|
|
||||||
signature := utility.LabelsToSignature(labels)
|
signature := labelsToSignature(labels)
|
||||||
if original, ok := metric.values[signature]; ok {
|
if original, ok := metric.values[signature]; ok {
|
||||||
original.value = value
|
original.Value = value
|
||||||
} else {
|
} else {
|
||||||
metric.values[signature] = &counterVector{
|
metric.values[signature] = &counterVector{
|
||||||
labels: labels,
|
Labels: labels,
|
||||||
value: value,
|
Value: value,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,8 +66,8 @@ func (metric *counter) ResetAll() {
|
||||||
defer metric.mutex.Unlock()
|
defer metric.mutex.Unlock()
|
||||||
|
|
||||||
for key, value := range metric.values {
|
for key, value := range metric.values {
|
||||||
for label := range value.labels {
|
for label := range value.Labels {
|
||||||
delete(value.labels, label)
|
delete(value.Labels, label)
|
||||||
}
|
}
|
||||||
delete(metric.values, key)
|
delete(metric.values, key)
|
||||||
}
|
}
|
||||||
|
@ -91,13 +90,13 @@ func (metric *counter) IncrementBy(labels map[string]string, value float64) floa
|
||||||
labels = map[string]string{}
|
labels = map[string]string{}
|
||||||
}
|
}
|
||||||
|
|
||||||
signature := utility.LabelsToSignature(labels)
|
signature := labelsToSignature(labels)
|
||||||
if original, ok := metric.values[signature]; ok {
|
if original, ok := metric.values[signature]; ok {
|
||||||
original.value += value
|
original.Value += value
|
||||||
} else {
|
} else {
|
||||||
metric.values[signature] = &counterVector{
|
metric.values[signature] = &counterVector{
|
||||||
labels: labels,
|
Labels: labels,
|
||||||
value: value,
|
Value: value,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -116,13 +115,13 @@ func (metric *counter) DecrementBy(labels map[string]string, value float64) floa
|
||||||
labels = map[string]string{}
|
labels = map[string]string{}
|
||||||
}
|
}
|
||||||
|
|
||||||
signature := utility.LabelsToSignature(labels)
|
signature := labelsToSignature(labels)
|
||||||
if original, ok := metric.values[signature]; ok {
|
if original, ok := metric.values[signature]; ok {
|
||||||
original.value -= value
|
original.Value -= value
|
||||||
} else {
|
} else {
|
||||||
metric.values[signature] = &counterVector{
|
metric.values[signature] = &counterVector{
|
||||||
labels: labels,
|
Labels: labels,
|
||||||
value: -1 * value,
|
Value: -1 * value,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -133,20 +132,18 @@ func (metric *counter) Decrement(labels map[string]string) float64 {
|
||||||
return metric.DecrementBy(labels, 1)
|
return metric.DecrementBy(labels, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (metric counter) AsMarshallable() map[string]interface{} {
|
func (metric counter) MarshalJSON() ([]byte, error) {
|
||||||
metric.mutex.RLock()
|
metric.mutex.RLock()
|
||||||
defer metric.mutex.RUnlock()
|
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 {
|
for _, value := range metric.values {
|
||||||
values = append(values, map[string]interface{}{
|
values = append(values, value)
|
||||||
labelsKey: value.labels,
|
|
||||||
valueKey: value.value,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return map[string]interface{}{
|
return json.Marshal(map[string]interface{}{
|
||||||
valueKey: values,
|
valueKey: values,
|
||||||
typeKey: counterTypeValue,
|
typeKey: counterTypeValue,
|
||||||
}
|
})
|
||||||
}
|
}
|
|
@ -1,18 +1,17 @@
|
||||||
// Copyright (c) 2012, Matt T. Proud
|
// Copyright (c) 2013, Prometheus Team
|
||||||
// All rights reserved.
|
// All rights reserved.
|
||||||
//
|
//
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
package metrics
|
package prometheus
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"github.com/prometheus/client_golang/utility/test"
|
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func testCounter(t test.Tester) {
|
func testCounter(t tester) {
|
||||||
type input struct {
|
type input struct {
|
||||||
steps []func(g Counter)
|
steps []func(g Counter)
|
||||||
}
|
}
|
||||||
|
@ -29,7 +28,7 @@ func testCounter(t test.Tester) {
|
||||||
steps: []func(g Counter){},
|
steps: []func(g Counter){},
|
||||||
},
|
},
|
||||||
out: output{
|
out: output{
|
||||||
value: "{\"type\":\"counter\",\"value\":[]}",
|
value: `{"type":"counter","value":[]}`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -41,7 +40,7 @@ func testCounter(t test.Tester) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
out: output{
|
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{
|
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{
|
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{
|
out: output{
|
||||||
value: "{\"type\":\"counter\",\"value\":[]}",
|
value: `{"type":"counter","value":[]}`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -98,7 +97,7 @@ func testCounter(t test.Tester) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
out: output{
|
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{
|
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{
|
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{
|
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{
|
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{
|
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{
|
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)
|
step(counter)
|
||||||
}
|
}
|
||||||
|
|
||||||
marshallable := counter.AsMarshallable()
|
bytes, err := json.Marshal(counter)
|
||||||
|
|
||||||
bytes, err := json.Marshal(marshallable)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("%d. could not marshal into JSON %s", i, err)
|
t.Errorf("%d. could not marshal into JSON %s", i, err)
|
||||||
continue
|
continue
|
|
@ -1,17 +1,17 @@
|
||||||
// Copyright (c) 2012, Matt T. Proud
|
// Copyright (c) 2013, Prometheus Team
|
||||||
// All rights reserved.
|
// All rights reserved.
|
||||||
//
|
//
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
package maths
|
package prometheus
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"math"
|
"math"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Go's standard library does not offer a factorial function.
|
// Go's standard library does not offer a factorial function.
|
||||||
func Factorial(of int) int64 {
|
func factorial(of int) int64 {
|
||||||
if of <= 0 {
|
if of <= 0 {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
@ -28,8 +28,8 @@ func Factorial(of int) int64 {
|
||||||
// Calculate the value of a probability density for a given binomial statistic,
|
// Calculate the value of a probability density for a given binomial statistic,
|
||||||
// where k is the target count of true cases, n is the number of subjects, and
|
// where k is the target count of true cases, n is the number of subjects, and
|
||||||
// p is the probability.
|
// p is the probability.
|
||||||
func BinomialPDF(k, n int, p float64) float64 {
|
func binomialPDF(k, n int, p float64) float64 {
|
||||||
binomialCoefficient := float64(Factorial(n)) / float64(Factorial(k)*Factorial(n-k))
|
binomialCoefficient := float64(factorial(n)) / float64(factorial(k)*factorial(n-k))
|
||||||
intermediate := math.Pow(p, float64(k)) * math.Pow(1-p, float64(n-k))
|
intermediate := math.Pow(p, float64(k)) * math.Pow(1-p, float64(n-k))
|
||||||
|
|
||||||
return binomialCoefficient * intermediate
|
return binomialCoefficient * intermediate
|
|
@ -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
|
|
@ -1,15 +1,13 @@
|
||||||
// Copyright (c) 2012, Matt T. Proud
|
// Copyright (c) 2013, Prometheus Team
|
||||||
// All rights reserved.
|
// All rights reserved.
|
||||||
//
|
//
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
package metrics
|
package prometheus
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"container/heap"
|
"container/heap"
|
||||||
"github.com/prometheus/client_golang/maths"
|
|
||||||
"github.com/prometheus/client_golang/utility"
|
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -29,17 +27,17 @@ func EvictOldest(count int) EvictionPolicy {
|
||||||
|
|
||||||
// This factory produces an EvictionPolicy that applies some standardized
|
// This factory produces an EvictionPolicy that applies some standardized
|
||||||
// reduction methodology on the to-be-terminated values.
|
// reduction methodology on the to-be-terminated values.
|
||||||
func EvictAndReplaceWith(count int, reducer maths.ReductionMethod) EvictionPolicy {
|
func EvictAndReplaceWith(count int, reducer ReductionMethod) EvictionPolicy {
|
||||||
return func(h heap.Interface) {
|
return func(h heap.Interface) {
|
||||||
oldValues := make([]float64, count)
|
oldValues := make([]float64, count)
|
||||||
|
|
||||||
for i := 0; i < count; i++ {
|
for i := 0; i < count; i++ {
|
||||||
oldValues[i] = heap.Pop(h).(*utility.Item).Value.(float64)
|
oldValues[i] = heap.Pop(h).(*item).Value.(float64)
|
||||||
}
|
}
|
||||||
|
|
||||||
reduced := reducer(oldValues)
|
reduced := reducer(oldValues)
|
||||||
|
|
||||||
heap.Push(h, &utility.Item{
|
heap.Push(h, &item{
|
||||||
Value: reduced,
|
Value: reduced,
|
||||||
// TODO(mtp): Parameterize the priority generation since these tools are
|
// TODO(mtp): Parameterize the priority generation since these tools are
|
||||||
// useful.
|
// useful.
|
|
@ -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)
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright (c) 2013, Matt T. Proud
|
// Copyright (c) 2013, Prometheus Team
|
||||||
// All rights reserved.
|
// All rights reserved.
|
||||||
//
|
//
|
||||||
// Use of this source code is governed by a BSD-style license that can be found in
|
// Use of this source code is governed by a BSD-style license that can be found in
|
||||||
|
@ -8,8 +8,7 @@ package exp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/prometheus/client_golang"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
"github.com/prometheus/client_golang/metrics"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
@ -33,11 +32,11 @@ type (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
requestCounts = metrics.NewCounter()
|
requestCounts = prometheus.NewCounter()
|
||||||
requestDuration = metrics.NewCounter()
|
requestDuration = prometheus.NewCounter()
|
||||||
requestDurations = metrics.NewDefaultHistogram()
|
requestDurations = prometheus.NewDefaultHistogram()
|
||||||
requestBytes = metrics.NewCounter()
|
requestBytes = prometheus.NewCounter()
|
||||||
responseBytes = metrics.NewCounter()
|
responseBytes = prometheus.NewCounter()
|
||||||
|
|
||||||
// DefaultCoarseMux is a drop-in replacement for http.DefaultServeMux that
|
// DefaultCoarseMux is a drop-in replacement for http.DefaultServeMux that
|
||||||
// provides standardized telemetry for Go's standard HTTP handler registration
|
// provides standardized telemetry for Go's standard HTTP handler registration
|
||||||
|
@ -103,9 +102,9 @@ func HandleFunc(pattern string, handler http.HandlerFunc) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
registry.Register("http_requests_total", "A counter of the total number of HTTP requests made against the default multiplexor.", registry.NilLabels, requestCounts)
|
prometheus.Register("http_requests_total", "A counter of the total number of HTTP requests made against the default multiplexor.", prometheus.NilLabels, requestCounts)
|
||||||
registry.Register("http_request_durations_total_microseconds", "The total amount of time the default multiplexor has spent answering HTTP requests (microseconds).", registry.NilLabels, requestDuration)
|
prometheus.Register("http_request_durations_total_microseconds", "The total amount of time the default multiplexor has spent answering HTTP requests (microseconds).", prometheus.NilLabels, requestDuration)
|
||||||
registry.Register("http_request_durations_microseconds", "The amounts of time the default multiplexor has spent answering HTTP requests (microseconds).", registry.NilLabels, requestDurations)
|
prometheus.Register("http_request_durations_microseconds", "The amounts of time the default multiplexor has spent answering HTTP requests (microseconds).", prometheus.NilLabels, requestDurations)
|
||||||
registry.Register("http_request_bytes_total", "The total volume of content body sizes received (bytes).", registry.NilLabels, requestBytes)
|
prometheus.Register("http_request_bytes_total", "The total volume of content body sizes received (bytes).", prometheus.NilLabels, requestBytes)
|
||||||
registry.Register("http_response_bytes_total", "The total volume of response payloads emitted (bytes).", registry.NilLabels, responseBytes)
|
prometheus.Register("http_response_bytes_total", "The total volume of response payloads emitted (bytes).", prometheus.NilLabels, responseBytes)
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright (c) 2013, Matt T. Proud
|
// Copyright (c) 2013, Prometheus Team
|
||||||
// All rights reserved.
|
// All rights reserved.
|
||||||
//
|
//
|
||||||
// Use of this source code is governed by a BSD-style license that can be found in
|
// Use of this source code is governed by a BSD-style license that can be found in
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright (c) 2013, Matt T. Proud
|
// Copyright (c) 2013, Prometheus Team
|
||||||
// All rights reserved.
|
// All rights reserved.
|
||||||
//
|
//
|
||||||
// Use of this source code is governed by a BSD-style license that can be found in
|
// Use of this source code is governed by a BSD-style license that can be found in
|
|
@ -1,14 +1,14 @@
|
||||||
// Copyright (c) 2012, Matt T. Proud
|
// Copyright (c) 2013, Prometheus Team
|
||||||
// All rights reserved.
|
// All rights reserved.
|
||||||
//
|
//
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
package metrics
|
package prometheus
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/prometheus/client_golang/utility"
|
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -17,15 +17,13 @@ import (
|
||||||
// temperature or the hitherto bandwidth used, this would be the metric for such
|
// temperature or the hitherto bandwidth used, this would be the metric for such
|
||||||
// circumstances.
|
// circumstances.
|
||||||
type Gauge interface {
|
type Gauge interface {
|
||||||
AsMarshallable() map[string]interface{}
|
Metric
|
||||||
ResetAll()
|
|
||||||
Set(labels map[string]string, value float64) float64
|
Set(labels map[string]string, value float64) float64
|
||||||
String() string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type gaugeVector struct {
|
type gaugeVector struct {
|
||||||
labels map[string]string
|
Labels map[string]string `json:"labels"`
|
||||||
value float64
|
Value float64 `json:"value"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewGauge() Gauge {
|
func NewGauge() Gauge {
|
||||||
|
@ -56,14 +54,14 @@ func (metric *gauge) Set(labels map[string]string, value float64) float64 {
|
||||||
labels = map[string]string{}
|
labels = map[string]string{}
|
||||||
}
|
}
|
||||||
|
|
||||||
signature := utility.LabelsToSignature(labels)
|
signature := labelsToSignature(labels)
|
||||||
|
|
||||||
if original, ok := metric.values[signature]; ok {
|
if original, ok := metric.values[signature]; ok {
|
||||||
original.value = value
|
original.Value = value
|
||||||
} else {
|
} else {
|
||||||
metric.values[signature] = &gaugeVector{
|
metric.values[signature] = &gaugeVector{
|
||||||
labels: labels,
|
Labels: labels,
|
||||||
value: value,
|
Value: value,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,27 +73,24 @@ func (metric *gauge) ResetAll() {
|
||||||
defer metric.mutex.Unlock()
|
defer metric.mutex.Unlock()
|
||||||
|
|
||||||
for key, value := range metric.values {
|
for key, value := range metric.values {
|
||||||
for label := range value.labels {
|
for label := range value.Labels {
|
||||||
delete(value.labels, label)
|
delete(value.Labels, label)
|
||||||
}
|
}
|
||||||
delete(metric.values, key)
|
delete(metric.values, key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (metric gauge) AsMarshallable() map[string]interface{} {
|
func (metric gauge) MarshalJSON() ([]byte, error) {
|
||||||
metric.mutex.RLock()
|
metric.mutex.RLock()
|
||||||
defer metric.mutex.RUnlock()
|
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 {
|
for _, value := range metric.values {
|
||||||
values = append(values, map[string]interface{}{
|
values = append(values, value)
|
||||||
labelsKey: value.labels,
|
|
||||||
valueKey: value.value,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return map[string]interface{}{
|
return json.Marshal(map[string]interface{}{
|
||||||
typeKey: gaugeTypeValue,
|
typeKey: gaugeTypeValue,
|
||||||
valueKey: values,
|
valueKey: values,
|
||||||
}
|
})
|
||||||
}
|
}
|
|
@ -1,18 +1,17 @@
|
||||||
// Copyright (c) 2012, Matt T. Proud
|
// Copyright (c) 2013, Prometheus Team
|
||||||
// All rights reserved.
|
// All rights reserved.
|
||||||
//
|
//
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
package metrics
|
package prometheus
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"github.com/prometheus/client_golang/utility/test"
|
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func testGauge(t test.Tester) {
|
func testGauge(t tester) {
|
||||||
type input struct {
|
type input struct {
|
||||||
steps []func(g Gauge)
|
steps []func(g Gauge)
|
||||||
}
|
}
|
||||||
|
@ -29,7 +28,7 @@ func testGauge(t test.Tester) {
|
||||||
steps: []func(g Gauge){},
|
steps: []func(g Gauge){},
|
||||||
},
|
},
|
||||||
out: output{
|
out: output{
|
||||||
value: "{\"type\":\"gauge\",\"value\":[]}",
|
value: `{"type":"gauge","value":[]}`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -41,7 +40,7 @@ func testGauge(t test.Tester) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
out: output{
|
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{
|
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{
|
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{
|
out: output{
|
||||||
value: "{\"type\":\"gauge\",\"value\":[]}",
|
value: `{"type":"gauge","value":[]}`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -98,7 +97,7 @@ func testGauge(t test.Tester) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
out: output{
|
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)
|
step(gauge)
|
||||||
}
|
}
|
||||||
|
|
||||||
marshallable := gauge.AsMarshallable()
|
bytes, err := json.Marshal(gauge)
|
||||||
|
|
||||||
bytes, err := json.Marshal(marshallable)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("%d. could not marshal into JSON %s", i, err)
|
t.Errorf("%d. could not marshal into JSON %s", i, err)
|
||||||
continue
|
continue
|
|
@ -1,10 +1,10 @@
|
||||||
// Copyright (c) 2012, Matt T. Proud
|
// Copyright (c) 2013, Prometheus Team
|
||||||
// All rights reserved.
|
// All rights reserved.
|
||||||
//
|
//
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
package maths
|
package prometheus
|
||||||
|
|
||||||
import (
|
import (
|
||||||
. "github.com/matttproud/gocheck"
|
. "github.com/matttproud/gocheck"
|
|
@ -1,16 +1,15 @@
|
||||||
// Copyright (c) 2012, Matt T. Proud
|
// Copyright (c) 2013, Prometheus Team
|
||||||
// All rights reserved.
|
// All rights reserved.
|
||||||
//
|
//
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
package metrics
|
package prometheus
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/prometheus/client_golang/maths"
|
|
||||||
"github.com/prometheus/client_golang/utility"
|
|
||||||
"math"
|
"math"
|
||||||
"strconv"
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
|
@ -54,10 +53,8 @@ type HistogramSpecification struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type Histogram interface {
|
type Histogram interface {
|
||||||
|
Metric
|
||||||
Add(labels map[string]string, value float64)
|
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
|
// 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{}
|
labels = map[string]string{}
|
||||||
}
|
}
|
||||||
|
|
||||||
signature := utility.LabelsToSignature(labels)
|
signature := labelsToSignature(labels)
|
||||||
var histogram *histogramVector = nil
|
var histogram *histogramVector = nil
|
||||||
if original, ok := h.values[signature]; ok {
|
if original, ok := h.values[signature]; ok {
|
||||||
histogram = original
|
histogram = original
|
||||||
|
@ -242,29 +239,30 @@ func formatFloat(value float64) string {
|
||||||
return strconv.FormatFloat(value, floatFormat, floatPrecision, floatBitCount)
|
return strconv.FormatFloat(value, floatFormat, floatPrecision, floatBitCount)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h histogram) AsMarshallable() map[string]interface{} {
|
func (h histogram) MarshalJSON() ([]byte, error) {
|
||||||
h.mutex.RLock()
|
h.mutex.RLock()
|
||||||
defer h.mutex.RUnlock()
|
defer h.mutex.RUnlock()
|
||||||
|
|
||||||
result := make(map[string]interface{}, 2)
|
|
||||||
result[typeKey] = histogramTypeValue
|
|
||||||
values := make([]map[string]interface{}, 0, len(h.values))
|
values := make([]map[string]interface{}, 0, len(h.values))
|
||||||
|
|
||||||
for signature, value := range h.values {
|
for signature, value := range h.values {
|
||||||
metricContainer := map[string]interface{}{}
|
percentiles := make(map[string]float64, len(h.reportablePercentiles))
|
||||||
metricContainer[labelsKey] = value.labels
|
|
||||||
intermediate := map[string]interface{}{}
|
|
||||||
for _, percentile := range h.reportablePercentiles {
|
for _, percentile := range h.reportablePercentiles {
|
||||||
formatted := formatFloat(percentile)
|
formatted := formatFloat(percentile)
|
||||||
intermediate[formatted] = h.percentile(signature, percentile)
|
percentiles[formatted] = h.percentile(signature, percentile)
|
||||||
}
|
}
|
||||||
metricContainer[valueKey] = intermediate
|
|
||||||
values = append(values, metricContainer)
|
values = append(values, map[string]interface{}{
|
||||||
|
labelsKey: value.labels,
|
||||||
|
valueKey: percentiles,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
result[valueKey] = values
|
return json.Marshal(map[string]interface{}{
|
||||||
|
typeKey: histogramTypeValue,
|
||||||
return result
|
valueKey: values,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *histogram) ResetAll() {
|
func (h *histogram) ResetAll() {
|
||||||
|
@ -298,7 +296,7 @@ func NewDefaultHistogram() Histogram {
|
||||||
return NewHistogram(
|
return NewHistogram(
|
||||||
&HistogramSpecification{
|
&HistogramSpecification{
|
||||||
Starts: LogarithmicSizedBucketsFor(0, 4096),
|
Starts: LogarithmicSizedBucketsFor(0, 4096),
|
||||||
BucketBuilder: AccumulatingBucketBuilder(EvictAndReplaceWith(10, maths.Average), 50),
|
BucketBuilder: AccumulatingBucketBuilder(EvictAndReplaceWith(10, AverageReducer), 50),
|
||||||
ReportablePercentiles: []float64{0.01, 0.05, 0.5, 0.90, 0.99},
|
ReportablePercentiles: []float64{0.01, 0.05, 0.5, 0.90, 0.99},
|
||||||
},
|
},
|
||||||
)
|
)
|
|
@ -1,9 +1,9 @@
|
||||||
// Copyright (c) 2013, Matt T. Proud
|
// Copyright (c) 2013, Prometheus Team
|
||||||
// All rights reserved.
|
// All rights reserved.
|
||||||
//
|
//
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
package metrics
|
package prometheus
|
||||||
|
|
||||||
// TODO(matt): Re-Add tests for this type.
|
// TODO(matt): Re-Add tests for this type.
|
|
@ -1,12 +1,12 @@
|
||||||
// Copyright (c) 2013, Matt T. Proud
|
// Copyright (c) 2013, Prometheus Team
|
||||||
// All rights reserved.
|
// All rights reserved.
|
||||||
//
|
//
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
package test
|
package prometheus
|
||||||
|
|
||||||
type Tester interface {
|
type tester interface {
|
||||||
Error(args ...interface{})
|
Error(args ...interface{})
|
||||||
Errorf(format string, args ...interface{})
|
Errorf(format string, args ...interface{})
|
||||||
Fatal(args ...interface{})
|
Fatal(args ...interface{})
|
|
@ -1,15 +1,17 @@
|
||||||
// Copyright (c) 2012, Matt T. Proud
|
// Copyright (c) 2013, Prometheus Team
|
||||||
// All rights reserved.
|
// All rights reserved.
|
||||||
//
|
//
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
package metrics
|
package prometheus
|
||||||
|
|
||||||
|
import "encoding/json"
|
||||||
|
|
||||||
// A Metric is something that can be exposed via the registry framework.
|
// A Metric is something that can be exposed via the registry framework.
|
||||||
type Metric interface {
|
type Metric interface {
|
||||||
// Produce a JSON-consumable representation of the metric.
|
// Produce a JSON representation of the metric.
|
||||||
AsMarshallable() map[string]interface{}
|
json.Marshaler
|
||||||
// Reset the parent metrics and delete all child metrics.
|
// Reset the parent metrics and delete all child metrics.
|
||||||
ResetAll()
|
ResetAll()
|
||||||
// Produce a human-consumable representation of the metric.
|
// Produce a human-consumable representation of the metric.
|
|
@ -1,44 +1,44 @@
|
||||||
// Copyright (c) 2012, Matt T. Proud
|
// Copyright (c) 2013, Prometheus Team
|
||||||
// All rights reserved.
|
// All rights reserved.
|
||||||
|
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
package utility
|
package prometheus
|
||||||
|
|
||||||
type Item struct {
|
type item struct {
|
||||||
Priority int64
|
Priority int64
|
||||||
Value interface{}
|
Value interface{}
|
||||||
index int
|
index int
|
||||||
}
|
}
|
||||||
|
|
||||||
type PriorityQueue []*Item
|
type priorityQueue []*item
|
||||||
|
|
||||||
func (q PriorityQueue) Len() int {
|
func (q priorityQueue) Len() int {
|
||||||
return len(q)
|
return len(q)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q PriorityQueue) Less(i, j int) bool {
|
func (q priorityQueue) Less(i, j int) bool {
|
||||||
return q[i].Priority > q[j].Priority
|
return q[i].Priority > q[j].Priority
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q PriorityQueue) Swap(i, j int) {
|
func (q priorityQueue) Swap(i, j int) {
|
||||||
q[i], q[j] = q[j], q[i]
|
q[i], q[j] = q[j], q[i]
|
||||||
q[i].index = i
|
q[i].index = i
|
||||||
q[j].index = j
|
q[j].index = j
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *PriorityQueue) Push(x interface{}) {
|
func (q *priorityQueue) Push(x interface{}) {
|
||||||
queue := *q
|
queue := *q
|
||||||
size := len(queue)
|
size := len(queue)
|
||||||
queue = queue[0 : size+1]
|
queue = queue[0 : size+1]
|
||||||
item := x.(*Item)
|
item := x.(*item)
|
||||||
item.index = size
|
item.index = size
|
||||||
queue[size] = item
|
queue[size] = item
|
||||||
*q = queue
|
*q = queue
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *PriorityQueue) Pop() interface{} {
|
func (q *priorityQueue) Pop() interface{} {
|
||||||
queue := *q
|
queue := *q
|
||||||
size := len(queue)
|
size := len(queue)
|
||||||
item := queue[size-1]
|
item := queue[size-1]
|
|
@ -1,10 +1,10 @@
|
||||||
// Copyright (c) 2012, Matt T. Proud
|
// Copyright (c) 2013, Prometheus Team
|
||||||
// All rights reserved.
|
// All rights reserved.
|
||||||
//
|
//
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
package utility
|
package prometheus
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"container/heap"
|
"container/heap"
|
||||||
|
@ -12,16 +12,16 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *S) TestPriorityQueueSort(c *C) {
|
func (s *S) TestPriorityQueueSort(c *C) {
|
||||||
q := make(PriorityQueue, 0, 6)
|
q := make(priorityQueue, 0, 6)
|
||||||
|
|
||||||
c.Check(len(q), Equals, 0)
|
c.Check(len(q), Equals, 0)
|
||||||
|
|
||||||
heap.Push(&q, &Item{Value: "newest", Priority: -100})
|
heap.Push(&q, &item{Value: "newest", Priority: -100})
|
||||||
heap.Push(&q, &Item{Value: "older", Priority: 90})
|
heap.Push(&q, &item{Value: "older", Priority: 90})
|
||||||
heap.Push(&q, &Item{Value: "oldest", Priority: 100})
|
heap.Push(&q, &item{Value: "oldest", Priority: 100})
|
||||||
heap.Push(&q, &Item{Value: "newer", Priority: -90})
|
heap.Push(&q, &item{Value: "newer", Priority: -90})
|
||||||
heap.Push(&q, &Item{Value: "new", Priority: -80})
|
heap.Push(&q, &item{Value: "new", Priority: -80})
|
||||||
heap.Push(&q, &Item{Value: "old", Priority: 80})
|
heap.Push(&q, &item{Value: "old", Priority: 80})
|
||||||
|
|
||||||
c.Check(len(q), Equals, 6)
|
c.Check(len(q), Equals, 6)
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
// Copyright (c) 2012, Matt T. Proud
|
// Copyright (c) 2013, Prometheus Team
|
||||||
// All rights reserved.
|
// All rights reserved.
|
||||||
//
|
//
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
package utility
|
package prometheus
|
||||||
|
|
||||||
import (
|
import (
|
||||||
. "github.com/matttproud/gocheck"
|
. "github.com/matttproud/gocheck"
|
||||||
|
@ -15,6 +15,6 @@ type S struct{}
|
||||||
|
|
||||||
var _ = Suite(&S{})
|
var _ = Suite(&S{})
|
||||||
|
|
||||||
func TestUtility(t *testing.T) {
|
func TestPrometheus(t *testing.T) {
|
||||||
TestingT(t)
|
TestingT(t)
|
||||||
}
|
}
|
|
@ -1,10 +1,10 @@
|
||||||
// Copyright (c) 2012, Matt T. Proud
|
// Copyright (c) 2013, Prometheus Team
|
||||||
// All rights reserved.
|
// All rights reserved.
|
||||||
//
|
//
|
||||||
// Use of this source code is governed by a BSD-style license that can be found
|
// Use of this source code is governed by a BSD-style license that can be found
|
||||||
// in the LICENSE file.
|
// in the LICENSE file.
|
||||||
|
|
||||||
package registry
|
package prometheus
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"compress/gzip"
|
"compress/gzip"
|
||||||
|
@ -12,14 +12,13 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/prometheus/client_golang/metrics"
|
|
||||||
"github.com/prometheus/client_golang/utility"
|
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -44,9 +43,9 @@ var (
|
||||||
// container represents a top-level registered metric that encompasses its
|
// container represents a top-level registered metric that encompasses its
|
||||||
// static metadata.
|
// static metadata.
|
||||||
type container struct {
|
type container struct {
|
||||||
baseLabels map[string]string
|
BaseLabels map[string]string `json:"baseLabels"`
|
||||||
docstring string
|
Docstring string `json:"docstring"`
|
||||||
metric metrics.Metric
|
Metric Metric `json:"metric"`
|
||||||
name string
|
name string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,7 +60,7 @@ type registry struct {
|
||||||
// own.
|
// own.
|
||||||
type Registry interface {
|
type Registry interface {
|
||||||
// Register a metric with a given name. Name should be globally unique.
|
// Register a metric with a given name. Name should be globally unique.
|
||||||
Register(name, docstring string, baseLabels map[string]string, metric metrics.Metric) error
|
Register(name, docstring string, baseLabels map[string]string, metric Metric) error
|
||||||
// Create a http.HandlerFunc that is tied to a Registry such that requests
|
// Create a http.HandlerFunc that is tied to a Registry such that requests
|
||||||
// against it generate a representation of the housed metrics.
|
// against it generate a representation of the housed metrics.
|
||||||
Handler() http.HandlerFunc
|
Handler() http.HandlerFunc
|
||||||
|
@ -79,10 +78,28 @@ func NewRegistry() Registry {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Associate a Metric with the DefaultRegistry.
|
// Associate a Metric with the DefaultRegistry.
|
||||||
func Register(name, docstring string, baseLabels map[string]string, metric metrics.Metric) error {
|
func Register(name, docstring string, baseLabels map[string]string, metric Metric) error {
|
||||||
return DefaultRegistry.Register(name, docstring, baseLabels, metric)
|
return DefaultRegistry.Register(name, docstring, baseLabels, metric)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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
|
// isValidCandidate returns true if the candidate is acceptable for use. In the
|
||||||
// event of any apparent incorrect use it will report the problem, invalidate
|
// event of any apparent incorrect use it will report the problem, invalidate
|
||||||
// the candidate, or outright abort.
|
// the candidate, or outright abort.
|
||||||
|
@ -110,7 +127,7 @@ func (r registry) isValidCandidate(name string, baseLabels map[string]string) (s
|
||||||
}
|
}
|
||||||
|
|
||||||
baseLabels[nameLabel] = name
|
baseLabels[nameLabel] = name
|
||||||
signature = utility.LabelsToSignature(baseLabels)
|
signature = labelsToSignature(baseLabels)
|
||||||
|
|
||||||
if _, contains := r.signatureContainers[signature]; contains {
|
if _, contains := r.signatureContainers[signature]; contains {
|
||||||
err = fmt.Errorf("metric named %s with baseLabels %s is already registered", name, baseLabels)
|
err = fmt.Errorf("metric named %s with baseLabels %s is already registered", name, baseLabels)
|
||||||
|
@ -126,7 +143,7 @@ func (r registry) isValidCandidate(name string, baseLabels map[string]string) (s
|
||||||
if useAggressiveSanityChecks {
|
if useAggressiveSanityChecks {
|
||||||
for _, container := range r.signatureContainers {
|
for _, container := range r.signatureContainers {
|
||||||
if container.name == name {
|
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 {
|
if abortOnMisuse {
|
||||||
panic(err)
|
panic(err)
|
||||||
} else if debugRegistration {
|
} else if debugRegistration {
|
||||||
|
@ -141,7 +158,7 @@ func (r registry) isValidCandidate(name string, baseLabels map[string]string) (s
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r registry) Register(name, docstring string, baseLabels map[string]string, metric metrics.Metric) (err error) {
|
func (r registry) Register(name, docstring string, baseLabels map[string]string, metric Metric) (err error) {
|
||||||
r.mutex.Lock()
|
r.mutex.Lock()
|
||||||
defer r.mutex.Unlock()
|
defer r.mutex.Unlock()
|
||||||
|
|
||||||
|
@ -155,9 +172,9 @@ func (r registry) Register(name, docstring string, baseLabels map[string]string,
|
||||||
}
|
}
|
||||||
|
|
||||||
r.signatureContainers[signature] = container{
|
r.signatureContainers[signature] = container{
|
||||||
baseLabels: baseLabels,
|
BaseLabels: baseLabels,
|
||||||
docstring: docstring,
|
Docstring: docstring,
|
||||||
metric: metric,
|
Metric: metric,
|
||||||
name: name,
|
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
|
// decorateWriter annotates the response writer to handle any other behaviors
|
||||||
// that might be beneficial to the client---e.g., GZIP encoding.
|
// that might be beneficial to the client---e.g., GZIP encoding.
|
||||||
func decorateWriter(request *http.Request, writer http.ResponseWriter) io.Writer {
|
func decorateWriter(request *http.Request, writer http.ResponseWriter) io.Writer {
|
||||||
|
@ -271,29 +233,26 @@ func (registry registry) YieldExporter() http.HandlerFunc {
|
||||||
|
|
||||||
func (registry registry) Handler() http.HandlerFunc {
|
func (registry registry) Handler() http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
var instrumentable metrics.InstrumentableCall = func() {
|
defer requestLatencyAccumulator(time.Now())
|
||||||
requestCount.Increment(nil)
|
|
||||||
url := r.URL
|
|
||||||
|
|
||||||
if strings.HasSuffix(url.Path, jsonSuffix) {
|
requestCount.Increment(nil)
|
||||||
header := w.Header()
|
url := r.URL
|
||||||
header.Set(ProtocolVersionHeader, APIVersion)
|
|
||||||
header.Set(contentTypeHeader, jsonContentType)
|
|
||||||
|
|
||||||
writer := decorateWriter(r, w)
|
if strings.HasSuffix(url.Path, jsonSuffix) {
|
||||||
|
header := w.Header()
|
||||||
|
header.Set(ProtocolVersionHeader, APIVersion)
|
||||||
|
header.Set(contentTypeHeader, jsonContentType)
|
||||||
|
|
||||||
if closer, ok := writer.(io.Closer); ok {
|
writer := decorateWriter(r, w)
|
||||||
defer closer.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
registry.dumpToWriter(writer)
|
if closer, ok := writer.(io.Closer); ok {
|
||||||
|
defer closer.Close()
|
||||||
} else {
|
|
||||||
w.WriteHeader(http.StatusNotFound)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
json.NewEncoder(writer).Encode(registry)
|
||||||
|
} else {
|
||||||
|
w.WriteHeader(http.StatusNotFound)
|
||||||
}
|
}
|
||||||
metrics.InstrumentCall(instrumentable, requestLatencyAccumulator)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,22 +1,21 @@
|
||||||
// Copyright (c) 2013, Matt T. Proud
|
// Copyright (c) 2013, Prometheus Team
|
||||||
// All rights reserved.
|
// All rights reserved.
|
||||||
//
|
//
|
||||||
// Use of this source code is governed by a BSD-style license that can be found
|
// Use of this source code is governed by a BSD-style license that can be found
|
||||||
// in the LICENSE file.
|
// in the LICENSE file.
|
||||||
|
|
||||||
package registry
|
package prometheus
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/prometheus/client_golang/metrics"
|
|
||||||
"github.com/prometheus/client_golang/utility/test"
|
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func testRegister(t test.Tester) {
|
func testRegister(t tester) {
|
||||||
var oldState = struct {
|
var oldState = struct {
|
||||||
abortOnMisuse bool
|
abortOnMisuse bool
|
||||||
debugRegistration bool
|
debugRegistration bool
|
||||||
|
@ -181,7 +180,7 @@ func (r *fakeResponseWriter) Write(d []byte) (l int, err error) {
|
||||||
func (r *fakeResponseWriter) WriteHeader(c int) {
|
func (r *fakeResponseWriter) WriteHeader(c int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func testDecorateWriter(t test.Tester) {
|
func testDecorateWriter(t tester) {
|
||||||
type input struct {
|
type input struct {
|
||||||
headers map[string]string
|
headers map[string]string
|
||||||
body []byte
|
body []byte
|
||||||
|
@ -266,9 +265,9 @@ func BenchmarkDecorateWriter(b *testing.B) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func testDumpToWriter(t test.Tester) {
|
func testDumpToWriter(t tester) {
|
||||||
type input struct {
|
type input struct {
|
||||||
metrics map[string]metrics.Metric
|
metrics map[string]Metric
|
||||||
}
|
}
|
||||||
|
|
||||||
var scenarios = []struct {
|
var scenarios = []struct {
|
||||||
|
@ -280,20 +279,20 @@ func testDumpToWriter(t test.Tester) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
in: input{
|
in: input{
|
||||||
metrics: map[string]metrics.Metric{
|
metrics: map[string]Metric{
|
||||||
"foo": metrics.NewCounter(),
|
"foo": NewCounter(),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
out: []byte("[{\"baseLabels\":{\"label_foo\":\"foo\",\"name\":\"foo\"},\"docstring\":\"metric foo\",\"metric\":{\"type\":\"counter\",\"value\":[]}}]"),
|
out: []byte(`[{"baseLabels":{"label_foo":"foo","name":"foo"},"docstring":"metric foo","metric":{"type":"counter","value":[]}}]`),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
in: input{
|
in: input{
|
||||||
metrics: map[string]metrics.Metric{
|
metrics: map[string]Metric{
|
||||||
"foo": metrics.NewCounter(),
|
"foo": NewCounter(),
|
||||||
"bar": metrics.NewCounter(),
|
"bar": NewCounter(),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
out: []byte("[{\"baseLabels\":{\"label_bar\":\"bar\",\"name\":\"bar\"},\"docstring\":\"metric bar\",\"metric\":{\"type\":\"counter\",\"value\":[]}},{\"baseLabels\":{\"label_foo\":\"foo\",\"name\":\"foo\"},\"docstring\":\"metric foo\",\"metric\":{\"type\":\"counter\",\"value\":[]}}]"),
|
out: []byte(`[{"baseLabels":{"label_bar":"bar","name":"bar"},"docstring":"metric bar","metric":{"type":"counter","value":[]}},{"baseLabels":{"label_foo":"foo","name":"foo"},"docstring":"metric foo","metric":{"type":"counter","value":[]}}]`),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -307,15 +306,14 @@ func testDumpToWriter(t test.Tester) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
actual := &bytes.Buffer{}
|
actual, err := json.Marshal(registry)
|
||||||
|
|
||||||
err := registry.dumpToWriter(actual)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("%d. encountered error while dumping %s", i, err)
|
t.Errorf("%d. encountered error while dumping %s", i, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !bytes.Equal(scenario.out, actual.Bytes()) {
|
if !bytes.Equal(scenario.out, actual) {
|
||||||
t.Errorf("%d. expected %q for dumping, got %q", i, scenario.out, actual.Bytes())
|
t.Errorf("%d. expected %q for dumping, got %q", i, scenario.out, actual)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,10 +1,10 @@
|
||||||
// Copyright (c) 2013, Matt T. Proud
|
// Copyright (c) 2013, Prometheus Team
|
||||||
// All rights reserved.
|
// All rights reserved.
|
||||||
//
|
//
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
package utility
|
package prometheus
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
@ -17,7 +17,7 @@ const (
|
||||||
|
|
||||||
// LabelsToSignature provides a way of building a unique signature
|
// LabelsToSignature provides a way of building a unique signature
|
||||||
// (i.e., fingerprint) for a given label set sequence.
|
// (i.e., fingerprint) for a given label set sequence.
|
||||||
func LabelsToSignature(labels map[string]string) string {
|
func labelsToSignature(labels map[string]string) string {
|
||||||
// TODO(matt): This is a wart, and we'll want to validate that collisions
|
// TODO(matt): This is a wart, and we'll want to validate that collisions
|
||||||
// do not occur in less-than-diligent environments.
|
// do not occur in less-than-diligent environments.
|
||||||
cardinality := len(labels)
|
cardinality := len(labels)
|
|
@ -1,17 +1,16 @@
|
||||||
// Copyright (c) 2013, Matt T. Proud
|
// Copyright (c) 2013, Prometheus Team
|
||||||
// All rights reserved.
|
// All rights reserved.
|
||||||
//
|
//
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
package utility
|
package prometheus
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/prometheus/client_golang/utility/test"
|
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func testLabelsToSignature(t test.Tester) {
|
func testLabelsToSignature(t tester) {
|
||||||
var scenarios = []struct {
|
var scenarios = []struct {
|
||||||
in map[string]string
|
in map[string]string
|
||||||
out string
|
out string
|
||||||
|
@ -24,7 +23,7 @@ func testLabelsToSignature(t test.Tester) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, scenario := range scenarios {
|
for i, scenario := range scenarios {
|
||||||
actual := LabelsToSignature(scenario.in)
|
actual := labelsToSignature(scenario.in)
|
||||||
|
|
||||||
if actual != scenario.out {
|
if actual != scenario.out {
|
||||||
t.Errorf("%d. expected %s, got %s", i, scenario.out, actual)
|
t.Errorf("%d. expected %s, got %s", i, scenario.out, actual)
|
|
@ -1,10 +1,10 @@
|
||||||
// Copyright (c) 2012, Matt T. Proud
|
// Copyright (c) 2013, Prometheus Team
|
||||||
// All rights reserved.
|
// All rights reserved.
|
||||||
//
|
//
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
package maths
|
package prometheus
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"math"
|
"math"
|
||||||
|
@ -14,11 +14,32 @@ import (
|
||||||
// TODO(mtp): Split this out into a summary statistics file once moving/rolling
|
// TODO(mtp): Split this out into a summary statistics file once moving/rolling
|
||||||
// averages are calculated.
|
// averages are calculated.
|
||||||
|
|
||||||
// ReductionMethod provides a method for reducing metrics into a given scalar
|
// ReductionMethod provides a method for reducing metrics into a scalar value.
|
||||||
// value.
|
|
||||||
type ReductionMethod func([]float64) float64
|
type ReductionMethod func([]float64) float64
|
||||||
|
|
||||||
var Average ReductionMethod = func(input []float64) float64 {
|
var (
|
||||||
|
medianReducer = NearestRankReducer(50)
|
||||||
|
)
|
||||||
|
|
||||||
|
// These are the canned ReductionMethods.
|
||||||
|
var (
|
||||||
|
// Reduce to the average of the set.
|
||||||
|
AverageReducer = averageReducer
|
||||||
|
|
||||||
|
// Extract the first modal value.
|
||||||
|
FirstModeReducer = firstModeReducer
|
||||||
|
|
||||||
|
// Reduce to the maximum of the set.
|
||||||
|
MaximumReducer = maximumReducer
|
||||||
|
|
||||||
|
// Reduce to the median of the set.
|
||||||
|
MedianReducer = medianReducer
|
||||||
|
|
||||||
|
// Reduce to the minimum of the set.
|
||||||
|
MinimumReducer = minimumReducer
|
||||||
|
)
|
||||||
|
|
||||||
|
func averageReducer(input []float64) float64 {
|
||||||
count := 0.0
|
count := 0.0
|
||||||
sum := 0.0
|
sum := 0.0
|
||||||
|
|
||||||
|
@ -34,11 +55,10 @@ var Average ReductionMethod = func(input []float64) float64 {
|
||||||
return sum / count
|
return sum / count
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract the first modal value.
|
func firstModeReducer(input []float64) float64 {
|
||||||
var FirstMode ReductionMethod = func(input []float64) float64 {
|
|
||||||
valuesToFrequency := map[float64]int64{}
|
valuesToFrequency := map[float64]int64{}
|
||||||
var largestTally int64 = math.MinInt64
|
largestTally := int64(math.MinInt64)
|
||||||
var largestTallyValue float64 = math.NaN()
|
largestTallyValue := math.NaN()
|
||||||
|
|
||||||
for _, v := range input {
|
for _, v := range input {
|
||||||
presentCount, _ := valuesToFrequency[v]
|
presentCount, _ := valuesToFrequency[v]
|
||||||
|
@ -56,7 +76,7 @@ var FirstMode ReductionMethod = func(input []float64) float64 {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate the percentile by choosing the nearest neighboring value.
|
// Calculate the percentile by choosing the nearest neighboring value.
|
||||||
func NearestRank(input []float64, percentile float64) float64 {
|
func nearestRank(input []float64, percentile float64) float64 {
|
||||||
inputSize := len(input)
|
inputSize := len(input)
|
||||||
|
|
||||||
if inputSize == 0 {
|
if inputSize == 0 {
|
||||||
|
@ -81,14 +101,12 @@ func NearestRank(input []float64, percentile float64) float64 {
|
||||||
// Generate a ReductionMethod based off of extracting a given percentile value.
|
// Generate a ReductionMethod based off of extracting a given percentile value.
|
||||||
func NearestRankReducer(percentile float64) ReductionMethod {
|
func NearestRankReducer(percentile float64) ReductionMethod {
|
||||||
return func(input []float64) float64 {
|
return func(input []float64) float64 {
|
||||||
return NearestRank(input, percentile)
|
return nearestRank(input, percentile)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var Median ReductionMethod = NearestRankReducer(50)
|
func minimumReducer(input []float64) float64 {
|
||||||
|
minimum := math.MaxFloat64
|
||||||
var Minimum ReductionMethod = func(input []float64) float64 {
|
|
||||||
var minimum float64 = math.MaxFloat64
|
|
||||||
|
|
||||||
for _, v := range input {
|
for _, v := range input {
|
||||||
minimum = math.Min(minimum, v)
|
minimum = math.Min(minimum, v)
|
||||||
|
@ -97,8 +115,8 @@ var Minimum ReductionMethod = func(input []float64) float64 {
|
||||||
return minimum
|
return minimum
|
||||||
}
|
}
|
||||||
|
|
||||||
var Maximum ReductionMethod = func(input []float64) float64 {
|
func maximumReducer(input []float64) float64 {
|
||||||
var maximum float64 = math.SmallestNonzeroFloat64
|
maximum := math.SmallestNonzeroFloat64
|
||||||
|
|
||||||
for _, v := range input {
|
for _, v := range input {
|
||||||
maximum = math.Max(maximum, v)
|
maximum = math.Max(maximum, v)
|
|
@ -1,10 +1,10 @@
|
||||||
// Copyright (c) 2012, Matt T. Proud
|
// Copyright (c) 2013, Prometheus Team
|
||||||
// All rights reserved.
|
// All rights reserved.
|
||||||
//
|
//
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
package maths
|
package prometheus
|
||||||
|
|
||||||
import (
|
import (
|
||||||
. "github.com/matttproud/gocheck"
|
. "github.com/matttproud/gocheck"
|
||||||
|
@ -12,42 +12,42 @@ import (
|
||||||
|
|
||||||
func (s *S) TestAverageOnEmpty(c *C) {
|
func (s *S) TestAverageOnEmpty(c *C) {
|
||||||
empty := []float64{}
|
empty := []float64{}
|
||||||
var v float64 = Average(empty)
|
var v float64 = AverageReducer(empty)
|
||||||
|
|
||||||
c.Assert(v, IsNaN)
|
c.Assert(v, IsNaN)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *S) TestAverageForSingleton(c *C) {
|
func (s *S) TestAverageForSingleton(c *C) {
|
||||||
input := []float64{5}
|
input := []float64{5}
|
||||||
var v float64 = Average(input)
|
var v float64 = AverageReducer(input)
|
||||||
|
|
||||||
c.Check(v, Equals, 5.0)
|
c.Check(v, Equals, 5.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *S) TestAverage(c *C) {
|
func (s *S) TestAverage(c *C) {
|
||||||
input := []float64{5, 15}
|
input := []float64{5, 15}
|
||||||
var v float64 = Average(input)
|
var v float64 = AverageReducer(input)
|
||||||
|
|
||||||
c.Check(v, Equals, 10.0)
|
c.Check(v, Equals, 10.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *S) TestFirstModeOnEmpty(c *C) {
|
func (s *S) TestFirstModeOnEmpty(c *C) {
|
||||||
input := []float64{}
|
input := []float64{}
|
||||||
var v float64 = FirstMode(input)
|
var v float64 = FirstModeReducer(input)
|
||||||
|
|
||||||
c.Assert(v, IsNaN)
|
c.Assert(v, IsNaN)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *S) TestFirstModeForSingleton(c *C) {
|
func (s *S) TestFirstModeForSingleton(c *C) {
|
||||||
input := []float64{5}
|
input := []float64{5}
|
||||||
var v float64 = FirstMode(input)
|
var v float64 = FirstModeReducer(input)
|
||||||
|
|
||||||
c.Check(v, Equals, 5.0)
|
c.Check(v, Equals, 5.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *S) TestFirstModeForUnimodal(c *C) {
|
func (s *S) TestFirstModeForUnimodal(c *C) {
|
||||||
input := []float64{1, 2, 3, 4, 3}
|
input := []float64{1, 2, 3, 4, 3}
|
||||||
var v float64 = FirstMode(input)
|
var v float64 = FirstModeReducer(input)
|
||||||
|
|
||||||
c.Check(v, Equals, 3.0)
|
c.Check(v, Equals, 3.0)
|
||||||
}
|
}
|
||||||
|
@ -55,25 +55,25 @@ func (s *S) TestFirstModeForUnimodal(c *C) {
|
||||||
func (s *S) TestNearestRankForEmpty(c *C) {
|
func (s *S) TestNearestRankForEmpty(c *C) {
|
||||||
input := []float64{}
|
input := []float64{}
|
||||||
|
|
||||||
c.Assert(NearestRank(input, 0), IsNaN)
|
c.Assert(nearestRank(input, 0), IsNaN)
|
||||||
c.Assert(NearestRank(input, 50), IsNaN)
|
c.Assert(nearestRank(input, 50), IsNaN)
|
||||||
c.Assert(NearestRank(input, 100), IsNaN)
|
c.Assert(nearestRank(input, 100), IsNaN)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *S) TestNearestRankForSingleton(c *C) {
|
func (s *S) TestNearestRankForSingleton(c *C) {
|
||||||
input := []float64{5}
|
input := []float64{5}
|
||||||
|
|
||||||
c.Check(NearestRank(input, 0), Equals, 5.0)
|
c.Check(nearestRank(input, 0), Equals, 5.0)
|
||||||
c.Check(NearestRank(input, 50), Equals, 5.0)
|
c.Check(nearestRank(input, 50), Equals, 5.0)
|
||||||
c.Check(NearestRank(input, 100), Equals, 5.0)
|
c.Check(nearestRank(input, 100), Equals, 5.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *S) TestNearestRankForDouble(c *C) {
|
func (s *S) TestNearestRankForDouble(c *C) {
|
||||||
input := []float64{5, 5}
|
input := []float64{5, 5}
|
||||||
|
|
||||||
c.Check(NearestRank(input, 0), Equals, 5.0)
|
c.Check(nearestRank(input, 0), Equals, 5.0)
|
||||||
c.Check(NearestRank(input, 50), Equals, 5.0)
|
c.Check(nearestRank(input, 50), Equals, 5.0)
|
||||||
c.Check(NearestRank(input, 100), Equals, 5.0)
|
c.Check(nearestRank(input, 100), Equals, 5.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *S) TestNearestRankFor100(c *C) {
|
func (s *S) TestNearestRankFor100(c *C) {
|
||||||
|
@ -83,9 +83,9 @@ func (s *S) TestNearestRankFor100(c *C) {
|
||||||
input[i] = float64(i + 1)
|
input[i] = float64(i + 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Check(NearestRank(input, 0), Equals, 1.0)
|
c.Check(nearestRank(input, 0), Equals, 1.0)
|
||||||
c.Check(NearestRank(input, 50), Equals, 51.0)
|
c.Check(nearestRank(input, 50), Equals, 51.0)
|
||||||
c.Check(NearestRank(input, 100), Equals, 100.0)
|
c.Check(nearestRank(input, 100), Equals, 100.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *S) TestNearestRankFor101(c *C) {
|
func (s *S) TestNearestRankFor101(c *C) {
|
||||||
|
@ -95,25 +95,25 @@ func (s *S) TestNearestRankFor101(c *C) {
|
||||||
input[i] = float64(i + 1)
|
input[i] = float64(i + 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Check(NearestRank(input, 0), Equals, 1.0)
|
c.Check(nearestRank(input, 0), Equals, 1.0)
|
||||||
c.Check(NearestRank(input, 50), Equals, 51.0)
|
c.Check(nearestRank(input, 50), Equals, 51.0)
|
||||||
c.Check(NearestRank(input, 100), Equals, 101.0)
|
c.Check(nearestRank(input, 100), Equals, 101.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *S) TestMedianReducer(c *C) {
|
func (s *S) TestMedianReducer(c *C) {
|
||||||
input := []float64{1, 2, 3}
|
input := []float64{1, 2, 3}
|
||||||
|
|
||||||
c.Check(Median(input), Equals, 2.0)
|
c.Check(MedianReducer(input), Equals, 2.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *S) TestMinimum(c *C) {
|
func (s *S) TestMinimum(c *C) {
|
||||||
input := []float64{5, 1, 10, 1.1, 4}
|
input := []float64{5, 1, 10, 1.1, 4}
|
||||||
|
|
||||||
c.Check(Minimum(input), Equals, 1.0)
|
c.Check(MinimumReducer(input), Equals, 1.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *S) TestMaximum(c *C) {
|
func (s *S) TestMaximum(c *C) {
|
||||||
input := []float64{5, 1, 10, 1.1, 4}
|
input := []float64{5, 1, 10, 1.1, 4}
|
||||||
|
|
||||||
c.Check(Maximum(input), Equals, 10.0)
|
c.Check(MaximumReducer(input), Equals, 10.0)
|
||||||
}
|
}
|
|
@ -1,14 +1,13 @@
|
||||||
// Copyright (c) 2012, Matt T. Proud
|
// Copyright (c) 2013, Prometheus Team
|
||||||
// All rights reserved.
|
// All rights reserved.
|
||||||
//
|
//
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
package metrics
|
package prometheus
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/prometheus/client_golang/maths"
|
|
||||||
"math"
|
"math"
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
@ -35,38 +34,49 @@ func emptyFilter(e TallyingIndexEstimator) TallyingIndexEstimator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Report the smallest observed value in the bucket.
|
var (
|
||||||
var Minimum TallyingIndexEstimator = emptyFilter(func(minimum, maximum float64, _, observations int) float64 {
|
minimumEstimator = emptyFilter(func(minimum, maximum float64, _, observations int) float64 {
|
||||||
return minimum
|
|
||||||
})
|
|
||||||
|
|
||||||
// Report the largest observed value in the bucket.
|
|
||||||
var Maximum TallyingIndexEstimator = emptyFilter(func(minimum, maximum float64, _, observations int) float64 {
|
|
||||||
return maximum
|
|
||||||
})
|
|
||||||
|
|
||||||
// Report the average of the extrema.
|
|
||||||
var Average TallyingIndexEstimator = emptyFilter(func(minimum, maximum float64, _, observations int) float64 {
|
|
||||||
return maths.Average([]float64{minimum, maximum})
|
|
||||||
})
|
|
||||||
|
|
||||||
// Report the minimum value of the index is in the lower-third of observations,
|
|
||||||
// the average if in the middle-third, and the maximum if in the largest third.
|
|
||||||
var Uniform TallyingIndexEstimator = emptyFilter(func(minimum, maximum float64, index, observations int) float64 {
|
|
||||||
if observations == 1 {
|
|
||||||
return minimum
|
return minimum
|
||||||
}
|
})
|
||||||
|
|
||||||
location := float64(index) / float64(observations)
|
maximumEstimator = emptyFilter(func(minimum, maximum float64, _, observations int) float64 {
|
||||||
|
|
||||||
if location > upperThird {
|
|
||||||
return maximum
|
return maximum
|
||||||
} else if location < lowerThird {
|
})
|
||||||
return minimum
|
|
||||||
}
|
|
||||||
|
|
||||||
return maths.Average([]float64{minimum, maximum})
|
averageEstimator = emptyFilter(func(minimum, maximum float64, _, observations int) float64 {
|
||||||
})
|
return AverageReducer([]float64{minimum, maximum})
|
||||||
|
})
|
||||||
|
|
||||||
|
uniformEstimator = emptyFilter(func(minimum, maximum float64, index, observations int) float64 {
|
||||||
|
if observations == 1 {
|
||||||
|
return minimum
|
||||||
|
}
|
||||||
|
|
||||||
|
location := float64(index) / float64(observations)
|
||||||
|
|
||||||
|
if location > upperThird {
|
||||||
|
return maximum
|
||||||
|
} else if location < lowerThird {
|
||||||
|
return minimum
|
||||||
|
}
|
||||||
|
|
||||||
|
return AverageReducer([]float64{minimum, maximum})
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
// These are the canned TallyingIndexEstimators.
|
||||||
|
var (
|
||||||
|
// Report the smallest observed value in the bucket.
|
||||||
|
MinimumEstimator = minimumEstimator
|
||||||
|
// Report the largest observed value in the bucket.
|
||||||
|
MaximumEstimator = maximumEstimator
|
||||||
|
// Report the average of the extrema.
|
||||||
|
AverageEstimator = averageEstimator
|
||||||
|
// Report the minimum value of the index if it is in the lower-third of
|
||||||
|
// observations, the average if in the middle-third, and the maximum if in
|
||||||
|
// the largest third
|
||||||
|
UniformEstimator = uniformEstimator
|
||||||
|
)
|
||||||
|
|
||||||
// A TallyingBucket is a Bucket that tallies when an object is added to it.
|
// A TallyingBucket is a Bucket that tallies when an object is added to it.
|
||||||
// Upon insertion, an object is compared against collected extrema and noted
|
// Upon insertion, an object is compared against collected extrema and noted
|
||||||
|
@ -127,7 +137,7 @@ func (b *TallyingBucket) Reset() {
|
||||||
// Produce a TallyingBucket with sane defaults.
|
// Produce a TallyingBucket with sane defaults.
|
||||||
func DefaultTallyingBucket() TallyingBucket {
|
func DefaultTallyingBucket() TallyingBucket {
|
||||||
return TallyingBucket{
|
return TallyingBucket{
|
||||||
estimator: Minimum,
|
estimator: MinimumEstimator,
|
||||||
largestObserved: math.SmallestNonzeroFloat64,
|
largestObserved: math.SmallestNonzeroFloat64,
|
||||||
smallestObserved: math.MaxFloat64,
|
smallestObserved: math.MaxFloat64,
|
||||||
}
|
}
|
|
@ -1,40 +1,39 @@
|
||||||
// Copyright (c) 2012, Matt T. Proud
|
// Copyright (c) 2013, Prometheus Team
|
||||||
// All rights reserved.
|
// All rights reserved.
|
||||||
//
|
//
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
package metrics
|
package prometheus
|
||||||
|
|
||||||
import (
|
import (
|
||||||
. "github.com/matttproud/gocheck"
|
. "github.com/matttproud/gocheck"
|
||||||
"github.com/prometheus/client_golang/maths"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *S) TestTallyingPercentileEstimatorMinimum(c *C) {
|
func (s *S) TestTallyingPercentileEstimatorMinimum(c *C) {
|
||||||
c.Assert(Minimum(-2, -1, 0, 0), maths.IsNaN)
|
c.Assert(MinimumEstimator(-2, -1, 0, 0), IsNaN)
|
||||||
c.Check(Minimum(-2, -1, 0, 1), Equals, -2.0)
|
c.Check(MinimumEstimator(-2, -1, 0, 1), Equals, -2.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *S) TestTallyingPercentileEstimatorMaximum(c *C) {
|
func (s *S) TestTallyingPercentileEstimatorMaximum(c *C) {
|
||||||
c.Assert(Maximum(-2, -1, 0, 0), maths.IsNaN)
|
c.Assert(MaximumEstimator(-2, -1, 0, 0), IsNaN)
|
||||||
c.Check(Maximum(-2, -1, 0, 1), Equals, -1.0)
|
c.Check(MaximumEstimator(-2, -1, 0, 1), Equals, -1.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *S) TestTallyingPercentilesEstimatorAverage(c *C) {
|
func (s *S) TestTallyingPercentilesEstimatorAverage(c *C) {
|
||||||
c.Assert(Average(-2, -1, 0, 0), maths.IsNaN)
|
c.Assert(AverageEstimator(-2, -1, 0, 0), IsNaN)
|
||||||
c.Check(Average(-2, -2, 0, 1), Equals, -2.0)
|
c.Check(AverageEstimator(-2, -2, 0, 1), Equals, -2.0)
|
||||||
c.Check(Average(-1, -1, 0, 1), Equals, -1.0)
|
c.Check(AverageEstimator(-1, -1, 0, 1), Equals, -1.0)
|
||||||
c.Check(Average(1, 1, 0, 2), Equals, 1.0)
|
c.Check(AverageEstimator(1, 1, 0, 2), Equals, 1.0)
|
||||||
c.Check(Average(2, 1, 0, 2), Equals, 1.5)
|
c.Check(AverageEstimator(2, 1, 0, 2), Equals, 1.5)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *S) TestTallyingPercentilesEstimatorUniform(c *C) {
|
func (s *S) TestTallyingPercentilesEstimatorUniform(c *C) {
|
||||||
c.Assert(Uniform(-5, 5, 0, 0), maths.IsNaN)
|
c.Assert(UniformEstimator(-5, 5, 0, 0), IsNaN)
|
||||||
|
|
||||||
c.Check(Uniform(-5, 5, 0, 2), Equals, -5.0)
|
c.Check(UniformEstimator(-5, 5, 0, 2), Equals, -5.0)
|
||||||
c.Check(Uniform(-5, 5, 1, 2), Equals, 0.0)
|
c.Check(UniformEstimator(-5, 5, 1, 2), Equals, 0.0)
|
||||||
c.Check(Uniform(-5, 5, 2, 2), Equals, 5.0)
|
c.Check(UniformEstimator(-5, 5, 2, 2), Equals, 5.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *S) TestTallyingBucketBuilder(c *C) {
|
func (s *S) TestTallyingBucketBuilder(c *C) {
|
|
@ -1,15 +1,13 @@
|
||||||
// Copyright (c) 2013, Matt T. Proud
|
// Copyright (c) 2013, Prometheus Team
|
||||||
// All rights reserved.
|
// All rights reserved.
|
||||||
//
|
//
|
||||||
// Use of this source code is governed by a BSD-style license that can be found
|
// Use of this source code is governed by a BSD-style license that can be found
|
||||||
// in the LICENSE file.
|
// in the LICENSE file.
|
||||||
//
|
//
|
||||||
|
|
||||||
package registry
|
package prometheus
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/prometheus/client_golang/maths"
|
|
||||||
"github.com/prometheus/client_golang/metrics"
|
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -17,18 +15,15 @@ import (
|
||||||
// exposed if the DefaultRegistry's exporter is hooked into the HTTP request
|
// exposed if the DefaultRegistry's exporter is hooked into the HTTP request
|
||||||
// handler.
|
// handler.
|
||||||
var (
|
var (
|
||||||
marshalErrorCount = metrics.NewCounter()
|
requestCount = NewCounter()
|
||||||
dumpErrorCount = metrics.NewCounter()
|
requestLatencyBuckets = LogarithmicSizedBucketsFor(0, 1000)
|
||||||
|
requestLatency = NewHistogram(&HistogramSpecification{
|
||||||
requestCount = metrics.NewCounter()
|
|
||||||
requestLatencyBuckets = metrics.LogarithmicSizedBucketsFor(0, 1000)
|
|
||||||
requestLatency = metrics.NewHistogram(&metrics.HistogramSpecification{
|
|
||||||
Starts: requestLatencyBuckets,
|
Starts: requestLatencyBuckets,
|
||||||
BucketBuilder: metrics.AccumulatingBucketBuilder(metrics.EvictAndReplaceWith(50, maths.Average), 1000),
|
BucketBuilder: AccumulatingBucketBuilder(EvictAndReplaceWith(50, AverageReducer), 1000),
|
||||||
ReportablePercentiles: []float64{0.01, 0.05, 0.5, 0.9, 0.99},
|
ReportablePercentiles: []float64{0.01, 0.05, 0.5, 0.9, 0.99},
|
||||||
})
|
})
|
||||||
|
|
||||||
startTime = metrics.NewGauge()
|
startTime = NewGauge()
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -42,8 +37,8 @@ func init() {
|
||||||
|
|
||||||
// This callback accumulates the microsecond duration of the reporting
|
// This callback accumulates the microsecond duration of the reporting
|
||||||
// framework's overhead such that it can be reported.
|
// framework's overhead such that it can be reported.
|
||||||
var requestLatencyAccumulator metrics.CompletionCallback = func(duration time.Duration) {
|
var requestLatencyAccumulator = func(began time.Time) {
|
||||||
microseconds := float64(duration / time.Microsecond)
|
microseconds := float64(time.Since(began) / time.Microsecond)
|
||||||
|
|
||||||
requestLatency.Add(nil, microseconds)
|
requestLatency.Add(nil, microseconds)
|
||||||
}
|
}
|
|
@ -1,10 +1,10 @@
|
||||||
// Copyright (c) 2012, Matt T. Proud
|
// Copyright (c) 2013, Prometheus Team
|
||||||
// All rights reserved.
|
// All rights reserved.
|
||||||
//
|
//
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
package utility
|
package prometheus
|
||||||
|
|
||||||
import (
|
import (
|
||||||
. "github.com/matttproud/gocheck"
|
. "github.com/matttproud/gocheck"
|
||||||
|
@ -19,7 +19,7 @@ var ValueEquals Checker = &valueEqualsChecker{
|
||||||
}
|
}
|
||||||
|
|
||||||
func (checker *valueEqualsChecker) Check(params []interface{}, names []string) (result bool, error string) {
|
func (checker *valueEqualsChecker) Check(params []interface{}, names []string) (result bool, error string) {
|
||||||
actual := params[0].(*Item).Value
|
actual := params[0].(*item).Value
|
||||||
expected := params[1]
|
expected := params[1]
|
||||||
|
|
||||||
return actual == expected, ""
|
return actual == expected, ""
|
|
@ -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
|
|
Loading…
Reference in New Issue