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
;
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");
17 ["start_time"] = "up_since";
18 ["cpu.percent:amount"] = "cpu";
19 ["cpu.clock:amount"] = "cpu_total";
20 ["memory.allocated_mmap:amount"] = "memory_allocated_mmap";
21 ["memory.allocated:amount"] = "memory_allocated";
22 ["memory.lua:amount"] = "memory_lua";
23 ["memory.returnable:amount"] = "memory_returnable";
24 ["memory.rss:amount"] = "memory_rss";
25 ["memory.total:amount"] = "memory_total";
26 ["memory.unused:amount"] = "memory_unused";
27 ["memory.used:amount"] = "memory_used";
28 ["/*/mod_c2s/connections:amount"] = "total_c2s";
29 ["/*/mod_s2s/connections:amount"] = "total_s2s";
32 local function push_stat(conn
, name
, value
)
33 local value_str
= serialize(value
);
34 name
= name_map
[name
] or name
;
35 return conn
:write((("STAT %q (%s)\n"):format(name
, value_str
):gsub("\\\n", "\\n")));
38 local function push_stat_to_all(name
, value
)
39 for conn
in pairs(sessions
) do
40 push_stat(conn
, name
, value
);
44 local session_stats_tpl
= ([[{
45 message_in = %d, message_out = %d;
46 presence_in = %d, presence_out = %d;
47 iq_in = %d, iq_out = %d;
48 bytes_in = %d, bytes_out = %d;
59 local function push_session_to_all(session
, stats
)
60 local id
= tostring(session
):match("[a-f0-9]+$"); -- FIXME: Better id? :/
61 local stanzas_in
, stanzas_out
= stats
.stanzas_in
, stats
.stanzas_out
;
62 local s
= (session_stats_tpl
):format(
63 stanzas_in
.message
, stanzas_out
.message
,
64 stanzas_in
.presence
, stanzas_out
.presence
,
65 stanzas_in
.iq
, stanzas_out
.iq
,
66 stats
.bytes_in
, stats
.bytes_out
);
67 local jid
= session
[jid_fields
[session
.type]]
or "";
68 for conn
in pairs(sessions
) do
69 conn
:write(("SESS %q %q %s\n"):format(id
, jid
, s
));
73 local active_sessions
= {};
78 function listener
.onconnect(conn
)
79 sessions
[conn
] = true;
80 push_stat(conn
, "version", prosody
.version
);
81 push_stat(conn
, "start_time", prosody
.start_time
);
82 push_stat(conn
, "statistics_interval", statistics_interval
);
83 push_stat(conn
, "time", time_now());
84 local stats
= statsman
.get_stats();
85 for name
, value
in pairs(stats
) do
86 push_stat(conn
, name
, value
);
88 conn
:write("\n"); -- Signal end of first batch (for non-streaming clients)
91 function listener
.onincoming(conn
, data
) -- luacheck: ignore 212
95 function listener
.ondisconnect(conn
)
99 function listener
.onreadtimeout()
103 local add_statistics_filter
; -- forward decl
104 if prosody
and prosody
.arg
then -- ensures we aren't in prosodyctl
105 setmetatable(active_sessions
, {
106 __index
= function ( t
, k
)
108 bytes_in
= 0, bytes_out
= 0;
110 message
= 0, presence
= 0, iq
= 0;
113 message
= 0, presence
= 0, iq
= 0;
120 local function handle_stanza_in(stanza
, session
)
121 local s
= active_sessions
[session
].stanzas_in
;
122 local n
= s
[stanza
.name
];
124 s
[stanza
.name
] = n
+ 1;
128 local function handle_stanza_out(stanza
, session
)
129 local s
= active_sessions
[session
].stanzas_out
;
130 local n
= s
[stanza
.name
];
132 s
[stanza
.name
] = n
+ 1;
136 local function handle_bytes_in(bytes
, session
)
137 local s
= active_sessions
[session
];
138 s
.bytes_in
= s
.bytes_in
+ #bytes
;
141 local function handle_bytes_out(bytes
, session
)
142 local s
= active_sessions
[session
];
143 s
.bytes_out
= s
.bytes_out
+ #bytes
;
146 function add_statistics_filter(session
)
147 filters
.add_filter(session
, "stanzas/in", handle_stanza_in
);
148 filters
.add_filter(session
, "stanzas/out", handle_stanza_out
);
149 filters
.add_filter(session
, "bytes/in", handle_bytes_in
);
150 filters
.add_filter(session
, "bytes/out", handle_bytes_out
);
155 function module
.load()
156 if not(prosody
and prosody
.arg
) then
159 filters
.add_filter_hook(add_statistics_filter
);
161 module
:add_timer(1, function ()
162 for session
, session_stats
in pairs(active_sessions
) do
163 active_sessions
[session
] = nil;
164 push_session_to_all(session
, session_stats
);
169 module
:hook("stats-updated", function (event
)
170 local stats
= event
.changed_stats
;
171 push_stat_to_all("time", time_now());
172 for name
, value
in pairs(stats
) do
173 push_stat_to_all(name
, value
);
177 module
:hook("server-stopping", function ()
178 push_stat_to_all("stop_time", time_now());
181 function module
.unload()
182 filters
.remove_filter_hook(add_statistics_filter
);
185 if prosody
and prosody
.arg
then
186 module
:provides("net", {