1
0
mirror of https://github.com/Refactorio/RedMew.git synced 2024-12-12 10:04:40 +02:00

Testing prometheus/grafana

We've tested it works on S10 but want it on a live map for a little while so we can gather some data for testing configuring Grafana.

For now it just saves the number of players, will add more later.
This commit is contained in:
Jayefuu 2021-01-24 12:06:29 +00:00
parent 164baaa523
commit f1ec322481
11 changed files with 1082 additions and 1 deletions

40
features/prometheus/.gitignore vendored Normal file
View File

@ -0,0 +1,40 @@
# Compiled Lua sources
luac.out
# luarocks build files
*.src.rock
*.zip
*.tar.gz
# Object files
*.o
*.os
*.ko
*.obj
*.elf
# Precompiled Headers
*.gch
*.pch
# Libraries
*.lib
*.a
*.la
*.lo
*.def
*.exp
# Shared objects (inc. Windows DLLs)
*.dll
*.so
*.so.*
*.dylib
# Executables
*.exe
*.out
*.app
*.i*86
*.x86_64
*.hex

View File

@ -0,0 +1,4 @@
FROM tarantool/tarantool:1.7
COPY example.lua /opt/tarantool/
CMD ["tarantool", "/opt/tarantool/example.lua"]

View File

@ -0,0 +1,29 @@
Copyright (C) 2014-2016 Tarantool AUTHORS:
please see AUTHORS file in tarantool/tarantool repository.
Redistribution and use in source and binary forms, with or
without modification, are permitted provided that the following
conditions are met:
1. Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
2. Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials
provided with the distribution.
THIS SOFTWARE IS PROVIDED BY AUTHORS ``AS IS'' AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.

View File

