diff options
-rw-r--r-- | core/statsmanager.lua | 93 | ||||
-rw-r--r-- | net/http.lua | 6 | ||||
-rw-r--r-- | util-src/table.c | 15 | ||||
-rw-r--r-- | util/statsd.lua | 84 |
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; +} |