2015-02-02 17:14:36 +03:00
// Copyright 2014 The Prometheus Authors
2014-05-07 22:08:33 +04:00
// 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.
package prometheus_test
import (
"flag"
"fmt"
"math"
"net/http"
2014-06-23 16:15:35 +04:00
"runtime"
2014-05-07 22:08:33 +04:00
"sort"
dto "github.com/prometheus/client_model/go"
2015-02-14 02:00:34 +03:00
"github.com/golang/protobuf/proto"
2014-05-07 22:08:33 +04:00
"github.com/prometheus/client_golang/prometheus"
)
func ExampleGauge ( ) {
opsQueued := prometheus . NewGauge ( prometheus . GaugeOpts {
Namespace : "our_company" ,
Subsystem : "blob_storage" ,
Name : "ops_queued" ,
Help : "Number of blob storage operations waiting to be processed." ,
} )
prometheus . MustRegister ( opsQueued )
// 10 operations queued by the goroutine managing incoming requests.
opsQueued . Add ( 10 )
// A worker goroutine has picked up a waiting operation.
opsQueued . Dec ( )
// And once more...
opsQueued . Dec ( )
}
func ExampleGaugeVec ( ) {
binaryVersion := flag . String ( "binary_version" , "debug" , "Version of the binary: debug, canary, production." )
flag . Parse ( )
opsQueued := prometheus . NewGaugeVec (
prometheus . GaugeOpts {
Namespace : "our_company" ,
Subsystem : "blob_storage" ,
Name : "ops_queued" ,
Help : "Number of blob storage operations waiting to be processed, partitioned by user and type." ,
ConstLabels : prometheus . Labels { "binary_version" : * binaryVersion } ,
} ,
[ ] string {
// Which user has requested the operation?
"user" ,
// Of what type is the operation?
"type" ,
} ,
)
prometheus . MustRegister ( opsQueued )
// Increase a value using compact (but order-sensitive!) WithLabelValues().
opsQueued . WithLabelValues ( "bob" , "put" ) . Add ( 4 )
// Increase a value with a map using WithLabels. More verbose, but order
// doesn't matter anymore.
opsQueued . With ( prometheus . Labels { "type" : "delete" , "user" : "alice" } ) . Inc ( )
}
2014-06-23 16:15:35 +04:00
func ExampleGaugeFunc ( ) {
Allow error reporting during metrics collection and simplify Register().
Both are interface changes I want to get in before public
announcement. They only break rare usage cases, and are always easy to
fix, but still we want to avoid breaking changes after a wider
announcement of the project.
The change of Register() simply removes the return of the Collector,
which nobody was using in practice. It was just bloating the call
syntax. Note that this is different from RegisterOrGet(), which is
used at various occasions where you want to register something that
might or might not be registered already, but if it is, you want the
previously registered Collector back (because that's the relevant
one).
WRT error reporting: I first tried the obvious way of letting the
Collector methods Describe() and Collect() return error. However, I
had to conclude that that bloated _many_ calls and their handling in
very obnoxious ways. On the other hand, the case where you actually
want to report errors during registration or collection is very
rare. Hence, this approach has the wrong trade-off. The approach taken
here might at first appear clunky but is in practice quite handy,
mostly because there is almost no change for the "normal" case of "no
special error handling", but also because it plays well with the way
descriptors and metrics are handled (via channels).
Explaining the approach in more detail:
- During registration / describe: Error handling was actually already
in place (for invalid descriptors, which carry an error anyway). I
only added a convenience function to create an invalid descriptor
with a given error on purpose.
- Metrics are now treated in a similar way. The Write method returns
an error now (the only change in interface). An "invalid metric" is
provided that can be sent via the channel to signal that that metric
could not be collected. It alse transports an error.
NON-GOALS OF THIS COMMIT:
This is NOT yet the major improvement of the whole registry part,
where we want a public Registry interface and plenty of modular
configurations (for error handling, various auto-metrics, http
instrumentation, testing, ...). However, we can do that whole thing
without breaking existing interfaces. For now (which is a significant
issue) any error during collection will either cause a 500 HTTP
response or a panic (depending on registry config). Later, we
definitely want to have a possibility to skip (and only report
somehow) non-collectible metrics instead of aborting the whole scrape.
2015-01-12 21:16:09 +03:00
if err := prometheus . Register ( prometheus . NewGaugeFunc (
2014-06-23 16:15:35 +04:00
prometheus . GaugeOpts {
Subsystem : "runtime" ,
Name : "goroutines_count" ,
Help : "Number of goroutines that currently exist." ,
} ,
func ( ) float64 { return float64 ( runtime . NumGoroutine ( ) ) } ,
) ) ; err == nil {
2014-07-01 22:20:42 +04:00
fmt . Println ( "GaugeFunc 'goroutines_count' registered." )
2014-06-23 16:15:35 +04:00
}
// Note that the count of goroutines is a gauge (and not a counter) as
// it can go up and down.
// Output:
// GaugeFunc 'goroutines_count' registered.
}
2014-05-07 22:08:33 +04:00
func ExampleCounter ( ) {
pushCounter := prometheus . NewCounter ( prometheus . CounterOpts {
Name : "repository_pushes" , // Note: No help string...
} )
Allow error reporting during metrics collection and simplify Register().
Both are interface changes I want to get in before public
announcement. They only break rare usage cases, and are always easy to
fix, but still we want to avoid breaking changes after a wider
announcement of the project.
The change of Register() simply removes the return of the Collector,
which nobody was using in practice. It was just bloating the call
syntax. Note that this is different from RegisterOrGet(), which is
used at various occasions where you want to register something that
might or might not be registered already, but if it is, you want the
previously registered Collector back (because that's the relevant
one).
WRT error reporting: I first tried the obvious way of letting the
Collector methods Describe() and Collect() return error. However, I
had to conclude that that bloated _many_ calls and their handling in
very obnoxious ways. On the other hand, the case where you actually
want to report errors during registration or collection is very
rare. Hence, this approach has the wrong trade-off. The approach taken
here might at first appear clunky but is in practice quite handy,
mostly because there is almost no change for the "normal" case of "no
special error handling", but also because it plays well with the way
descriptors and metrics are handled (via channels).
Explaining the approach in more detail:
- During registration / describe: Error handling was actually already
in place (for invalid descriptors, which carry an error anyway). I
only added a convenience function to create an invalid descriptor
with a given error on purpose.
- Metrics are now treated in a similar way. The Write method returns
an error now (the only change in interface). An "invalid metric" is
provided that can be sent via the channel to signal that that metric
could not be collected. It alse transports an error.
NON-GOALS OF THIS COMMIT:
This is NOT yet the major improvement of the whole registry part,
where we want a public Registry interface and plenty of modular
configurations (for error handling, various auto-metrics, http
instrumentation, testing, ...). However, we can do that whole thing
without breaking existing interfaces. For now (which is a significant
issue) any error during collection will either cause a 500 HTTP
response or a panic (depending on registry config). Later, we
definitely want to have a possibility to skip (and only report
somehow) non-collectible metrics instead of aborting the whole scrape.
2015-01-12 21:16:09 +03:00
err := prometheus . Register ( pushCounter ) // ... so this will return an error.
2014-05-07 22:08:33 +04:00
if err != nil {
fmt . Println ( "Push counter couldn't be registered, no counting will happen:" , err )
return
}
// Try it once more, this time with a help string.
pushCounter = prometheus . NewCounter ( prometheus . CounterOpts {
Name : "repository_pushes" ,
Help : "Number of pushes to external repository." ,
} )
Allow error reporting during metrics collection and simplify Register().
Both are interface changes I want to get in before public
announcement. They only break rare usage cases, and are always easy to
fix, but still we want to avoid breaking changes after a wider
announcement of the project.
The change of Register() simply removes the return of the Collector,
which nobody was using in practice. It was just bloating the call
syntax. Note that this is different from RegisterOrGet(), which is
used at various occasions where you want to register something that
might or might not be registered already, but if it is, you want the
previously registered Collector back (because that's the relevant
one).
WRT error reporting: I first tried the obvious way of letting the
Collector methods Describe() and Collect() return error. However, I
had to conclude that that bloated _many_ calls and their handling in
very obnoxious ways. On the other hand, the case where you actually
want to report errors during registration or collection is very
rare. Hence, this approach has the wrong trade-off. The approach taken
here might at first appear clunky but is in practice quite handy,
mostly because there is almost no change for the "normal" case of "no
special error handling", but also because it plays well with the way
descriptors and metrics are handled (via channels).
Explaining the approach in more detail:
- During registration / describe: Error handling was actually already
in place (for invalid descriptors, which carry an error anyway). I
only added a convenience function to create an invalid descriptor
with a given error on purpose.
- Metrics are now treated in a similar way. The Write method returns
an error now (the only change in interface). An "invalid metric" is
provided that can be sent via the channel to signal that that metric
could not be collected. It alse transports an error.
NON-GOALS OF THIS COMMIT:
This is NOT yet the major improvement of the whole registry part,
where we want a public Registry interface and plenty of modular
configurations (for error handling, various auto-metrics, http
instrumentation, testing, ...). However, we can do that whole thing
without breaking existing interfaces. For now (which is a significant
issue) any error during collection will either cause a 500 HTTP
response or a panic (depending on registry config). Later, we
definitely want to have a possibility to skip (and only report
somehow) non-collectible metrics instead of aborting the whole scrape.
2015-01-12 21:16:09 +03:00
err = prometheus . Register ( pushCounter )
2014-05-07 22:08:33 +04:00
if err != nil {
fmt . Println ( "Push counter couldn't be registered AGAIN, no counting will happen:" , err )
return
}
pushComplete := make ( chan struct { } )
// TODO: Start a goroutine that performs repository pushes and reports
// each completion via the channel.
for _ = range pushComplete {
pushCounter . Inc ( )
}
// Output:
// Push counter couldn't be registered, no counting will happen: descriptor Desc{fqName: "repository_pushes", help: "", constLabels: {}, variableLabels: []} is invalid: empty help string
}
func ExampleCounterVec ( ) {
binaryVersion := flag . String ( "environment" , "test" , "Execution environment: test, staging, production." )
flag . Parse ( )
httpReqs := prometheus . NewCounterVec (
prometheus . CounterOpts {
Name : "http_requests_total" ,
2015-02-19 17:34:04 +03:00
Help : "How many HTTP requests processed, partitioned by status code and HTTP method." ,
2014-05-07 22:08:33 +04:00
ConstLabels : prometheus . Labels { "env" : * binaryVersion } ,
} ,
[ ] string { "code" , "method" } ,
)
prometheus . MustRegister ( httpReqs )
httpReqs . WithLabelValues ( "404" , "POST" ) . Add ( 42 )
// If you have to access the same set of labels very frequently, it
// might be good to retrieve the metric only once and keep a handle to
// it. But beware of deletion of that metric, see below!
m := httpReqs . WithLabelValues ( "200" , "GET" )
for i := 0 ; i < 1000000 ; i ++ {
m . Inc ( )
}
// Delete a metric from the vector. If you have previously kept a handle
// to that metric (as above), future updates via that handle will go
// unseen (even if you re-create a metric with the same label set
// later).
httpReqs . DeleteLabelValues ( "200" , "GET" )
// Same thing with the more verbose Labels syntax.
httpReqs . Delete ( prometheus . Labels { "method" : "GET" , "code" : "200" } )
}
func ExampleInstrumentHandler ( ) {
// Handle the "/doc" endpoint with the standard http.FileServer handler.
// By wrapping the handler with InstrumentHandler, request count,
// request and response sizes, and request latency are automatically
// exported to Prometheus, partitioned by HTTP status code and method
// and by the handler name (here "fileserver").
http . Handle ( "/doc" , prometheus . InstrumentHandler (
"fileserver" , http . FileServer ( http . Dir ( "/usr/share/doc" ) ) ,
) )
// The Prometheus handler still has to be registered to handle the
// "/metrics" endpoint. The handler returned by prometheus.Handler() is
// already instrumented - with "prometheus" as the handler name. In this
// example, we want the handler name to be "metrics", so we instrument
// the uninstrumented Prometheus handler ourselves.
http . Handle ( "/metrics" , prometheus . InstrumentHandler (
"metrics" , prometheus . UninstrumentedHandler ( ) ,
) )
}
func ExampleLabelPairSorter ( ) {
labelPairs := [ ] * dto . LabelPair {
& dto . LabelPair { Name : proto . String ( "status" ) , Value : proto . String ( "404" ) } ,
& dto . LabelPair { Name : proto . String ( "method" ) , Value : proto . String ( "get" ) } ,
}
sort . Sort ( prometheus . LabelPairSorter ( labelPairs ) )
fmt . Println ( labelPairs )
// Output:
// [name:"method" value:"get" name:"status" value:"404" ]
}
func ExampleRegister ( ) {
// Imagine you have a worker pool and want to count the tasks completed.
taskCounter := prometheus . NewCounter ( prometheus . CounterOpts {
Subsystem : "worker_pool" ,
Name : "completed_tasks_total" ,
Help : "Total number of tasks completed." ,
} )
// This will register fine.
Allow error reporting during metrics collection and simplify Register().
Both are interface changes I want to get in before public
announcement. They only break rare usage cases, and are always easy to
fix, but still we want to avoid breaking changes after a wider
announcement of the project.
The change of Register() simply removes the return of the Collector,
which nobody was using in practice. It was just bloating the call
syntax. Note that this is different from RegisterOrGet(), which is
used at various occasions where you want to register something that
might or might not be registered already, but if it is, you want the
previously registered Collector back (because that's the relevant
one).
WRT error reporting: I first tried the obvious way of letting the
Collector methods Describe() and Collect() return error. However, I
had to conclude that that bloated _many_ calls and their handling in
very obnoxious ways. On the other hand, the case where you actually
want to report errors during registration or collection is very
rare. Hence, this approach has the wrong trade-off. The approach taken
here might at first appear clunky but is in practice quite handy,
mostly because there is almost no change for the "normal" case of "no
special error handling", but also because it plays well with the way
descriptors and metrics are handled (via channels).
Explaining the approach in more detail:
- During registration / describe: Error handling was actually already
in place (for invalid descriptors, which carry an error anyway). I
only added a convenience function to create an invalid descriptor
with a given error on purpose.
- Metrics are now treated in a similar way. The Write method returns
an error now (the only change in interface). An "invalid metric" is
provided that can be sent via the channel to signal that that metric
could not be collected. It alse transports an error.
NON-GOALS OF THIS COMMIT:
This is NOT yet the major improvement of the whole registry part,
where we want a public Registry interface and plenty of modular
configurations (for error handling, various auto-metrics, http
instrumentation, testing, ...). However, we can do that whole thing
without breaking existing interfaces. For now (which is a significant
issue) any error during collection will either cause a 500 HTTP
response or a panic (depending on registry config). Later, we
definitely want to have a possibility to skip (and only report
somehow) non-collectible metrics instead of aborting the whole scrape.
2015-01-12 21:16:09 +03:00
if err := prometheus . Register ( taskCounter ) ; err != nil {
2014-05-07 22:08:33 +04:00
fmt . Println ( err )
} else {
fmt . Println ( "taskCounter registered." )
}
// Don't forget to tell the HTTP server about the Prometheus handler.
2015-02-19 17:34:04 +03:00
// (In a real program, you still need to start the HTTP server...)
2014-05-07 22:08:33 +04:00
http . Handle ( "/metrics" , prometheus . Handler ( ) )
// Now you can start workers and give every one of them a pointer to
// taskCounter and let it increment it whenever it completes a task.
taskCounter . Inc ( ) // This has to happen somewhere in the worker code.
// But wait, you want to see how individual workers perform. So you need
// a vector of counters, with one element for each worker.
taskCounterVec := prometheus . NewCounterVec (
prometheus . CounterOpts {
Subsystem : "worker_pool" ,
Name : "completed_tasks_total" ,
Help : "Total number of tasks completed." ,
} ,
[ ] string { "worker_id" } ,
)
// Registering will fail because we already have a metric of that name.
Allow error reporting during metrics collection and simplify Register().
Both are interface changes I want to get in before public
announcement. They only break rare usage cases, and are always easy to
fix, but still we want to avoid breaking changes after a wider
announcement of the project.
The change of Register() simply removes the return of the Collector,
which nobody was using in practice. It was just bloating the call
syntax. Note that this is different from RegisterOrGet(), which is
used at various occasions where you want to register something that
might or might not be registered already, but if it is, you want the
previously registered Collector back (because that's the relevant
one).
WRT error reporting: I first tried the obvious way of letting the
Collector methods Describe() and Collect() return error. However, I
had to conclude that that bloated _many_ calls and their handling in
very obnoxious ways. On the other hand, the case where you actually
want to report errors during registration or collection is very
rare. Hence, this approach has the wrong trade-off. The approach taken
here might at first appear clunky but is in practice quite handy,
mostly because there is almost no change for the "normal" case of "no
special error handling", but also because it plays well with the way
descriptors and metrics are handled (via channels).
Explaining the approach in more detail:
- During registration / describe: Error handling was actually already
in place (for invalid descriptors, which carry an error anyway). I
only added a convenience function to create an invalid descriptor
with a given error on purpose.
- Metrics are now treated in a similar way. The Write method returns
an error now (the only change in interface). An "invalid metric" is
provided that can be sent via the channel to signal that that metric
could not be collected. It alse transports an error.
NON-GOALS OF THIS COMMIT:
This is NOT yet the major improvement of the whole registry part,
where we want a public Registry interface and plenty of modular
configurations (for error handling, various auto-metrics, http
instrumentation, testing, ...). However, we can do that whole thing
without breaking existing interfaces. For now (which is a significant
issue) any error during collection will either cause a 500 HTTP
response or a panic (depending on registry config). Later, we
definitely want to have a possibility to skip (and only report
somehow) non-collectible metrics instead of aborting the whole scrape.
2015-01-12 21:16:09 +03:00
if err := prometheus . Register ( taskCounterVec ) ; err != nil {
2014-05-07 22:08:33 +04:00
fmt . Println ( "taskCounterVec not registered:" , err )
} else {
fmt . Println ( "taskCounterVec registered." )
}
// To fix, first unregister the old taskCounter.
if prometheus . Unregister ( taskCounter ) {
fmt . Println ( "taskCounter unregistered." )
}
// Try registering taskCounterVec again.
Allow error reporting during metrics collection and simplify Register().
Both are interface changes I want to get in before public
announcement. They only break rare usage cases, and are always easy to
fix, but still we want to avoid breaking changes after a wider
announcement of the project.
The change of Register() simply removes the return of the Collector,
which nobody was using in practice. It was just bloating the call
syntax. Note that this is different from RegisterOrGet(), which is
used at various occasions where you want to register something that
might or might not be registered already, but if it is, you want the
previously registered Collector back (because that's the relevant
one).
WRT error reporting: I first tried the obvious way of letting the
Collector methods Describe() and Collect() return error. However, I
had to conclude that that bloated _many_ calls and their handling in
very obnoxious ways. On the other hand, the case where you actually
want to report errors during registration or collection is very
rare. Hence, this approach has the wrong trade-off. The approach taken
here might at first appear clunky but is in practice quite handy,
mostly because there is almost no change for the "normal" case of "no
special error handling", but also because it plays well with the way
descriptors and metrics are handled (via channels).
Explaining the approach in more detail:
- During registration / describe: Error handling was actually already
in place (for invalid descriptors, which carry an error anyway). I
only added a convenience function to create an invalid descriptor
with a given error on purpose.
- Metrics are now treated in a similar way. The Write method returns
an error now (the only change in interface). An "invalid metric" is
provided that can be sent via the channel to signal that that metric
could not be collected. It alse transports an error.
NON-GOALS OF THIS COMMIT:
This is NOT yet the major improvement of the whole registry part,
where we want a public Registry interface and plenty of modular
configurations (for error handling, various auto-metrics, http
instrumentation, testing, ...). However, we can do that whole thing
without breaking existing interfaces. For now (which is a significant
issue) any error during collection will either cause a 500 HTTP
response or a panic (depending on registry config). Later, we
definitely want to have a possibility to skip (and only report
somehow) non-collectible metrics instead of aborting the whole scrape.
2015-01-12 21:16:09 +03:00
if err := prometheus . Register ( taskCounterVec ) ; err != nil {
2014-05-07 22:08:33 +04:00
fmt . Println ( "taskCounterVec not registered:" , err )
} else {
fmt . Println ( "taskCounterVec registered." )
}
// Bummer! Still doesn't work.
// Prometheus will not allow you to ever export metrics with
// inconsistent help strings or label names. After unregistering, the
2015-02-19 17:34:04 +03:00
// unregistered metrics will cease to show up in the /metrics HTTP
2014-05-07 22:08:33 +04:00
// response, but the registry still remembers that those metrics had
// been exported before. For this example, we will now choose a
// different name. (In a real program, you would obviously not export
// the obsolete metric in the first place.)
taskCounterVec = prometheus . NewCounterVec (
prometheus . CounterOpts {
Subsystem : "worker_pool" ,
Name : "completed_tasks_by_id" ,
Help : "Total number of tasks completed." ,
} ,
[ ] string { "worker_id" } ,
)
Allow error reporting during metrics collection and simplify Register().
Both are interface changes I want to get in before public
announcement. They only break rare usage cases, and are always easy to
fix, but still we want to avoid breaking changes after a wider
announcement of the project.
The change of Register() simply removes the return of the Collector,
which nobody was using in practice. It was just bloating the call
syntax. Note that this is different from RegisterOrGet(), which is
used at various occasions where you want to register something that
might or might not be registered already, but if it is, you want the
previously registered Collector back (because that's the relevant
one).
WRT error reporting: I first tried the obvious way of letting the
Collector methods Describe() and Collect() return error. However, I
had to conclude that that bloated _many_ calls and their handling in
very obnoxious ways. On the other hand, the case where you actually
want to report errors during registration or collection is very
rare. Hence, this approach has the wrong trade-off. The approach taken
here might at first appear clunky but is in practice quite handy,
mostly because there is almost no change for the "normal" case of "no
special error handling", but also because it plays well with the way
descriptors and metrics are handled (via channels).
Explaining the approach in more detail:
- During registration / describe: Error handling was actually already
in place (for invalid descriptors, which carry an error anyway). I
only added a convenience function to create an invalid descriptor
with a given error on purpose.
- Metrics are now treated in a similar way. The Write method returns
an error now (the only change in interface). An "invalid metric" is
provided that can be sent via the channel to signal that that metric
could not be collected. It alse transports an error.
NON-GOALS OF THIS COMMIT:
This is NOT yet the major improvement of the whole registry part,
where we want a public Registry interface and plenty of modular
configurations (for error handling, various auto-metrics, http
instrumentation, testing, ...). However, we can do that whole thing
without breaking existing interfaces. For now (which is a significant
issue) any error during collection will either cause a 500 HTTP
response or a panic (depending on registry config). Later, we
definitely want to have a possibility to skip (and only report
somehow) non-collectible metrics instead of aborting the whole scrape.
2015-01-12 21:16:09 +03:00
if err := prometheus . Register ( taskCounterVec ) ; err != nil {
2014-05-07 22:08:33 +04:00
fmt . Println ( "taskCounterVec not registered:" , err )
} else {
fmt . Println ( "taskCounterVec registered." )
}
// Finally it worked!
// The workers have to tell taskCounterVec their id to increment the
// right element in the metric vector.
taskCounterVec . WithLabelValues ( "42" ) . Inc ( ) // Code from worker 42.
// Each worker could also keep a reference to their own counter element
// around. Pick the counter at initialization time of the worker.
myCounter := taskCounterVec . WithLabelValues ( "42" ) // From worker 42 initialization code.
myCounter . Inc ( ) // Somewhere in the code of that worker.
// Note that something like WithLabelValues("42", "spurious arg") would
// panic (because you have provided too many label values). If you want
// to get an error instead, use GetMetricWithLabelValues(...) instead.
notMyCounter , err := taskCounterVec . GetMetricWithLabelValues ( "42" , "spurious arg" )
if err != nil {
fmt . Println ( "Worker initialization failed:" , err )
}
if notMyCounter == nil {
fmt . Println ( "notMyCounter is nil." )
}
// A different (and somewhat tricky) approach is to use
// ConstLabels. ConstLabels are pairs of label names and label values
// that never change. You might ask what those labels are good for (and
// rightfully so - if they never change, they could as well be part of
// the metric name). There are essentially two use-cases: The first is
// if labels are constant throughout the lifetime of a binary execution,
// but they vary over time or between different instances of a running
// binary. The second is what we have here: Each worker creates and
// registers an own Counter instance where the only difference is in the
// value of the ConstLabels. Those Counters can all be registered
// because the different ConstLabel values guarantee that each worker
// will increment a different Counter metric.
counterOpts := prometheus . CounterOpts {
Subsystem : "worker_pool" ,
Name : "completed_tasks" ,
Help : "Total number of tasks completed." ,
ConstLabels : prometheus . Labels { "worker_id" : "42" } ,
}
taskCounterForWorker42 := prometheus . NewCounter ( counterOpts )
Allow error reporting during metrics collection and simplify Register().
Both are interface changes I want to get in before public
announcement. They only break rare usage cases, and are always easy to
fix, but still we want to avoid breaking changes after a wider
announcement of the project.
The change of Register() simply removes the return of the Collector,
which nobody was using in practice. It was just bloating the call
syntax. Note that this is different from RegisterOrGet(), which is
used at various occasions where you want to register something that
might or might not be registered already, but if it is, you want the
previously registered Collector back (because that's the relevant
one).
WRT error reporting: I first tried the obvious way of letting the
Collector methods Describe() and Collect() return error. However, I
had to conclude that that bloated _many_ calls and their handling in
very obnoxious ways. On the other hand, the case where you actually
want to report errors during registration or collection is very
rare. Hence, this approach has the wrong trade-off. The approach taken
here might at first appear clunky but is in practice quite handy,
mostly because there is almost no change for the "normal" case of "no
special error handling", but also because it plays well with the way
descriptors and metrics are handled (via channels).
Explaining the approach in more detail:
- During registration / describe: Error handling was actually already
in place (for invalid descriptors, which carry an error anyway). I
only added a convenience function to create an invalid descriptor
with a given error on purpose.
- Metrics are now treated in a similar way. The Write method returns
an error now (the only change in interface). An "invalid metric" is
provided that can be sent via the channel to signal that that metric
could not be collected. It alse transports an error.
NON-GOALS OF THIS COMMIT:
This is NOT yet the major improvement of the whole registry part,
where we want a public Registry interface and plenty of modular
configurations (for error handling, various auto-metrics, http
instrumentation, testing, ...). However, we can do that whole thing
without breaking existing interfaces. For now (which is a significant
issue) any error during collection will either cause a 500 HTTP
response or a panic (depending on registry config). Later, we
definitely want to have a possibility to skip (and only report
somehow) non-collectible metrics instead of aborting the whole scrape.
2015-01-12 21:16:09 +03:00
if err := prometheus . Register ( taskCounterForWorker42 ) ; err != nil {
2014-05-07 22:08:33 +04:00
fmt . Println ( "taskCounterVForWorker42 not registered:" , err )
} else {
fmt . Println ( "taskCounterForWorker42 registered." )
}
// Obviously, in real code, taskCounterForWorker42 would be a member
// variable of a worker struct, and the "42" would be retrieved with a
// GetId() method or something. The Counter would be created and
// registered in the initialization code of the worker.
// For the creation of the next Counter, we can recycle
// counterOpts. Just change the ConstLabels.
counterOpts . ConstLabels = prometheus . Labels { "worker_id" : "2001" }
taskCounterForWorker2001 := prometheus . NewCounter ( counterOpts )
Allow error reporting during metrics collection and simplify Register().
Both are interface changes I want to get in before public
announcement. They only break rare usage cases, and are always easy to
fix, but still we want to avoid breaking changes after a wider
announcement of the project.
The change of Register() simply removes the return of the Collector,
which nobody was using in practice. It was just bloating the call
syntax. Note that this is different from RegisterOrGet(), which is
used at various occasions where you want to register something that
might or might not be registered already, but if it is, you want the
previously registered Collector back (because that's the relevant
one).
WRT error reporting: I first tried the obvious way of letting the
Collector methods Describe() and Collect() return error. However, I
had to conclude that that bloated _many_ calls and their handling in
very obnoxious ways. On the other hand, the case where you actually
want to report errors during registration or collection is very
rare. Hence, this approach has the wrong trade-off. The approach taken
here might at first appear clunky but is in practice quite handy,
mostly because there is almost no change for the "normal" case of "no
special error handling", but also because it plays well with the way
descriptors and metrics are handled (via channels).
Explaining the approach in more detail:
- During registration / describe: Error handling was actually already
in place (for invalid descriptors, which carry an error anyway). I
only added a convenience function to create an invalid descriptor
with a given error on purpose.
- Metrics are now treated in a similar way. The Write method returns
an error now (the only change in interface). An "invalid metric" is
provided that can be sent via the channel to signal that that metric
could not be collected. It alse transports an error.
NON-GOALS OF THIS COMMIT:
This is NOT yet the major improvement of the whole registry part,
where we want a public Registry interface and plenty of modular
configurations (for error handling, various auto-metrics, http
instrumentation, testing, ...). However, we can do that whole thing
without breaking existing interfaces. For now (which is a significant
issue) any error during collection will either cause a 500 HTTP
response or a panic (depending on registry config). Later, we
definitely want to have a possibility to skip (and only report
somehow) non-collectible metrics instead of aborting the whole scrape.
2015-01-12 21:16:09 +03:00
if err := prometheus . Register ( taskCounterForWorker2001 ) ; err != nil {
2014-05-07 22:08:33 +04:00
fmt . Println ( "taskCounterVForWorker2001 not registered:" , err )
} else {
fmt . Println ( "taskCounterForWorker2001 registered." )
}
taskCounterForWorker2001 . Inc ( )
taskCounterForWorker42 . Inc ( )
taskCounterForWorker2001 . Inc ( )
// Yet another approach would be to turn the workers themselves into
// Collectors and register them. See the Collector example for details.
// Output:
// taskCounter registered.
// taskCounterVec not registered: a previously registered descriptor with the same fully-qualified name as Desc{fqName: "worker_pool_completed_tasks_total", help: "Total number of tasks completed.", constLabels: {}, variableLabels: [worker_id]} has different label names or a different help string
// taskCounter unregistered.
// taskCounterVec not registered: a previously registered descriptor with the same fully-qualified name as Desc{fqName: "worker_pool_completed_tasks_total", help: "Total number of tasks completed.", constLabels: {}, variableLabels: [worker_id]} has different label names or a different help string
// taskCounterVec registered.
// Worker initialization failed: inconsistent label cardinality
// notMyCounter is nil.
// taskCounterForWorker42 registered.
// taskCounterForWorker2001 registered.
}
func ExampleSummary ( ) {
temps := prometheus . NewSummary ( prometheus . SummaryOpts {
Name : "pond_temperature_celsius" ,
Help : "The temperature of the frog pond." , // Sorry, we can't measure how badly it smells.
} )
// Simulate some observations.
for i := 0 ; i < 1000 ; i ++ {
temps . Observe ( 30 + math . Floor ( 120 * math . Sin ( float64 ( i ) * 0.1 ) ) / 10 )
}
// Just for demonstration, let's check the state of the summary by
// (ab)using its Write method (which is usually only used by Prometheus
// internally).
metric := & dto . Metric { }
temps . Write ( metric )
fmt . Println ( proto . MarshalTextString ( metric ) )
// Output:
// summary: <
// sample_count: 1000
// sample_sum: 29969.50000000001
// quantile: <
// quantile: 0.5
2015-01-21 15:44:43 +03:00
// value: 31.1
2014-05-07 22:08:33 +04:00
// >
// quantile: <
// quantile: 0.9
2015-01-21 15:44:43 +03:00
// value: 41.3
2014-05-07 22:08:33 +04:00
// >
// quantile: <
// quantile: 0.99
// value: 41.9
// >
// >
}
func ExampleSummaryVec ( ) {
temps := prometheus . NewSummaryVec (
prometheus . SummaryOpts {
Name : "pond_temperature_celsius" ,
Help : "The temperature of the frog pond." , // Sorry, we can't measure how badly it smells.
} ,
[ ] string { "species" } ,
)
// Simulate some observations.
for i := 0 ; i < 1000 ; i ++ {
temps . WithLabelValues ( "litoria-caerulea" ) . Observe ( 30 + math . Floor ( 120 * math . Sin ( float64 ( i ) * 0.1 ) ) / 10 )
temps . WithLabelValues ( "lithobates-catesbeianus" ) . Observe ( 32 + math . Floor ( 100 * math . Cos ( float64 ( i ) * 0.11 ) ) / 10 )
}
// Just for demonstration, let's check the state of the summary vector
// by (ab)using its Collect method and the Write method of its elements
// (which is usually only used by Prometheus internally - code like the
// following will never appear in your own code).
metricChan := make ( chan prometheus . Metric )
go func ( ) {
defer close ( metricChan )
temps . Collect ( metricChan )
} ( )
metricStrings := [ ] string { }
for metric := range metricChan {
dtoMetric := & dto . Metric { }
metric . Write ( dtoMetric )
metricStrings = append ( metricStrings , proto . MarshalTextString ( dtoMetric ) )
}
sort . Strings ( metricStrings ) // For reproducible print order.
fmt . Println ( metricStrings )
// Output:
// [label: <
// name: "species"
// value: "lithobates-catesbeianus"
// >
// summary: <
// sample_count: 1000
// sample_sum: 31956.100000000017
// quantile: <
// quantile: 0.5
2015-01-21 15:44:43 +03:00
// value: 32.4
2014-05-07 22:08:33 +04:00
// >
// quantile: <
// quantile: 0.9
2015-01-21 15:44:43 +03:00
// value: 41.4
2014-05-07 22:08:33 +04:00
// >
// quantile: <
// quantile: 0.99
// value: 41.9
// >
// >
// label: <
// name: "species"
// value: "litoria-caerulea"
// >
// summary: <
// sample_count: 1000
// sample_sum: 29969.50000000001
// quantile: <
// quantile: 0.5
2015-01-21 15:44:43 +03:00
// value: 31.1
2014-05-07 22:08:33 +04:00
// >
// quantile: <
// quantile: 0.9
2015-01-21 15:44:43 +03:00
// value: 41.3
2014-05-07 22:08:33 +04:00
// >
// quantile: <
// quantile: 0.99
// value: 41.9
// >
// >
// ]
}
2015-02-18 21:23:34 +03:00
func ExampleHistogram ( ) {
temps := prometheus . NewHistogram ( prometheus . HistogramOpts {
Name : "pond_temperature_celsius" ,
Help : "The temperature of the frog pond." , // Sorry, we can't measure how badly it smells.
2015-02-19 14:40:29 +03:00
Buckets : prometheus . LinearBuckets ( 20 , 5 , 5 ) , // 5 buckets, each 5 centigrade wide.
2015-02-18 21:23:34 +03:00
} )
// Simulate some observations.
for i := 0 ; i < 1000 ; i ++ {
temps . Observe ( 30 + math . Floor ( 120 * math . Sin ( float64 ( i ) * 0.1 ) ) / 10 )
}
// Just for demonstration, let's check the state of the histogram by
// (ab)using its Write method (which is usually only used by Prometheus
// internally).
metric := & dto . Metric { }
temps . Write ( metric )
fmt . Println ( proto . MarshalTextString ( metric ) )
// Output:
// histogram: <
// sample_count: 1000
// sample_sum: 29969.50000000001
// bucket: <
// cumulative_count: 192
// upper_bound: 20
// >
// bucket: <
// cumulative_count: 366
// upper_bound: 25
// >
// bucket: <
// cumulative_count: 501
// upper_bound: 30
// >
// bucket: <
// cumulative_count: 638
// upper_bound: 35
// >
// bucket: <
// cumulative_count: 816
// upper_bound: 40
// >
// >
}