Commit 15151572 authored by Marek Vavrusa's avatar Marek Vavrusa
Browse files

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 ...@@ -90,7 +90,8 @@ for _, pg in ipairs(pages) do
end end
-- Export built-in prometheus interface -- 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 M.endpoints[k] = v
end end
...@@ -304,10 +305,16 @@ function M.interface(host, port, endpoints, crtfile, keyfile) ...@@ -304,10 +305,16 @@ function M.interface(host, port, endpoints, crtfile, keyfile)
end end
end end
-- @function Init module
function M.init()
cq:wrap(prometheus.init)
end
-- @function Cleanup module -- @function Cleanup module
function M.deinit() function M.deinit()
if M.ev then event.cancel(M.ev) end if M.ev then event.cancel(M.ev) end
M.servers = {} M.servers = {}
prometheus.deinit()
end end
-- @function Configure module -- @function Configure module
......
local cqueues = require('cqueues') local cqueues = require('cqueues')
local snapshots, snapshots_count = {}, 120
-- Load dependent modules -- Load dependent modules
if not stats then modules.load('stats') end if not stats then modules.load('stats') end
...@@ -10,14 +11,20 @@ local function getstats() ...@@ -10,14 +11,20 @@ local function getstats()
return t return t
end end
local function snapshot_end(h, ws)
snapshots_count = false
end
-- Function to sort frequency list -- Function to sort frequency list
local function stream_stats(h, ws) local function snapshot_start(h, ws)
local ok, prev = true, getstats() local ok, prev = true, getstats()
while ok do while snapshots_count do
local is_empty = true
-- Get current snapshot -- Get current snapshot
local cur, stats_dt = getstats(), {} local cur, stats_dt = getstats(), {}
for k,v in pairs(cur) do for k,v in pairs(cur) do
stats_dt[k] = v - (prev[k] or 0) stats_dt[k] = v - (prev[k] or 0)
is_empty = is_empty and stats_dt[k] == 0
end end
prev = cur prev = cur
-- Calculate upstreams and geotag them if possible -- Calculate upstreams and geotag them if possible
...@@ -37,8 +44,38 @@ local function stream_stats(h, ws) ...@@ -37,8 +44,38 @@ local function stream_stats(h, ws)
end end
end end
-- Publish stats updates periodically -- Publish stats updates periodically
local push = tojson({stats=stats_dt,upstreams=upstreams or {}}) if not is_empty then
ok = ws:send(push) 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) cqueues.sleep(1)
end end
end end
...@@ -79,7 +116,11 @@ end ...@@ -79,7 +116,11 @@ end
-- Export endpoints -- Export endpoints
return { return {
['/stats'] = {'application/json', getstats, stream_stats}, init = snapshot_start,
['/frequent'] = {'application/json', function () return stats.frequent() end}, deinit = snapshot_end,
['/metrics'] = {'text/plain; version=0.0.4', serve_prometheus}, 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() { ...@@ -59,11 +59,6 @@ $(function() {
renderer: 'multi', renderer: 'multi',
series: series, 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( { var y_axis = new Rickshaw.Graph.Axis.Y( {
graph: graph, graph: graph,
orientation: 'left', orientation: 'left',
...@@ -139,20 +134,22 @@ $(function() { ...@@ -139,20 +134,22 @@ $(function() {
} }
/* Realtime updates over WebSockets */ /* Realtime updates over WebSockets */
function pushMetrics(resp) { function pushMetrics(resp, now, buffer) {
var now = Date.now() / 1000;
for (var lb in resp) { for (var lb in resp) {
var val = resp[lb]; var val = resp[lb];
/* Push new datapoints */ /* Push new datapoints */
if (lb in data) { if (lb in data) {
data[lb].push({x: now, y:val}); data[lb].push({x: now, y:val});
if (data[lb].length > 100) { if (data[lb].length > 120) {
data[lb].shift(); data[lb].shift();
} }
} }
} }
graph.update(); /* Buffer graph changes. */
if (!buffer) {
graph.update();
}
} }
var age = 0; var age = 0;
...@@ -173,7 +170,7 @@ $(function() { ...@@ -173,7 +170,7 @@ $(function() {
/* Update bubbles and prune the oldest */ /* Update bubbles and prune the oldest */
for (var key in resp) { for (var key in resp) {
var val = resp[key]; var val = resp[key];
if (!val.data) { if (!val.data || !val.location || val.location.longitude == null) {
continue; continue;
} }
var sum = val.data.reduce(function(a, b) { return a + b; }); var sum = val.data.reduce(function(a, b) { return a + b; });
...@@ -215,10 +212,21 @@ $(function() { ...@@ -215,10 +212,21 @@ $(function() {
/* WebSocket endpoints */ /* WebSocket endpoints */
var wsStats = (secure ? 'wss://' : 'ws://') + location.host + '/stats'; var wsStats = (secure ? 'wss://' : 'ws://') + location.host + '/stats';
var ws = new Socket(wsStats); var ws = new Socket(wsStats);
ws.onmessage = function(evt) { ws.onmessage = function(evt) {
var data = $.parseJSON(evt.data); var data = JSON.parse(evt.data);
pushMetrics(data.stats); if (data[0]) {
pushUpstreams(data.upstreams); 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 @@ ...@@ -50,7 +50,6 @@
<div id="chart_container"> <div id="chart_container">
<div id="y_axis"></div> <div id="y_axis"></div>
<div id="chart"></div> <div id="chart"></div>
<div id="x_axis"></div>
</div> </div>
<form id="legend_container"> <form id="legend_container">
<div id="legend"></div> <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