Prepare required data folder for integration tests
[prosody.git] / util / statistics.lua
blob3995465221922a6abe94f28898618d03a861cfe0
1 local t_sort = table.sort
2 local m_floor = math.floor;
3 local time = require "util.time".now;
5 local function nop_function() end
7 local function percentile(arr, length, pc)
8 local n = pc/100 * (length + 1);
9 local k, d = m_floor(n), n%1;
10 if k == 0 then
11 return arr[1] or 0;
12 elseif k >= length then
13 return arr[length];
14 end
15 return arr[k] + d*(arr[k+1] - arr[k]);
16 end
18 local function new_registry(config)
19 config = config or {};
20 local duration_sample_interval = config.duration_sample_interval or 5;
21 local duration_max_samples = config.duration_max_stored_samples or 5000;
23 local function get_distribution_stats(events, n_actual_events, since, new_time, units)
24 local n_stored_events = #events;
25 t_sort(events);
26 local sum = 0;
27 for i = 1, n_stored_events do
28 sum = sum + events[i];
29 end
31 return {
32 samples = events;
33 sample_count = n_stored_events;
34 count = n_actual_events,
35 rate = n_actual_events/(new_time-since);
36 average = n_stored_events > 0 and sum/n_stored_events or 0,
37 min = events[1] or 0,
38 max = events[n_stored_events] or 0,
39 units = units,
41 end
44 local registry = {};
45 local methods;
46 methods = {
47 amount = function (name, initial)
48 local v = initial or 0;
49 registry[name..":amount"] = function () return "amount", v; end
50 return function (new_v) v = new_v; end
51 end;
52 counter = function (name, initial)
53 local v = initial or 0;
54 registry[name..":amount"] = function () return "amount", v; end
55 return function (delta)
56 v = v + delta;
57 end;
58 end;
59 rate = function (name)
60 local since, n = time(), 0;
61 registry[name..":rate"] = function ()
62 local t = time();
63 local stats = {
64 rate = n/(t-since);
65 count = n;
67 since, n = t, 0;
68 return "rate", stats.rate, stats;
69 end;
70 return function ()
71 n = n + 1;
72 end;
73 end;
74 distribution = function (name, unit, type)
75 type = type or "distribution";
76 local events, last_event = {}, 0;
77 local n_actual_events = 0;
78 local since = time();
80 registry[name..":"..type] = function ()
81 local new_time = time();
82 local stats = get_distribution_stats(events, n_actual_events, since, new_time, unit);
83 events, last_event = {}, 0;
84 n_actual_events = 0;
85 since = new_time;
86 return type, stats.average, stats;
87 end;
89 return function (value)
90 n_actual_events = n_actual_events + 1;
91 if n_actual_events%duration_sample_interval == 1 then
92 last_event = (last_event%duration_max_samples) + 1;
93 events[last_event] = value;
94 end
95 end;
96 end;
97 sizes = function (name)
98 return methods.distribution(name, "bytes", "size");
99 end;
100 times = function (name)
101 local events, last_event = {}, 0;
102 local n_actual_events = 0;
103 local since = time();
105 registry[name..":duration"] = function ()
106 local new_time = time();
107 local stats = get_distribution_stats(events, n_actual_events, since, new_time, "seconds");
108 events, last_event = {}, 0;
109 n_actual_events = 0;
110 since = new_time;
111 return "duration", stats.average, stats;
112 end;
114 return function ()
115 n_actual_events = n_actual_events + 1;
116 if n_actual_events%duration_sample_interval ~= 1 then
117 return nop_function;
120 local start_time = time();
121 return function ()
122 local end_time = time();
123 local duration = end_time - start_time;
124 last_event = (last_event%duration_max_samples) + 1;
125 events[last_event] = duration;
127 end;
128 end;
130 get_stats = function ()
131 return registry;
132 end;
134 return methods;
137 return {
138 new = new_registry;
139 get_histogram = function (duration, n_buckets)
140 n_buckets = n_buckets or 100;
141 local events, n_events = duration.samples, duration.sample_count;
142 if not (events and n_events) then
143 return nil, "not a valid distribution stat";
145 local histogram = {};
147 for i = 1, 100, 100/n_buckets do
148 histogram[i] = percentile(events, n_events, i);
150 return histogram;
151 end;
153 get_percentile = function (duration, pc)
154 local events, n_events = duration.samples, duration.sample_count;
155 if not (events and n_events) then
156 return nil, "not a valid distribution stat";
158 return percentile(events, n_events, pc);
159 end;