<dependency> <groupId>io.prometheusgroupId> <artifactId>simpleclientartifactId> <version>0.6.0version> dependency> <dependency> <groupId>io.prometheusgroupId> <artifactId>simpleclient_hotspotartifactId> <version>0.6.0version> dependency> <dependency> <groupId>io.prometheusgroupId> <artifactId>simpleclient_httpserverartifactId> <version>0.6.0version> dependency> <dependency> <groupId>io.prometheusgroupId> <artifactId>simpleclient_pushgatewayartifactId> <version>0.6.0version> dependency>
Counter
Counters go up, and reset when the process restarts.
import io.prometheus.client.Counter;
class YourClass {
static final Counter requests = Counter.build()
.name("requests_total").help("Total requests.").register();
void processRequest() {
requests.inc();
// Your code here.
}
}
Gauge
Gauges can go up and down.
class YourClass {
static final Gauge inprogressRequests = Gauge.build()
.name("inprogress_requests").help("Inprogress requests.").register();
void processRequest() {
inprogressRequests.inc();
// Your code here.
inprogressRequests.dec();
}
}
There are utilities for common use cases:
gauge.setToCurrentTime(); // Set to current unixtime.
As an advanced use case, a Gauge
can also take its value from a callback by using the setChild() method. Keep in mind that the default inc()
, dec()
and set()
methods on Gauge take care of thread safety, so when using this approach ensure the value you are reporting accounts for concurrency.
Summary
Summaries track the size and number of events.
class YourClass {
static final Summary receivedBytes = Summary.build()
.name("requests_size_bytes").help("Request size in bytes.").register();
static final Summary requestLatency = Summary.build()
.name("requests_latency_seconds").help("Request latency in seconds.").register();
void processRequest(Request req) {
Summary.Timer requestTimer = requestLatency.startTimer();
try {
// Your code here.
} finally {
receivedBytes.observe(req.size());
requestTimer.observeDuration();
}
}
}
There are utilities for timing code and support for quantiles. Essentially quantiles aren’t aggregatable and add some client overhead for the calculation.
class YourClass {
static final Summary requestLatency = Summary.build()
.quantile(0.5, 0.05) // Add 50th percentile (= median) with 5% tolerated error
.quantile(0.9, 0.01) // Add 90th percentile with 1% tolerated error
.name("requests_latency_seconds").help("Request latency in seconds.").register();
void processRequest(Request req) {
requestLatency.time(new Runnable() {
public abstract void run() {
// Your code here.
}
});
// Or the Java 8 lambda equivalent
requestLatency.time(() -> {
// Your code here.
});
}
}
Histogram
Histograms track the size and number of events in buckets. This allows for aggregatable calculation of quantiles.
class YourClass {
static final Histogram requestLatency = Histogram.build()
.name("requests_latency_seconds").help("Request latency in seconds.").register();
void processRequest(Request req) {
Histogram.Timer requestTimer = requestLatency.startTimer();
try {
// Your code here.
} finally {
requestTimer.observeDuration();
}
}
}
The default buckets are intended to cover a typical web/rpc request from milliseconds to seconds. They can be overridden with the buckets()
method on the Histogram.Builder.
There are utilities for timing code:
class YourClass {
static final Histogram requestLatency = Histogram.build()
.name("requests_latency_seconds").help("Request latency in seconds.").register();
void processRequest(Request req) {
requestLatency.time(new Runnable() {
public abstract void run() {
// Your code here.
}
});
// Or the Java 8 lambda equivalent
requestLatency.time(() -> {
// Your code here.
});
}
}
Labels
All metrics can have labels, allowing grouping of related time series.
See the best practices on naming and labels.
Taking a counter as an example:
class YourClass {
static final Counter requests = Counter.build()
.name("my_library_requests_total").help("Total requests.")
.labelNames("method").register();
void processGetRequest() {
requests.labels("get").inc();
// Your code here.
}
}
Registering Metrics
The best way to register a metric is via a static final
class variable as is common with loggers.
static final Counter requests = Counter.build()
.name("my_library_requests_total").help("Total requests.").labelNames("path").register();
Using the default registry with variables that are static
is ideal since registering a metric with the same name is not allowed and the default registry is also itself static. You can think of registering a metric, more like registering a definition (as in the TYPE
and HELP
sections). The metric ‘definition’ internally holds the samples that are reported and pulled out by Prometheus. Here is an example of registering a metric that has no labels.
class YourClass {
static final Gauge activeTransactions = Gauge.build()
.name("my_library_transactions_active")
.help("Active transactions.")
.register();
void processThatCalculates(String key) {
activeTransactions.inc();
try {
// Perform work.
} finally{
activeTransactions.dec();
}
}
}
To create timeseries with labels, include labelNames()
with the builder. The labels()
method looks up or creates the corresponding labelled timeseries. You might also consider storing the labelled timeseries as an instance variable if it is appropriate. It is thread safe and can be used multiple times, which can help performance.
class YourClass {
static final Counter calculationsCounter = Counter.build()
.name("my_library_calculations_total").help("Total calls.")
.labelNames("key").register();
void processThatCalculates(String key) {
calculationsCounter.labels(key).inc();
// Run calculations.
}
}