Commit 15151572 authored by Marek Vavrusa's avatar Marek Vavrusa

modules/http: keep history of last 120 datapoints

* the http module collects stats snapshots on one
  central location and then streams it to clients
* history of last 120 datapoints (at least 2min)
  is kept for convenience
parent 5997a3a0
......@@ -90,7 +90,8 @@ for _, pg in ipairs(pages) do
end
-- Export built-in prometheus interface
for k, v in pairs(require('prometheus')) do
local prometheus = require('prometheus')
for k, v in pairs(prometheus.endpoints) do
M.endpoints[k] = v
end
......@@ -304,10 +305,16 @@ function M.interface(host, port, endpoints, crtfile, keyfile)
end
end
-- @function Init module
function M.init()
cq:wrap(prometheus.init)
end
-- @function Cleanup module
function M.deinit()
if M.ev then event.cancel(M.ev) end
M.servers = {}
prometheus.deinit()
end
-- @function Configure module
......
local cqueues = require('cqueues')
local snapshots, snapshots_count = {}, 120
-- Load dependent modules
if not stats then modules.load('stats') end
......@@ -10,14 +11,20 @@ local function getstats()
return t
end
local function snapshot_end(h, ws)
snapshots_count = false
end
-- Function to sort frequency list
local function stream_stats(h, ws)
local function snapshot_start(h, ws)
local ok, prev = true, getstats()
while ok do
while snapshots_count do
local is_empty = true
-- Get current snapshot
local cur, stats_dt = getstats(), {}
for k,v in pairs(cur) do
stats_dt[k] = v - (prev[k] or 0)
is_empty = is_empty and stats_dt[k] == 0
end
prev = cur
-- Calculate upstreams and geotag them if possible
......@@ -37,8 +44,38 @@ local function stream_stats(h, ws)
end
end
-- Publish stats updates periodically
local push = tojson({stats=stats_dt,upstreams=upstreams or {}})
ok = ws:send(push)
if not is_empty then
local update = {time=os.time(), stats=stats_dt, upstreams=upstreams or {}}
table.insert(snapshots, update)
if #snapshots > snapshots_count then
table.remove(snapshots, 1)
end
end
cqueues.sleep(1)
end
end
-- Function to sort frequency list
local function stream_stats(h, ws)
-- Initially, stream history
local ok, last = true, nil
local batch = {}
for i, s in ipairs(snapshots) do
table.insert(batch, s)
if #batch == 20 or i + 1 == #snapshots then
ok = ws:send(tojson(batch))
batch = {}
end
end
-- Publish stats updates periodically
while ok do
-- Get last snapshot
local id = #snapshots - 1
if id > 0 and snapshots[id].time ~= last then
local push = tojson(snapshots[id])
last = snapshots[id].time
ok = ws:send(push)
end
cqueues.sleep(1)
end
end
......@@ -79,7 +116,11 @@ end
-- Export endpoints
return {
['/stats'] = {'application/json', getstats, stream_stats},
['/frequent'] = {'application/json', function () return stats.frequent() end},
['/metrics'] = {'text/plain; version=0.0.4', serve_prometheus},
init = snapshot_start,
deinit = snapshot_end,
endpoints = {
['/stats'] = {'application/json', getstats, stream_stats},
['/frequent'] = {'application/json', function () return stats.frequent() end},
['/metrics'] = {'text/plain; version=0.0.4', serve_prometheus},
}
}
\ No newline at end of file
......@@ -59,11 +59,6 @@ $(function() {
renderer: 'multi',
series: series,
});
var x_axis = new Rickshaw.Graph.Axis.Time( {
graph: graph,
ticksTreatment: 'glow',
element: document.querySelector("#x_axis"),
} );
var y_axis = new Rickshaw.Graph.Axis.Y( {
graph: graph,
orientation: 'left',
......@@ -139,20 +134,22 @@ $(function() {
}
/* Realtime updates over WebSockets */
function pushMetrics(resp) {
var now = Date.now() / 1000;
function pushMetrics(resp, now, buffer) {
for (var lb in resp) {
var val = resp[lb];
/* Push new datapoints */
if (lb in data) {
data[lb].push({x: now, y:val});
if (data[lb].length > 100) {
if (data[lb].length > 120) {
data[lb].shift();
}
}
}
graph.update();
/* Buffer graph changes. */
if (!buffer) {
graph.update();
}
}
var age = 0;
......@@ -173,7 +170,7 @@ $(function() {
/* Update bubbles and prune the oldest */
for (var key in resp) {
var val = resp[key];
if (!val.data) {
if (!val.data || !val.location || val.location.longitude == null) {
continue;
}
var sum = val.data.reduce(function(a, b) { return a + b; });
......@@ -215,10 +212,21 @@ $(function() {
/* WebSocket endpoints */
var wsStats = (secure ? 'wss://' : 'ws://') + location.host + '/stats';
var ws = new Socket(wsStats);
ws.onmessage = function(evt) {
var data = $.parseJSON(evt.data);
pushMetrics(data.stats);
pushUpstreams(data.upstreams);
};
var ws = new Socket(wsStats);
ws.onmessage = function(evt) {
var data = JSON.parse(evt.data);
if (data[0]) {
if (data.length > 0) {
pushUpstreams(data[data.length - 1].upstreams);
}
for (var i in data) {
pushMetrics(data[i].stats, data[i].time, true);
}
graph.update();
} else {
pushMetrics(data.stats, data.time);
pushUpstreams(data.upstreams);
}
};
});
\ No newline at end of file
......@@ -50,7 +50,6 @@
<div id="chart_container">
<div id="y_axis"></div>
<div id="chart"></div>
<div id="x_axis"></div>
</div>
<form id="legend_container">
<div id="legend"></div>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment