nepenthes/components/stats.lua
2025-10-24 17:32:59 +00:00

253 lines
4.1 KiB
Lua

#!/usr/bin/env lua5.3
local fifo = require 'fifo'
local config = require 'components.config'
local _M = {}
local buf = fifo()
local start = os.time()
local tick = os.time()
local tick_count = 0
local totals = {
bytes_sent = 0,
bytes_generated = 0,
hits = 0,
delay = 0
}
function _M.clear()
for k in pairs(totals) do
totals[k] = 0
end
buf = fifo()
end
function _M.log( val )
--
-- Schema Check
--
assert(type(val) == 'table')
assert(type(val.address) == 'string')
assert(type(val.agent) == 'string')
assert(type(val.uri) == 'string')
assert(type(val.silo) == 'string')
assert(type(val.complete) == 'boolean')
assert(type(val.when) == 'number')
assert(type(val.bytes_sent) == 'number')
assert(type(val.bytes_generated) == 'number')
assert(type(val.response) == 'number')
assert(type(val.delay) == 'number')
assert(type(val.cpu) == 'number')
if os.time() == tick then
tick_count = tick_count + 1
else
tick = os.time()
tick_count = 1
end
val.id = string.format('%s.%s', tick, tick_count)
buf:push( val )
local expired = os.time() - config.stats_remember_time
if #buf > 0 then
while buf:peek().when <= expired do
local hit = buf:pop()
totals.hits = totals.hits + 1
totals.bytes_generated = totals.bytes_generated + hit.bytes_generated
totals.bytes_sent = totals.bytes_sent + hit.bytes_sent
totals.delay = totals.delay + hit.delay
end
end
end
function _M.build_entry( x )
local ret = {}
for k, v in pairs(x) do
ret[k] = v
end
ret.complete = false
return setmetatable( ret, {
__close = function()
ret.complete = true
end
})
end
function _M.compute( silo )
local ret = {
hits = 0,
addresses = 0,
agents = 0,
cpu = 0,
cpu_total = os.clock(),
bytes_sent = 0,
bytes_generated = 0,
memory_usage = collectgarbage( "count" ) * 1024,
delay = 0,
active = 0,
bogons = 0,
delay_total = 0,
bytes_sent_total = 0,
bytes_generated_total = 0,
hits_total = 0,
uptime = os.time() - start
}
-- this is inaccurate at first (due to training) but will decay
-- to close enough with time.
ret.cpu_percent = ( ret.cpu_total / ret.uptime ) * 100
local seen_addresses = {}
local seen_agents = {}
for i = 1, #buf do
local v = buf:peek(i)
if silo then
if v.silo ~= silo then
goto skip
end
end
ret.hits = ret.hits + 1
if v.response == 404 then
ret.bogons = ret.bogons + 1
end
if not seen_addresses[ v.address ] then
seen_addresses[ v.address ] = true
ret.addresses = ret.addresses + 1
end
if not seen_agents[ v.agent ] then
seen_agents[ v.agent ] = true
ret.agents = ret.agents + 1
end
if not v.complete then
ret.active = ret.active + 1
end
ret.cpu = ret.cpu + v.cpu
ret.bytes_generated = ret.bytes_generated + v.bytes_generated
ret.bytes_sent = ret.bytes_sent + v.bytes_sent
ret.delay = ret.delay + v.delay
::skip::
end
ret.unsent_bytes = ret.bytes_generated - ret.bytes_sent
ret.hits_total = ret.hits + totals.hits
ret.delay_total = ret.delay + totals.delay
ret.bytes_generated_total = ret.bytes_generated + totals.bytes_generated
ret.bytes_sent_total = ret.bytes_sent + totals.bytes_sent
if ret.bytes_generated > 0 then
ret.unsent_bytes_percent = (( ret.bytes_generated - ret.bytes_sent ) / ret.bytes_generated ) * 100
end
return ret
end
function _M.address_list( silo )
local ret = {}
for i = 1, #buf do
local v = buf:peek(i)
if silo then
if v.silo ~= silo then
goto skip
end
end
if not ret[ v.address ] then
ret[ v.address ] = 1
else
ret[ v.address ] = ret[ v.address ] + 1
end
::skip::
end
return ret
end
function _M.agent_list( silo )
local ret = {}
for i = 1, #buf do
local v = buf:peek(i)
if silo then
if v.silo ~= silo then
goto skip
end
end
if not ret[ v.agent ] then
ret[ v.agent ] = 1
else
ret[ v.agent ] = ret[ v.agent ] + 1
end
::skip::
end
return ret
end
function _M.buffer( from )
local ret = {}
local include = true
if from then
include = false
end
for i = 1, #buf do
local v = buf:peek(i)
if include then
ret[ #ret + 1 ] = v
else
if v.id == from then
include = true
end
end
end
return ret
end
return _M