Diff

mod_statistics/mod_statistics.lua @ 1072:4dbdb1b465e8

mod_statistics: Initial version, and a rough 'prosodyctl mod_statistics top'
author Matthew Wild <mwild1@gmail.com>
date Sat, 15 Jun 2013 19:08:34 +0100
child 1074:cccd9f6a628d
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mod_statistics/mod_statistics.lua	Sat Jun 15 19:08:34 2013 +0100
@@ -0,0 +1,134 @@
+module:set_global();
+
+local stats = module:require("mod_statistics/stats");
+local filters = require "util.filters";
+local serialize = require "util.serialization".serialize;
+
+local cached_values = {};
+
+local sessions = {};
+
+local function push_stat(conn, name, value)
+	local value_str = serialize(value);
+	return conn:write((("STAT %q (%s)\n"):format(name, value_str):gsub("\\\n", "\\n")));
+end
+
+local function push_stat_to_all(name, value)
+	for conn in pairs(sessions) do
+		push_stat(conn, name, value);
+	end
+end
+
+local session_stats_tpl = ([[{
+	message_in = %d, message_out = %d;
+	presence_in = %d, presence_out = %d;
+	iq_in = %d, iq_out = %d;
+	bytes_in = %d, bytes_out = %d;
+}]]):gsub("%s", "");
+
+
+local jid_fields = {
+	c2s = "full_jid";
+	s2sin = "from_host";
+	s2sout = "to_host";
+	component = "host";
+};
+
+local function push_session_to_all(session, stats)
+	local id = tostring(session):match("[a-f0-9]+$"); -- FIXME: Better id? :/
+	local stanzas_in, stanzas_out = stats.stanzas_in, stats.stanzas_out;
+	local s = (session_stats_tpl):format(
+		stanzas_in.message, stanzas_out.message,
+		stanzas_in.presence, stanzas_out.presence,
+		stanzas_in.iq, stanzas_out.iq,
+		stats.bytes_in, stats.bytes_out);
+	local jid = session[jid_fields[session.type]] or "";
+	for conn in pairs(sessions) do
+		return conn:write(("SESS %q %q %s\n"):format(id, jid, s));
+	end
+end
+
+local available_stats = stats.stats;
+local active_sessions = stats.active_sessions;
+
+-- Handle statistics provided by other modules
+local function item_handlers(host)
+	host = host and (host.."/") or "";
+	
+	return function (event) -- Added
+		local stats = event.item.statistics;
+		local group = host..(stats.name and (stats.name.."::") or "");
+		for name, stat in pairs(stats) do
+			available_stats[group..name] = stat;
+		end
+	end, function (event) -- Removed
+		local stats = event.item.statistics;
+		local group = host..(stats.name and (stats.name.."::") or "");
+		for name, stat in pairs(stats) do
+			available_stats[group..name] = nil;
+		end
+	end;
+end
+
+module:handle_items("statistics-provider", item_handlers());
+function module.add_host(module)
+	module:handle_items("statistics-provider", item_handlers(module.host));
+end
+
+-- Network listener
+local listener = {};
+
+function listener.onconnect(conn)
+	sessions[conn] = {};
+	push_stat(conn, "version", prosody.version);
+	for name, value in pairs(cached_values) do
+		push_stat(conn, name, value);
+	end
+	conn:write("\n"); -- Signal end of first batch (for non-streaming clients)
+end
+
+function listener.onincoming(conn, data)
+end
+
+function listener.ondisconnect(conn)
+	sessions[conn] = nil;
+end
+
+function module.load()
+	if not(prosody and prosody.full_sessions) then return; end --FIXME: hack, need a proper flag
+	filters.add_filter_hook(stats.filter_hook);
+
+	module:add_timer(1, function ()
+		for stat_name, stat in pairs(available_stats) do
+			if stat.get then
+				local cached = cached_values[stat_name];
+				local new_value = stat.get();
+				if new_value ~= cached then
+					push_stat_to_all(stat_name, new_value);
+					cached_values[stat_name] = new_value;
+				end
+			end
+		end
+		for session, session_stats in pairs(active_sessions) do
+			active_sessions[session] = nil;
+			push_session_to_all(session, session_stats);
+		end
+		return 1;
+	end);
+	module:provides("net", {
+		default_port = 5782;
+		listener = listener;
+	});
+end
+function module.unload()
+	filters.remove_filter_hook(stats.filter_hook);
+end
+function module.command( args )
+	local command = args[1];
+	if command == "top" then
+		local dir = module:get_directory();
+		package.path = dir.."/?.lua;"..dir.."/?.lib.lua;"..package.path;
+		local prosodytop = require "prosodytop";
+		prosodytop.run();
+	end
+end