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;
12 elseif k
>= length
then
15 return arr
[k
] + d
*(arr
[k
+1] - arr
[k
]);
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
;
27 for i
= 1, n_stored_events
do
28 sum
= sum
+ events
[i
];
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,
38 max = events
[n_stored_events
] or 0,
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
52 counter
= function (name
, initial
)
53 local v
= initial
or 0;
54 registry
[name
..":amount"] = function () return "amount", v
; end
55 return function (delta
)
59 rate
= function (name
)
60 local since
, n
= time(), 0;
61 registry
[name
..":rate"] = function ()
68 return "rate", stats
.rate
, stats
;
74 distribution
= function (name
, unit
, type)
75 type = type or "distribution";
76 local events
, last_event
= {}, 0;
77 local n_actual_events
= 0;
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;
86 return type, stats
.average
, stats
;
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
;
97 sizes
= function (name
)
98 return methods
.distribution(name
, "bytes", "size");
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;
111 return "duration", stats
.average
, stats
;
115 n_actual_events
= n_actual_events
+ 1;
116 if n_actual_events
%duration_sample_interval
~= 1 then
120 local start_time
= time();
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
;
130 get_stats
= function ()
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
);
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
);