@ -0,0 +1,257 @@
<a href="http://tarantool.org">
<img src="https://avatars2.githubusercontent.com/u/2344919?v=2&s=250" align="right">
</a>
<!--a href="https://travis-ci.org/tarantool/tarantool-prometheus">
<img src="https://travis-ci.org/tarantool/tarantool-prometheus.png?branch=master" align="right">
</a-->
# Prometheus metric collector for Tarantool
This is a Lua library that makes it easy to collect metrics from your Tarantool
apps and databases and expose them via the Prometheus protocol. You may use the
library to instrument your code and get an insight into performance bottlenecks.
At the moment, 3 types of metrics are supported:
* Counter: a non-decreasing numeric value, used e.g. for counting the number of
requests
* Gauge: an arbitrary numeric value, which can be used e.g. to report memory
usage
* Histogram: for counting value distribution by user-specified buckets. Can be
used for recording request/response times.
## Table of contents
* [Limitations](#limitations)
* [Getting started](#getting-started)
* [Basic examples](#basic-examples)
* [A more detailed example](#a-more-detailed-example)
* [Usage](#usage)
* [counter(name, help, labels)](#countername-help-labels)
* [gauge(name, help, labels)](#gaugename-help-labels)
* [histogram(name, help, labels, buckets)](#histogramname-help-labels-buckets)
* [Counter:inc(value, labels)](#counterincvalue-labels)
* [Gauge:set(value, labels)](#gaugesetvalue-labels)
* [Gauge:inc(value, labels)](#gaugeincvalue-labels)
* [Gauge:dec(value, labels)](#gaugedecvalue-labels)
* [Histogram:observe(value, labels)](#histogramobservevalue-labels)
* [collect()](#collect)
* [collect\_http()](#collect_http)
* [Development](#development)
* [Credits](#credits)
* [License](#license)
## Limitations
The Summary metric is not implemented yet. It may be implemented in future.
## Getting started
The easiest way is, of course, to use
[one of the official Docker images](https://hub.docker.com/r/tarantool/tarantool/),
which already contain the Prometheus collector. But if you run on a regular
Linux distro, first install the library from
[Tarantool Rocks server](http://rocks.tarantool.org):
```bash
$ luarocks install tarantool-prometheus
```
### Basic examples
To report the arena size, you can write the following code:
```lua
prometheus = require('tarantool-prometheus')
fiber = require('fiber')
box.cfg{}
httpd = http.new('0.0.0.0', 8080)
arena_used = prometheus.gauge("tarantool_arena_used",
"The amount of arena used by Tarantool")
function monitor_arena_size()
while true do
arena_used:set(box.slab.info().arena_used)
fiber.sleep(5)
end
end
fiber.create(monitor_arena_size)
httpd:route( { path = '/metrics' }, prometheus.collect_http)
httpd:start()
```
The code will periodically measure the arena size and update the `arena_used`
metric. Later, when Prometheus polls the instance, it will get the values of all
metrics the instance created.
There are 3 important bits in the code above:
```lua
arena_used = prometheus.gauge(...)
```
This creates a [Gauge](https://prometheus.io/docs/concepts/metric_types/#gauge)
object that can be set to an arbitrary numeric value. After this, the metric from
this object will be automatically collected by Prometheus every time metrics are
polled.
```lua
arena_used:set(...)
```
This sets the current value of the metric.
```lua
httpd:route( { path = '/metrics' }, prometheus.collect_http)
```
This exposes metrics over the text/plain HTTP protocol on
[http://localhost:8080/metrics](http://localhost:8080/metrics) for Prometheus to
collect. Prometheus periodically polls this endpoint and stores the results in
its time series database.
### A more detailed example
If you want a more detailed example, there is an `example.lua` file in the root
of this repo. It demonstrates the usage of each of the 3 metric types.
To run it with Docker, you can do as follows:
``` bash
$ docker build -t tarantool-prometheus .
$ docker run --rm -t -i -p8080:8080 tarantool-prometheus
```
Then visit [http://localhost:8080/metrics](http://localhost:8080/metrics) and
refresh the page a few times to see the metrics change.
## Usage
This section documents the user-facing API of the module.
### counter(name, help, labels)
Creates and registers a [Counter](https://prometheus.io/docs/concepts/metric_types/#counter).
* `name` is the name of the metric. Required.
* `help` is the metric docstring. You can use newlines and quotes here. Optional.
* `labels` is an array of label names for the metric. Optional.
Example:
```lua
num_of_logins = prometheus.counter(
"tarantool_number_of_logins", "Total number of user logins")
http_requests = prometheus:counter(
"tarantool_http_requests_total", "Number of HTTP requests", {"host", "status"})
```
### gauge(name, help, labels)
Creates and registers a [Gauge](https://prometheus.io/docs/concepts/metric_types/#gauge).
* `name` is the name of the metric. Required.
* `help` is the metric docstring. You can use newlines and quotes here. Optional.
* `labels` is an array of label names for the metric. Optional.
Example:
``` lua
arena_used = prometheus.gauge(
"tarantool_arena_used_size", "Total size of the arena used")
requests_inprogress = prometheus.gauge(
"tarantool_requests_inprogress", "Number of requests in progress", {"request_type"})
```
### histogram(name, help, labels, buckets)
Creates and registers a [Histogram](https://prometheus.io/docs/concepts/metric_types/#histogram).
* `name` is the name of the metric. Required.
* `help` is the metric docstring. You can use newlines and quotes here. Optional.
* `labels` is an array of label names for the metric. Optional.
* `buckets` is an array of numbers defining histogram buckets. Optional. Defaults to
`{.005, .01, .025, .05, .075, .1, .25, .5, .75, 1.0, 2.5, 5.0, 7.5, 10.0, INF}`.
Example:
``` lua
request_latency = prometheus.histogram(
"tarantool_request_latency_seconds", "Incoming request latency", {"client"})
response_size = prometheus.histogram(
"tarantool_response_size", "Size of response, in bytes", nil, {100, 1000, 100000})
```
### Counter:inc(value, labels)
Increments a counter created by `prometheus.counter()`.
* `value` specifies by how much to increment. Optional. Defaults to `1`.
* `labels` is an array of label values. Optional.
### Gauge:set(value, labels)
Sets a value of a gauge created by `prometheus.gauge()`.
* `value` is the value to set. Optional. Defaults to `0`.
* `labels` is an array of label values. Optional.
### Gauge:inc(value, labels)
Increments a gauge created by `prometheus.gauge()`.
* `value` specifies by how much to increment. Optional. Defaults to `1`.
* `labels` is an array of label values. Optional.
### Gauge:dec(value, labels)
Decrements a gauge created by `prometheus.gauge()`.
* `value` specifies by how much to decrement. Optional. Defaults to `1`.
* `labels` is an array of label values. Optional.
### Histogram:observe(value, labels)
Records a value to a histogram created by `prometheus.histogram()`.
* `value` is the value to record. Optional. Defaults to `0`.
* `labels` is an array of label values. Optional.
### collect()
Presents all metrics in a text format compatible with Prometheus. This can be
called either by `http.server` callback or by
[Tarantool nginx_upstream_module](https://github.com/tarantool/nginx_upstream_module).
### collect_http()
Convenience function, specially for one-line registration in the Tarantool
`http.server`, as follows:
```lua
httpd:route( { path = '/metrics' }, prometheus.collect_http)
```
## Development
Contributions are welcome. Report issues and feature requests at
https://github.com/tarantool/tarantool-prometheus/issues
To run tests, do:
```bash
$ tarantool test.lua
```
NB: Tests require `luaunit` library.
## Credits
Loosely based on the implementation by @knyar: https://github.com/knyar/nginx-lua-prometheus
## License
Licensed under the BSD license. See the LICENSE file.

View File

@ -0,0 +1,52 @@
#!/usr/bin/env tarantool
http = require('http.server')
prometheus = require('tarantool-prometheus')
fiber = require('fiber')
box.cfg{}
prometheus.init()
httpd = http.new('0.0.0.0', 8080)
space = box.schema.space.create("test_space")
space:create_index('primary', {type = 'hash', parts = {1, 'NUM'}})
function random_write()
num = math.random(10000)
box.space.test_space:truncate()
for i=0,num do
box.space.test_space:insert({i, tostring(i)})
end
end
function worker()
exec_count = prometheus.counter("tarantool_worker_execution_count",
"Number of times worker process has been executed")
exec_time = prometheus.histogram("tarantool_worker_execution_time",
"Time of each worker process execution")
arena_used = prometheus.gauge("tarantool_arena_used",
"The amount of arena used by Tarantool")
while true do
time_start = fiber.time()
random_write()
time_end = fiber.time()
exec_time:observe(time_end - time_start)
exec_count:inc()
arena_used:set(box.slab.info().arena_used)
fiber.sleep(1)
end
end
httpd:route( { path = '/metrics' }, prometheus.collect_http)
httpd:start()
fiber.create(worker)

View File

@ -0,0 +1,79 @@
#!/usr/bin/env tarantool
local prometheus = require('tarantool-prometheus')
local memory_limit_bytes = prometheus.gauge(
'tarantool_memory_limit_bytes',
'Maximum amount of memory Tarantool can use')
local memory_used_bytes = prometheus.gauge(
'tarantool_memory_used_bytes',
'Amount of memory currently used by Tarantool')
local tuples_memory_bytes = prometheus.gauge(
'tarantool_tuples_memory_bytes',
'Amount of memory allocated for Tarantool tuples')
local system_memory_bytes = prometheus.gauge(
'tarantool_system_memory_bytes',
'Amount of memory used by Tarantool indexes and system')
local requests_total = prometheus.gauge(
'tarantool_requests_total',
'Total number of requests by request type',
{'request_type'})
local uptime_seconds = prometheus.gauge(
'tarantool_uptime_seconds',
'Number of seconds since the server started')
local tuples_total = prometheus.gauge(
'tarantool_space_tuples_total',
'Total number of tuples in a space',
{'space_name'})
local function measure_tarantool_memory_usage()
local slabs = box.slab.info()
local memory_limit = slabs.quota_size
local memory_used = slabs.quota_used
local tuples_memory = slabs.arena_used
local system_memory = memory_used - tuples_memory
memory_limit_bytes:set(memory_limit)
memory_used_bytes:set(memory_used)
tuples_memory_bytes:set(tuples_memory)
system_memory_bytes:set(system_memory)
end
local function measure_tarantool_request_stats()
local stat = box.stat()
local request_types = {'delete', 'select', 'insert', 'eval', 'call',
'replace', 'upsert', 'auth', 'error', 'update'}
for _, request_type in ipairs(request_types) do
requests_total:set(stat[string.upper(request_type)].total,
{request_type})
end
end
local function measure_tarantool_uptime()
uptime_seconds:set(box.info.uptime)
end
local function measure_tarantool_space_stats()
for _, space in box.space._space:pairs() do
local space_name = space[3]
if string.sub(space_name, 1,1) ~= '_' then
tuples_total:set(box.space[space_name]:len(), {space_name})
end
end
end
local function measure_tarantool_metrics()
measure_tarantool_memory_usage()
measure_tarantool_request_stats()
measure_tarantool_uptime()
measure_tarantool_space_stats()
end
return {measure_tarantool_metrics=measure_tarantool_metrics}

View File

@ -0,0 +1,22 @@
package = 'tarantool-prometheus'
version = 'scm-1'
source = {
url = 'git://github.com/tarantool/prometheus.git',
branch = 'master',
}
description = {
summary = 'Prometheus library to collect metrics from Tarantool',
homepage = 'https://github.com/tarantool/prometheus.git',
license = 'MIT',
}
dependencies = {
'lua >= 5.1';
}
build = {
type = 'builtin',
modules = {
['tarantool-prometheus.tarantool-metrics'] = 'tarantool-metrics.lua',
['tarantool-prometheus'] = 'tarantool-prometheus.lua'
}
}

View File

@ -0,0 +1,390 @@
-- vim: ts=2:sw=2:sts=2:expandtab
local INF = math.huge
local NAN = math.huge * 0
local DEFAULT_BUCKETS = {.005, .01, .025, .05, .075, .1, .25, .5,
.75, 1.0, 2.5, 5.0, 7.5, 10.0, INF}
local REGISTRY = nil
local Registry = {}
Registry.__index = Registry
function Registry.new()
local obj = {}
setmetatable(obj, Registry)
obj.collectors = {}
obj.callbacks = {}
return obj
end
function Registry:register(collector)
if self.collectors[collector.name]~=nil then
return self.collectors[collector.name]
end
self.collectors[collector.name] = collector
return collector
end
function Registry:unregister(collector)
if self.collectors[collector.name]~=nil then
table.remove(self.collectors, collector.name)
end
end
function Registry:collect()
for _, registered_callback in ipairs(self.callbacks) do
registered_callback()
end
local result = {}
for _, collector in pairs(self.collectors) do
for _, metric in ipairs(collector:collect()) do
table.insert(result, metric)
end
table.insert(result, '')
end
return result
end
function Registry:register_callback(callback)
local found = false
for _, registered_callback in ipairs(self.callbacks) do
if registered_callback == calback then
found = true
end
end
if not found then
table.insert(self.callbacks, callback)
end
end
local function get_registry()
if not REGISTRY then
REGISTRY = Registry.new()
end
return REGISTRY
end
local function register(collector)
local registry = get_registry()
registry:register(collector)
return collector
end
local function register_callback(callback)
local registry = get_registry()
registry:register_callback(callback)
end
function zip(lhs, rhs)
if lhs == nil or rhs == nil then
return {}
end
local len = math.min(#lhs, #rhs)
local result = {}
for i=1,len do
table.insert(result, {lhs[i], rhs[i]})
end
return result
end
local function metric_to_string(value)
if value == INF then
return "+Inf"
elseif value == -INF then
return "-Inf"
elseif value ~= value then
return "Nan"
else
return tostring(value)
end
end
local function escape_string(str)
return str
:gsub("\\", "\\\\")
:gsub("\n", "\\n")
:gsub('"', '\\"')
end
local function labels_to_string(label_pairs)
if #label_pairs == 0 then
return ""
end
local label_parts = {}
for _, label in ipairs(label_pairs) do
local label_name = label[1]
local label_value = label[2]
local label_value_escaped = escape_string(string.format("%s", label_value))
table.insert(label_parts, label_name .. '="' .. label_value_escaped .. '"')
end
return "{" .. table.concat(label_parts, ",") .. "}"
end
local Counter = {}
Counter.__index = Counter
function Counter.new(name, help, labels)
local obj = {}
setmetatable(obj, Counter)
if not name then
error("Name should be set for Counter")
end
obj.name = name
obj.help = help or ""
obj.labels = labels or {}
obj.observations = {}
obj.label_values = {}
return obj
end
function Counter:inc(num, label_values)
local num = num or 1
local label_values = label_values or {}
if num < 0 then
error("Counter increment should not be negative")
end
local key = table.concat(label_values, '\0')
local old_value = self.observations[key] or 0
self.observations[key] = old_value + num
self.label_values[key] = label_values
end
function Counter:collect()
local result = {}
if next(self.observations) == nil then
return {}
end
table.insert(result, '# HELP '..self.name..' '..escape_string(self.help))
table.insert(result, "# TYPE "..self.name.." counter")
for key, observation in pairs(self.observations) do
local label_values = self.label_values[key]
local prefix = self.name
local labels = zip(self.labels, label_values)
local str = prefix..labels_to_string(labels)..
' '..metric_to_string(observation)
table.insert(result, str)
end
return result
end
local Gauge = {}
Gauge.__index = Gauge
function Gauge.new(name, help, labels)
local obj = {}
setmetatable(obj, Gauge)
if not name then
error("Name should be set for Gauge")
end
obj.name = name
obj.help = help or ""
obj.labels = labels or {}
obj.observations = {}
obj.label_values = {}
return obj
end
function Gauge:inc(num, label_values)
local num = num or 1
local label_values = label_values or {}
local key = table.concat(label_values, '\0')
local old_value = self.observations[key] or 0
self.observations[key] = old_value + num
self.label_values[key] = label_values
end
function Gauge:dec(num, label_values)
local num = num or 1
local label_values = label_values or {}
local key = table.concat(label_values, '\0')
local old_value = self.observations[key] or 0
self.observations[key] = old_value - num
self.label_values[key] = label_values
end
function Gauge:set(num, label_values)
local num = num or 0
local label_values = label_values or {}
local key = table.concat(label_values, '\0')
self.observations[key] = num
self.label_values[key] = label_values
end
function Gauge:collect()
local result = {}
if next(self.observations) == nil then
return {}
end
table.insert(result, '# HELP '..self.name..' '..escape_string(self.help))
table.insert(result, "# TYPE "..self.name.." gauge")
for key, observation in pairs(self.observations) do
local label_values = self.label_values[key]
local prefix = self.name
local labels = zip(self.labels, label_values)
local str = prefix..labels_to_string(labels)..
' '..metric_to_string(observation)
table.insert(result, str)
end
return result
end
local Histogram = {}
Histogram.__index = Histogram
function Histogram.new(name, help, labels,
buckets)
local obj = {}
setmetatable(obj, Histogram)
if not name then
error("Name should be set for Histogram")
end
obj.name = name
obj.help = help or ""
obj.labels = labels or {}
obj.buckets = buckets or DEFAULT_BUCKETS
table.sort(obj.buckets)
if obj.buckets[#obj.buckets] ~= INF then
obj.buckets[#obj.buckets+1] = INF
end
obj.observations = {}
obj.label_values = {}
obj.counts = {}
obj.sums = {}
return obj
end
function Histogram:observe(num, label_values)
local num = num or 0
local label_values = label_values or {}
local key = table.concat(label_values, '\0')
local obs = nil
if self.observations[key] == nil then
obs = {}
for i=1, #self.buckets do
obs[i] = 0
end
self.observations[key] = obs
self.label_values[key] = label_values
self.counts[key] = 0
self.sums[key] = 0
else
obs = self.observations[key]
end
self.counts[key] = self.counts[key] + 1
self.sums[key] = self.sums[key] + num
for i, bucket in ipairs(self.buckets) do
if num <= bucket then
obs[i] = obs[i] + 1
end
end
end
function Histogram:collect()
local result = {}
if next(self.observations) == nil then
return {}
end
table.insert(result, '# HELP '..self.name..' '..escape_string(self.help))
table.insert(result, "# TYPE "..self.name.." histogram")
for key, observation in pairs(self.observations) do
local label_values = self.label_values[key]
local prefix = self.name
local labels = zip(self.labels, label_values)
labels[#labels+1] = {le="0"}
for i, bucket in ipairs(self.buckets) do
labels[#labels] = {"le", metric_to_string(bucket)}
str = prefix.."_bucket"..labels_to_string(labels)..
' '..metric_to_string(observation[i])
table.insert(result, str)
end
table.remove(labels, #labels)
table.insert(result,
prefix.."_sum"..labels_to_string(labels)..' '..self.sums[key])
table.insert(result,
prefix.."_count"..labels_to_string(labels)..' '..self.counts[key])
end
return result
end
-- #################### Public API ####################
local function counter(name, help, labels)
local obj = Counter.new(name, help, labels)
obj = register(obj)
return obj
end
local function gauge(name, help, labels)
local obj = Gauge.new(name, help, labels)
obj = register(obj)
return obj
end
local function histogram(name, help, labels, buckets)
local obj = Histogram.new(name, help, labels, buckets)
obj = register(obj)
return obj
end
local function collect()
local registry = get_registry()
return table.concat(registry:collect(), '\n')..'\n'
end
local function collect_http()
return {
status = 200,
headers = { ['content-type'] = 'text/plain; charset=utf8' },
body = collect()
}
end
local function clear()
local registry = get_registry()
registry.collectors = {}
registry.callbacks = {}
end
local function init()
local registry = get_registry()
local tarantool_metrics = require('tarantool-prometheus.tarantool-metrics')
registry:register_callback(tarantool_metrics.measure_tarantool_metrics)
end
return {counter=counter,
gauge=gauge,
histogram=histogram,
collect=collect,
collect_http=collect_http,
clear=clear,
init=init}

View File

@ -0,0 +1,180 @@
#!/usr/bin/env tarantool
luaunit = require('luaunit')
prometheus = require('tarantool-prometheus')
TestPrometheus = {}
function TestPrometheus:tearDown()
prometheus.clear()
end
function TestPrometheus:testCounterNegativeValue()
c = prometheus.counter("counter")
luaunit.assertErrorMsgContains("should not be negative", c.inc, c, -1)
end
function TestPrometheus:testLabelNames()
c = prometheus.counter("counter", "", {'a1', 'foo', "var"})
c:inc(1, {1, '2', 'q4'})
r = c:collect()
luaunit.assertEquals(r[3], 'counter{a1="1",foo="2",var="q4"} 1')
end
function TestPrometheus:testLabelEscape()
c = prometheus.counter("counter", "", {'a1', 'foo', "var"})
c:inc(1, {'"', '\\a', '\n'})
r = c:collect()
luaunit.assertEquals(r[3], 'counter{a1="\\"",foo="\\\\a",var="\\n"} 1')
end
function TestPrometheus:testHelpEscape()
c = prometheus.counter("counter", "some\" escaped\\strings\n")
c:inc(1, {'"', '\\a', '\n'})
r = c:collect()
luaunit.assertEquals(r[1], '# HELP counter some\\" escaped\\\\strings\\n')
end
function TestPrometheus:testCounters()
first = prometheus.counter("counter1", "", {"a", "b"})
second = prometheus.counter("counter2", "", {"a", "b"})
first:inc()
first:inc(4)
second:inc(1, {"v1", "v2"})
second:inc(3, {"v1", "v3"})
second:inc(2, {"v1", "v3"})
r = first:collect()
luaunit.assertEquals(r[1], "# HELP counter1 ")
luaunit.assertEquals(r[2], "# TYPE counter1 counter")
luaunit.assertEquals(r[3], "counter1 5")
luaunit.assertEquals(r[4], nil)
r = second:collect()
luaunit.assertEquals(r[3], 'counter2{a="v1",b="v2"} 1')
luaunit.assertEquals(r[4], 'counter2{a="v1",b="v3"} 5')
luaunit.assertEquals(r[5], nil)
end
function TestPrometheus:testGauge()
first = prometheus.gauge("gauge1", "", {"a", "b"})
second = prometheus.gauge("gauge2", "", {"a", "b"})
first:inc()
first:inc(4)
first:set(2)
first:dec()
second:set(1, {"v1", "v2"})
second:inc(3, {"v1", "v3"})
second:dec(1, {"v1", "v3"})
second:inc(0, {"v1", "v3"})
r = first:collect()
luaunit.assertEquals(r[1], "# HELP gauge1 ")
luaunit.assertEquals(r[2], "# TYPE gauge1 gauge")
luaunit.assertEquals(r[3], "gauge1 1")
luaunit.assertEquals(r[4], nil)
r = second:collect()
luaunit.assertEquals(r[3], 'gauge2{a="v1",b="v2"} 1')
luaunit.assertEquals(r[4], 'gauge2{a="v1",b="v3"} 2')
luaunit.assertEquals(r[5], nil)
end
function TestPrometheus:testSpecialValues()
gauge = prometheus.gauge("gauge")
gauge:set(math.huge)
r = gauge:collect()
luaunit.assertEquals(r[3], "gauge +Inf")
gauge:set(-math.huge)
r = gauge:collect()
luaunit.assertEquals(r[3], "gauge -Inf")
gauge:set(math.huge * 0)
r = gauge:collect()
luaunit.assertEquals(r[3], "gauge Nan")
end
function TestPrometheus:testHistogram()
hist1 = prometheus.histogram("l1", "Histogram 1")
hist2 = prometheus.histogram("l2", "Histogram 2", {"var", "site"}, {0.1, 0.2})
hist3 = prometheus.histogram("l3", "Histogram 3", {})
hist1:observe(0.35)
hist1:observe(0.9)
hist1:observe(5)
hist1:observe(15)
hist2:observe(0.001, {"ok", "site1"})
hist2:observe(0.15, {"ok", "site1"})
hist2:observe(0.15, {"ok", "site2"})
r = hist1:collect()
luaunit.assertEquals(r[1], "# HELP l1 Histogram 1")
luaunit.assertEquals(r[2], "# TYPE l1 histogram")
luaunit.assertEquals(r[3], 'l1_bucket{le="0.005"} 0')
luaunit.assertEquals(r[4], 'l1_bucket{le="0.01"} 0')
luaunit.assertEquals(r[5], 'l1_bucket{le="0.025"} 0')
luaunit.assertEquals(r[6], 'l1_bucket{le="0.05"} 0')
luaunit.assertEquals(r[7], 'l1_bucket{le="0.075"} 0')
luaunit.assertEquals(r[8], 'l1_bucket{le="0.1"} 0')
luaunit.assertEquals(r[9], 'l1_bucket{le="0.25"} 0')
luaunit.assertEquals(r[10], 'l1_bucket{le="0.5"} 1')
luaunit.assertEquals(r[11], 'l1_bucket{le="0.75"} 1')
luaunit.assertEquals(r[12], 'l1_bucket{le="1"} 2')
luaunit.assertEquals(r[13], 'l1_bucket{le="2.5"} 2')
luaunit.assertEquals(r[14], 'l1_bucket{le="5"} 3')
luaunit.assertEquals(r[15], 'l1_bucket{le="7.5"} 3')
luaunit.assertEquals(r[16], 'l1_bucket{le="10"} 3')
luaunit.assertEquals(r[17], 'l1_bucket{le="+Inf"} 4')
luaunit.assertEquals(r[18], 'l1_sum 21.25')
luaunit.assertEquals(r[19], 'l1_count 4')
r = hist2:collect()
luaunit.assertEquals(r[3], 'l2_bucket{var="ok",site="site1",le="0.1"} 1')
luaunit.assertEquals(r[4], 'l2_bucket{var="ok",site="site1",le="0.2"} 2')
luaunit.assertEquals(r[5], 'l2_bucket{var="ok",site="site1",le="+Inf"} 2')
luaunit.assertEquals(r[6], 'l2_sum{var="ok",site="site1"} 0.151')
luaunit.assertEquals(r[7], 'l2_count{var="ok",site="site1"} 2')
luaunit.assertEquals(r[8], 'l2_bucket{var="ok",site="site2",le="0.1"} 0')
luaunit.assertEquals(r[9], 'l2_bucket{var="ok",site="site2",le="0.2"} 1')
luaunit.assertEquals(r[10], 'l2_bucket{var="ok",site="site2",le="+Inf"} 1')
luaunit.assertEquals(r[11], 'l2_sum{var="ok",site="site2"} 0.15')
luaunit.assertEquals(r[12], 'l2_count{var="ok",site="site2"} 1')
r = hist3:collect()
luaunit.assertEquals(r[3], nil)
end
function TestPrometheus:testHistogramUnorderedBuckets()
hist = prometheus.histogram("l2", "Histogram 2", {}, {0.2, 0.1, 0.5})
hist:observe(0.15)
hist:observe(0.4)
r = hist:collect()
luaunit.assertEquals(r[3], 'l2_bucket{le="0.1"} 0')
luaunit.assertEquals(r[4], 'l2_bucket{le="0.2"} 1')
luaunit.assertEquals(r[5], 'l2_bucket{le="0.5"} 2')
luaunit.assertEquals(r[6], 'l2_bucket{le="+Inf"} 2')
luaunit.assertEquals(r[7], 'l2_sum 0.55')
luaunit.assertEquals(r[8], 'l2_count 2')
luaunit.assertEquals(r[9], nil)
end
os.exit(luaunit.run())

View File

@ -0,0 +1,28 @@
local Event = require 'utils.event'
local event_repeat_seconds = 10
local prometheus = require 'features.prometheus.tarantool-prometheus'
local gauge_players_online = prometheus.gauge("players_online", "description")
local function writeMetrics()
game.write_file("metrics/game.prom", prometheus.collect(), false)
end
local function on_nth_tick()
writeMetrics()
end
local function on_player_joined()
gauge_players_online:inc(1)
end
local function on_player_left()
gauge_players_online:dec(1)
end
Event.on_nth_tick(60*event_repeat_seconds, on_nth_tick)
Event.add(defines.events.on_player_joined_game, on_player_joined)
Event.add(defines.events.on_player_left_game, on_player_left)

View File

@ -2,7 +2,7 @@ require 'map_gen.maps.crash_site.blueprint_extractor'
require 'map_gen.maps.crash_site.events'
require 'map_gen.maps.crash_site.weapon_balance'
require 'map_gen.maps.crash_site.features.rocket_tanks'
require 'features.prometheus_event'
local b = require 'map_gen.shared.builders'
local Global = require('utils.global')