aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/statsmanager.lua93
-rw-r--r--net/http.lua6
-rw-r--r--util-src/table.c15
-rw-r--r--util/statsd.lua84
4 files changed, 168 insertions, 30 deletions
diff --git a/core/statsmanager.lua b/core/statsmanager.lua
index 7771a2b3..9572f68d 100644
--- a/core/statsmanager.lua
+++ b/core/statsmanager.lua
@@ -1,5 +1,4 @@
-local stats = require "util.statistics".new();
local config = require "core.configmanager";
local log = require "util.logger".init("stats");
local timer = require "util.timer";
@@ -11,46 +10,87 @@ if stats_config and not stats_interval then
log("error", "Invalid 'statistics_interval' setting, statistics will be disabled");
end
+local stats_provider_config = config.get("*", "statistics_provider");
+local stats_provider = stats_provider_config or "internal";
+
+local builtin_providers = {
+ internal = "util.statistics";
+ statsd = "util.statsd";
+};
+
+if stats_provider:match("^library:") then
+ stats_provider = stats_provider:match(":(.+)$");
+else
+ stats_provider = builtin_providers[stats_provider];
+ if not stats_provider then
+ log("error", "Unrecognized built-in statistics provider '%s', using internal instead", stats_provider_config);
+ stats_provider = builtin_providers["internal"];
+ end
+end
+
+local have_stats_provider, stats_lib = pcall(require, stats_provider);
+
+local stats, stats_err;
+
+if not have_stats_provider then
+ stats, stats_err = nil, stats_lib;
+else
+ local stats_config = config.get("*", "statistics_config");
+ stats, stats_err = stats_lib.new(stats_config);
+end
+
+if not stats then
+ log("error", "Error loading statistics provider '%s': %s", stats_provider, stats_err);
+end
+
local measure, collect;
local latest_stats = {};
local changed_stats = {};
local stats_extra = {};
-if stats_interval then
- log("debug", "Statistics collection is enabled every %d seconds", stats_interval);
+if stats then
function measure(type, name)
local f = assert(stats[type], "unknown stat type: "..type);
return f(name);
end
+end
- local mark_collection_start = measure("times", "stats.collection");
- local mark_processing_start = measure("times", "stats.processing");
+if stats_interval then
+ if stats.get_stats then
+ log("debug", "Statistics collection is enabled every %d seconds", stats_interval);
- function collect()
- local mark_collection_done = mark_collection_start();
- fire_event("stats-update");
- changed_stats, stats_extra = {}, {};
- for stat_name, getter in pairs(stats.get_stats()) do
- local type, value, extra = getter();
- local old_value = latest_stats[stat_name];
- latest_stats[stat_name] = value;
- if value ~= old_value then
- changed_stats[stat_name] = value;
- end
- if extra then
- stats_extra[stat_name] = extra;
+ local mark_collection_start = measure("times", "stats.collection");
+ local mark_processing_start = measure("times", "stats.processing");
+
+ function collect()
+ local mark_collection_done = mark_collection_start();
+ fire_event("stats-update");
+ changed_stats, stats_extra = {}, {};
+ for stat_name, getter in pairs(stats.get_stats()) do
+ local type, value, extra = getter();
+ local old_value = latest_stats[stat_name];
+ latest_stats[stat_name] = value;
+ if value ~= old_value then
+ changed_stats[stat_name] = value;
+ end
+ if extra then
+ stats_extra[stat_name] = extra;
+ end
end
+ mark_collection_done();
+ local mark_processing_done = mark_processing_start();
+ fire_event("stats-updated", { stats = latest_stats, changed_stats = changed_stats, stats_extra = stats_extra });
+ mark_processing_done();
+ return stats_interval;
end
- mark_collection_done();
- local mark_processing_done = mark_processing_start();
- fire_event("stats-updated", { stats = latest_stats, changed_stats = changed_stats, stats_extra = stats_extra });
- mark_processing_done();
- return stats_interval;
+ timer.add_task(stats_interval, collect);
+ prosody.events.add_handler("server-started", function () collect() end, -1);
+ else
+ log("error", "statistics_interval specified, but the selected statistics_provider (%s) does not support statistics collection", stats_provider_config or "internal");
end
+end
- timer.add_task(stats_interval, collect);
- prosody.events.add_handler("server-started", function () collect() end, -1);
-else
+if not stats_interval and stats_provider == "util.statistics" then
log("debug", "Statistics collection is disabled");
-- nop
function measure()
@@ -62,7 +102,6 @@ end
return {
measure = measure;
- collect = collect;
get_stats = function ()
return latest_stats, changed_stats, stats_extra;
end;
diff --git a/net/http.lua b/net/http.lua
index 669fa9a5..5c1f2058 100644
--- a/net/http.lua
+++ b/net/http.lua
@@ -117,7 +117,7 @@ end
local function handleerr(err) log("error", "Traceback[http]: %s", traceback(tostring(err), 2)); end
local function log_if_failed(id, ret, ...)
if not ret then
- log("error", "Request %s: error in callback: %s", id, tostring((...)));
+ log("error", "Request '%s': error in callback: %s", id, tostring((...)));
end
return ...;
end
@@ -172,7 +172,7 @@ local function request(u, ex, callback)
end
end
- log("debug", "Making %s %s request %s to %s", req.scheme, method or "GET", req.id, (ex and ex.suppress_url and host_header) or u);
+ log("debug", "Making %s %s request '%s' to %s", req.scheme:upper(), method or "GET", req.id, (ex and ex.suppress_url and host_header) or u);
-- Attach to request object
req.method, req.headers, req.body = method, headers, body;
@@ -197,7 +197,7 @@ local function request(u, ex, callback)
req.write = function (...) return req.handler:write(...); end
req.callback = function (content, code, request, response)
- log("debug", "request %s: Calling callback, status %s", req.id, code or "---");
+ log("debug", "Request '%s': Calling callback, status %s", req.id, code or "---");
return log_if_failed(req.id, xpcall(function () return callback(content, code, request, response) end, handleerr));
end
req.reader = request_reader;
diff --git a/util-src/table.c b/util-src/table.c
index 6ad45dce..423f8f29 100644
--- a/util-src/table.c
+++ b/util-src/table.c
@@ -6,9 +6,24 @@ static int Lcreate_table(lua_State* L) {
return 1;
}
+static int Lpack(lua_State* L) {
+ unsigned int n_args = lua_gettop(L);
+ lua_createtable(L, n_args, 1);
+ lua_insert(L, 1);
+ for(int arg = n_args; arg >= 1; arg--) {
+ lua_rawseti(L, 1, arg);
+ }
+ lua_pushinteger(L, n_args);
+ lua_setfield(L, -2, "n");
+ return 1;
+}
+
+
int luaopen_util_table(lua_State* L) {
lua_newtable(L);
lua_pushcfunction(L, Lcreate_table);
lua_setfield(L, -2, "create");
+ lua_pushcfunction(L, Lpack);
+ lua_setfield(L, -2, "pack");
return 1;
}
diff --git a/util/statsd.lua b/util/statsd.lua
new file mode 100644
index 00000000..2874e8a8
--- /dev/null
+++ b/util/statsd.lua
@@ -0,0 +1,84 @@
+local socket = require "socket";
+
+local time = require "socket".gettime;
+
+local function new(config)
+ if not config or not config.statsd_server then
+ return nil, "No statsd server specified in the config, please see https://prosody.im/doc/statistics";
+ end
+
+ local sock = socket.udp();
+ sock:setpeername(config.statsd_server, config.statsd_port or 8125);
+
+ local prefix = (config.prefix or "prosody")..".";
+
+ local function send_metric(s)
+ return sock:send(prefix..s);
+ end
+
+ local function send_gauge(name, amount, relative)
+ local s_amount = tostring(amount);
+ if relative and amount > 0 then
+ s_amount = "+"..s_amount;
+ end
+ return send_metric(name..":"..s_amount.."|g");
+ end
+
+ local function send_counter(name, amount)
+ return send_metric(name..":"..tostring(amount).."|c");
+ end
+
+ local function send_duration(name, duration)
+ return send_metric(name..":"..tostring(duration).."|ms");
+ end
+
+ local function send_histogram_sample(name, sample)
+ return send_metric(name..":"..tostring(sample).."|h");
+ end
+
+ local methods;
+ methods = {
+ amount = function (name, initial)
+ if initial then
+ send_gauge(name, initial);
+ end
+ return function (new_v) send_gauge(name, new_v); end
+ end;
+ counter = function (name, initial)
+ return function (delta)
+ send_gauge(name, delta, true);
+ end;
+ end;
+ rate = function (name)
+ return function ()
+ send_counter(name, 1);
+ end;
+ end;
+ distribution = function (name, unit, type) --luacheck: ignore 212/unit 212/type
+ return function (value)
+ send_histogram_sample(name, value);
+ end;
+ end;
+ sizes = function (name)
+ name = name.."_size";
+ return function (value)
+ send_histogram_sample(name, value);
+ end;
+ end;
+ times = function (name)
+ return function ()
+ local start_time = time();
+ return function ()
+ local end_time = time();
+ local duration = end_time - start_time;
+ send_duration(name, duration*1000);
+ end
+ end;
+ end;
+ };
+ return methods;
+end
+
+return {
+ new = new;
+}