Metrics is a tool to collect, manipulate metrics timeseriess.
Metrics uses a collection of primitives borrowed from Prometheus TSDB, which can be exported to any TSDB.
cd ${PROJECT_ROOT}
tarantoolctl rocks install metrics
In order to easily export metrics to any TSDB you can use one of supported export plugins:
or you can write your custom plugin and use it. Hopefully, plugins for other TSDBs will be supported soon.
Below are the examples of using metrics primitives.
Note that this usage is independent of export-plugins such as Prometheus / Graphite / etc.
For documentation on plugins usage go to their respective README under metrics/plugins/<name>/README.md
.
Using counters:
local metrics = require('metrics')
-- create counter
local http_requests_total_counter = metrics.counter('http_requests_total')
-- somewhere in HTTP requests middleware:
http_requests_total_counter:inc(1, {method = 'GET'})
Using gauges:
local metrics = require('metrics')
-- create gauge
local cpu_usage_gauge = metrics.gauge('cpu_usage', 'CPU usage')
-- register a lazy gauge value update
-- this will be called whenever the export is invoked in any plugins.
metrics.register_callback(function()
local current_cpu_usage = math.random()
cpu_usage_gauge:set(current_cpu_usage, {app = 'tarantool'})
end)
Using histograms:
local metrics = require('metrics')
-- create histogram
local http_requests_latency_hist = metrics.histogram(
'http_requests_latency', 'HTTP requests total', {2, 4, 6})
-- somewhere in HTTP requests middleware:
local latency = math.random(1, 10)
http_requests_latency_hist:observe(latency)
The application using metrics
module has 3 primitives (called collectors) at its disposal:
- Counter
- Gauge
- Histogram
Collectors represent an observation or a few that are changing over time.
All collectors support providing label_pairs
on data modification.
Labels are basically a metainfo you associate with a metric in format of key-value pairs.
See tags in Graphite and labels in Prometheus.
Labels are used to differentiate the characteristics of a thing being measured. For example, in metric associated with http total # of requests you can use methods and statuses label pairs:
http_requests_total_counter:inc(1, {method = 'POST', status = '200'})
You don't have to predefine a set of labels in advance.
Using labels on your metrics allows you to later derive new timeserieses (visualize their graphs) by specifying conditions on label values. In above example, we could derive a timeserieses:
- total # of requests over time with method = "POST" (and any status).
- total # of requests over time with status = 500 (and any method).
You can also set global labels by calling metrics.set_global_labels({ label = value, ...})
.
-- importing metrics
metrics = require('metrics')
Enables default metrics collections. Collects tarantool metrics, ported from https://github.com/tarantool/stat
Set global labels that will be added to every observation.
label_pairs
Table containing label names as string keys, label values as values (table).
Global labels applied only on metrics collect and have no effect on observations' storage.
Global labels can be changed along the way.
Observation label_pairs
are prior to global labels: if you pass label_pairs
to observation method with the same key as some global label, the method argument value will be used.
Registers a function callback
which will be called right
before metrics collection on plugin export.
callback
Function which takes no parameters.
Most common usage is for gauge metrics updates.
Registers a new counter. Returns Counter object.
name
Collector name (string). Must be unique.help
(optional) Help description (string).
Increments observation under label_pairs
. If label_pairs
didn't exist before - this creates it.
num
Increase value (number).label_pairs
Table containing label names as keys, label values as values (table).
Returns array of observation
objects for given counter.
observation
is a Lua table:
{
label_pairs: table, -- `label_pairs` key-value table
timestamp: ctype<uint64_t>, -- current system time (in microseconds)
value: number, -- current value
metric_name: string, -- collector
}
Registers a new gauge. Returns Gauge object.
name
Collector name (string). Must be unique.help
(optional) Help description (string).
Same as Counter inc()
.
Same as inc()
, but decreases the observation.
Same as inc()
, but sets the observation.
Returns array of observation
objects for given gauge.
For observation
description see counter_obj:collect()
section.
Registers a new histogram. Returns Histogram object.
name
Collector name (string). Must be unique.help
(optional) Help description (string).buckets
Histogram buckets (array of positive sorted numbers).INF
bucket is added automatically. Default is {.005, .01, .025, .05, .075, .1, .25, .5, .75, 1.0, 2.5, 5.0, 7.5, 10.0, INF}.
NOTE: The histogram is just a set of collectors:
name .. "_sum"
- Counter holding sum of added observations. Has only empty labelset.name .. "_count"
- Counter holding number of added observations. Has only empty labelset.name .. "_bucket"
- Counter holding all bucket sizes under labelle
(low or equal). So to access specific bucketx
(x
is a number), you should specify valuex
for labelle
.
Records a new value in histogram. This increments all buckets sizes under labels le
>= num
and labels matching label_pairs
.
num
Value to put in histogram (number).label_pairs
Table containing label names as keys, label values as values (table). New value is observed by all internal counters with these labels specified.
Returns concatenation of counter_obj:collect()
across all internal counters
of histogram_obj
.
For observation
description see counter_obj:collect()
section.
Can be used only as HTTP statistics collector (described below) and cannot be built explicitly.
Returns list of two observations:
name .. "_avg"
- average value of observations for the observing period (time from previous collect call to now),name .. "_count"
- observation count for the same period. Forobservation
description seecounter_obj:collect()
section.
metrics
also provides a middleware for monitoring HTTP (set by http module) latency statistics.
-- importing submodule
http_middleware = metrics.http_middleware
Registers collector for middleware and sets it as default.
type_name
Collector type (string): "histogram" or "average". Default is "histogram".name
Collector name (string). Default is "http_server_request_latency".help
(optional) Help description (string). Default is "HTTP Server Request Latency".
If collector with the same type and name already exists in registry, throws an error.
Registers collector for middleware and returns it.
type_name
Collector type (string): "histogram" or "average". Default is "histogram".name
Collector name (string). Default is "http_server_request_latency".help
(optional) Help description (string). Default is "HTTP Server Request Latency".
If collector with the same type and name already exists in registry, throws an error.
Sets default collector.
collector
Middleware collector object.
Returns default collector.
If default collector hasn't been set yet, registers it (with default http_middleware.build_default_collector(...)
parameters) and sets it as default.
Latency measure wrap-up for HTTP ver. 1.x.x handler. Returns wrapped handler.
handler
Handler function.collector
Middleware collector object. If not set, uses default collector (like inhttp_middleware.get_default_collector()
).
Usage:
httpd:route(route, http_middleware.v1(request_handler, collector))
For more detailed example see example/HTTP/latency_v1.lua.
Returns latency measure middleware for HTTP ver. 2.x.x.
collector
Middleware collector object. If not set, uses default collector (like inhttp_middleware.get_default_collector()
).
Usage:
router = require('http.router').new()
router:route(route, request_handler)
router:use(http_middleware.v2(collector), {name = 'http_instrumentation'}) -- Second argument is optional, see HTTP docs
For more detailed example see example/HTTP/latency_v2.lua.
Feel free to send Pull Requests. E.g. you can support new timeseries aggregation / manipulation functions (but be sure to check if there are any Prometheus analogues to borrow API from).
We would like to thank Prometheus for a great API that we brusquely borrowed.