Mercurial > prosody-modules
comparison mod_statistics_statsman/mod_statistics_statsman.lua @ 3158:2558ece20e58
mod_statistics_statsman: Streaming access to statsmanager
| author | Kim Alvefur <zash@zash.se> |
|---|---|
| date | Thu, 28 Jun 2018 21:42:48 +0200 |
| parents | |
| children | 5d58bdbfe024 |
comparison
equal
deleted
inserted
replaced
| 3157:8a870e0319db | 3158:2558ece20e58 |
|---|---|
| 1 module:set_global(); | |
| 2 | |
| 3 local statsman = require "core.statsmanager"; | |
| 4 local time_now = require "util.time".now; | |
| 5 local filters = require "util.filters"; | |
| 6 local serialize = require "util.serialization".serialize; | |
| 7 | |
| 8 local statistics_interval = module:context("*"):get_option_number("statistics_interval", 60); | |
| 9 if module:context("*"):get_option("statistics", "internal") ~= "internal" then | |
| 10 module:log("error", "Not using internal statistics, can't do anyting"); | |
| 11 return; | |
| 12 end | |
| 13 | |
| 14 local sessions = {}; | |
| 15 | |
| 16 local name_map = { | |
| 17 ["start_time"] = "up_since"; | |
| 18 ["cpu.percent:amount"] = "cpu"; | |
| 19 ["memory.allocated_mmap:size"] = "memory_allocated_mmap"; | |
| 20 ["memory.allocated:size"] = "memory_allocated"; | |
| 21 ["memory.lua:size"] = "memory_lua"; | |
| 22 ["memory.returnable:size"] = "memory_returnable"; | |
| 23 ["memory.rss:size"] = "memory_rss"; | |
| 24 ["memory.total:size"] = "memory_total"; | |
| 25 ["memory.unused:size"] = "memory_unused"; | |
| 26 ["memory.used:size"] = "memory_used"; | |
| 27 ["/*/mod_c2s/connections:amount"] = "total_c2s"; | |
| 28 ["/*/mod_s2s/connections:amount"] = "total_s2s"; | |
| 29 }; | |
| 30 | |
| 31 local function push_stat(conn, name, value) | |
| 32 local value_str = serialize(value); | |
| 33 name = name_map[name] or name; | |
| 34 return conn:write((("STAT %q (%s)\n"):format(name, value_str):gsub("\\\n", "\\n"))); | |
| 35 end | |
| 36 | |
| 37 local function push_stat_to_all(name, value) | |
| 38 for conn in pairs(sessions) do | |
| 39 push_stat(conn, name, value); | |
| 40 end | |
| 41 end | |
| 42 | |
| 43 local session_stats_tpl = ([[{ | |
| 44 message_in = %d, message_out = %d; | |
| 45 presence_in = %d, presence_out = %d; | |
| 46 iq_in = %d, iq_out = %d; | |
| 47 bytes_in = %d, bytes_out = %d; | |
| 48 }]]):gsub("%s", ""); | |
| 49 | |
| 50 | |
| 51 local jid_fields = { | |
| 52 c2s = "full_jid"; | |
| 53 s2sin = "from_host"; | |
| 54 s2sout = "to_host"; | |
| 55 component = "host"; | |
| 56 }; | |
| 57 | |
| 58 local function push_session_to_all(session, stats) | |
| 59 local id = tostring(session):match("[a-f0-9]+$"); -- FIXME: Better id? :/ | |
| 60 local stanzas_in, stanzas_out = stats.stanzas_in, stats.stanzas_out; | |
| 61 local s = (session_stats_tpl):format( | |
| 62 stanzas_in.message, stanzas_out.message, | |
| 63 stanzas_in.presence, stanzas_out.presence, | |
| 64 stanzas_in.iq, stanzas_out.iq, | |
| 65 stats.bytes_in, stats.bytes_out); | |
| 66 local jid = session[jid_fields[session.type]] or ""; | |
| 67 for conn in pairs(sessions) do | |
| 68 conn:write(("SESS %q %q %s\n"):format(id, jid, s)); | |
| 69 end | |
| 70 end | |
| 71 | |
| 72 local active_sessions = {}; | |
| 73 | |
| 74 -- Network listener | |
| 75 local listener = {}; | |
| 76 | |
| 77 function listener.onconnect(conn) | |
| 78 sessions[conn] = true; | |
| 79 push_stat(conn, "version", prosody.version); | |
| 80 push_stat(conn, "start_time", prosody.start_time); | |
| 81 push_stat(conn, "statistics_interval", statistics_interval); | |
| 82 push_stat(conn, "time", time_now()); | |
| 83 local stats = statsman.get_stats(); | |
| 84 for name, value in pairs(stats) do | |
| 85 push_stat(conn, name, value); | |
| 86 end | |
| 87 conn:write("\n"); -- Signal end of first batch (for non-streaming clients) | |
| 88 end | |
| 89 | |
| 90 function listener.onincoming(conn, data) | |
| 91 end | |
| 92 | |
| 93 function listener.ondisconnect(conn) | |
| 94 sessions[conn] = nil; | |
| 95 end | |
| 96 | |
| 97 function listener.onreadtimeout() | |
| 98 return true; | |
| 99 end | |
| 100 | |
| 101 local add_statistics_filter; -- forward decl | |
| 102 if prosody and prosody.arg then -- ensures we aren't in prosodyctl | |
| 103 setmetatable(active_sessions, { | |
| 104 __index = function ( t, k ) | |
| 105 local v = { | |
| 106 bytes_in = 0, bytes_out = 0; | |
| 107 stanzas_in = { | |
| 108 message = 0, presence = 0, iq = 0; | |
| 109 }; | |
| 110 stanzas_out = { | |
| 111 message = 0, presence = 0, iq = 0; | |
| 112 }; | |
| 113 } | |
| 114 rawset(t, k, v); | |
| 115 return v; | |
| 116 end | |
| 117 }); | |
| 118 local filters = require "util.filters"; | |
| 119 local function handle_stanza_in(stanza, session) | |
| 120 local s = active_sessions[session].stanzas_in; | |
| 121 local n = s[stanza.name]; | |
| 122 if n then | |
| 123 s[stanza.name] = n + 1; | |
| 124 end | |
| 125 return stanza; | |
| 126 end | |
| 127 local function handle_stanza_out(stanza, session) | |
| 128 local s = active_sessions[session].stanzas_out; | |
| 129 local n = s[stanza.name]; | |
| 130 if n then | |
| 131 s[stanza.name] = n + 1; | |
| 132 end | |
| 133 return stanza; | |
| 134 end | |
| 135 local function handle_bytes_in(bytes, session) | |
| 136 local s = active_sessions[session]; | |
| 137 s.bytes_in = s.bytes_in + #bytes; | |
| 138 return bytes; | |
| 139 end | |
| 140 local function handle_bytes_out(bytes, session) | |
| 141 local s = active_sessions[session]; | |
| 142 s.bytes_out = s.bytes_out + #bytes; | |
| 143 return bytes; | |
| 144 end | |
| 145 function add_statistics_filter(session) | |
| 146 filters.add_filter(session, "stanzas/in", handle_stanza_in); | |
| 147 filters.add_filter(session, "stanzas/out", handle_stanza_out); | |
| 148 filters.add_filter(session, "bytes/in", handle_bytes_in); | |
| 149 filters.add_filter(session, "bytes/out", handle_bytes_out); | |
| 150 end | |
| 151 end | |
| 152 | |
| 153 | |
| 154 function module.load() | |
| 155 if not(prosody and prosody.arg) then | |
| 156 return; | |
| 157 end | |
| 158 filters.add_filter_hook(add_statistics_filter); | |
| 159 | |
| 160 module:add_timer(1, function () | |
| 161 for session, session_stats in pairs(active_sessions) do | |
| 162 active_sessions[session] = nil; | |
| 163 push_session_to_all(session, session_stats); | |
| 164 end | |
| 165 return 1; | |
| 166 end); | |
| 167 | |
| 168 module:hook("stats-updated", function (event) | |
| 169 local stats = event.changed_stats; | |
| 170 push_stat_to_all("time", time_now()); | |
| 171 for name, value in pairs(stats) do | |
| 172 push_stat_to_all(name, value); | |
| 173 end | |
| 174 end); | |
| 175 | |
| 176 module:hook("server-stopping", function () | |
| 177 push_stat_to_all("stop_time", time_now()); | |
| 178 end); | |
| 179 end | |
| 180 function module.unload() | |
| 181 filters.remove_filter_hook(add_statistics_filter); | |
| 182 end | |
| 183 | |
| 184 if prosody and prosody.arg then | |
| 185 module:provides("net", { | |
| 186 default_port = 5782; | |
| 187 listener = listener; | |
| 188 private = true; | |
| 189 }); | |
| 190 end |
