Software / code / prosody
Comparison
util/statistics.lua @ 6557:8aa967c81cbc
Merge 0.10->trunk
| author | Matthew Wild <mwild1@gmail.com> |
|---|---|
| date | Wed, 21 Jan 2015 01:29:00 +0000 |
| parent | 6555:7b2d16c14659 |
| child | 6562:2b5ced5ca31f |
comparison
equal
deleted
inserted
replaced
| 6543:01cd51777abb | 6557:8aa967c81cbc |
|---|---|
| 1 local t_sort = table.sort | |
| 2 local m_floor = math.floor; | |
| 3 local time = require "socket".gettime; | |
| 4 | |
| 5 local function nop_function() end | |
| 6 | |
| 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]; | |
| 12 elseif k >= length then | |
| 13 return arr[length]; | |
| 14 end | |
| 15 return arr[k] + d*(arr[k+1] - arr[k]); | |
| 16 end | |
| 17 | |
| 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; | |
| 22 | |
| 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 | |
| 30 | |
| 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, | |
| 40 }; | |
| 41 end | |
| 42 | |
| 43 | |
| 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; | |
| 66 }; | |
| 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(); | |
| 79 | |
| 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; | |
| 88 | |
| 89 return function (value) | |
| 90 n_actual_events = n_actual_events + 1; | |
| 91 if n_actual_events%duration_sample_interval > 0 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(); | |
| 104 | |
| 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; | |
| 113 | |
| 114 return function () | |
| 115 n_actual_events = n_actual_events + 1; | |
| 116 if n_actual_events%duration_sample_interval > 0 then | |
| 117 return nop_function; | |
| 118 end | |
| 119 | |
| 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; | |
| 126 end | |
| 127 end; | |
| 128 end; | |
| 129 | |
| 130 get_stats = function () | |
| 131 return registry; | |
| 132 end; | |
| 133 }; | |
| 134 return methods; | |
| 135 end | |
| 136 | |
| 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"; | |
| 144 end | |
| 145 local histogram = {}; | |
| 146 | |
| 147 for i = 1, 100, 100/n_buckets do | |
| 148 histogram[i] = percentile(events, n_events, i); | |
| 149 end | |
| 150 return histogram; | |
| 151 end; | |
| 152 | |
| 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"; | |
| 157 end | |
| 158 return percentile(events, n_events, pc); | |
| 159 end; | |
| 160 } |