Skip to content

Commit 8672ce4

Browse files
committed
Support for time_s and time_ms
1 parent 85f6091 commit 8672ce4

File tree

2 files changed

+108
-20
lines changed

2 files changed

+108
-20
lines changed

src/seshat.erl

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
-type name() :: term().
2929

3030
-type field_spec() :: {Name :: atom(), Index :: pos_integer(),
31-
Type :: counter | gauge | ratio, Help :: string()}.
31+
Type :: counter | gauge | ratio | time_s | time_ms , Help :: string()}.
3232

3333
-type fields_spec() :: [field_spec()] | {persistent_term, term()}.
3434

@@ -296,6 +296,8 @@ format_fields(Fields, CRef, Labels, Acc) ->
296296
fun ({Name, Index, Type, Help}, Acc0) ->
297297
ComputedType = case Type of
298298
ratio -> gauge;
299+
time_s -> gauge;
300+
time_ms -> gauge;
299301
Other -> Other
300302
end,
301303
InitialMetric = #{type => ComputedType,
@@ -304,8 +306,14 @@ format_fields(Fields, CRef, Labels, Acc) ->
304306
MetricAcc = maps:get(Name, Acc0, InitialMetric),
305307
ValuesAcc = maps:get(values, MetricAcc),
306308
ComputedValue = case Type of
307-
ratio -> counters:get(CRef, Index) / 100;
308-
_ -> counters:get(CRef, Index)
309+
ratio ->
310+
counters:get(CRef, Index) / 100;
311+
time_ms ->
312+
counters:get(CRef, Index) / 1000; % ms to s
313+
time_s ->
314+
counters:get(CRef, Index) * 1.0; % ensure float
315+
_ ->
316+
counters:get(CRef, Index)
309317
end,
310318
ValuesAcc1 = ValuesAcc#{Labels => ComputedValue},
311319
MetricAcc1 = MetricAcc#{values => ValuesAcc1},
@@ -360,20 +368,33 @@ text_format_fields(Fields, CRef, Labels, PrefixBin, Acc) ->
360368
fun ({Name, Index, Type, Help}, Acc0) ->
361369
ComputedType = case Type of
362370
ratio -> gauge;
363-
Other -> Other
371+
time_s -> gauge;
372+
time_ms -> gauge;
373+
_ -> Type
364374
end,
365375
ComputedUnit = case Type of
366376
ratio -> <<"_ratio">>;
377+
time_ms -> <<"_seconds">>;
378+
time_s -> <<"_seconds">>;
367379
_ -> <<"">>
368380
end,
369381
ComputedValue = case Type of
370-
ratio -> Ratio = counters:get(CRef, Index) / 100,
371-
Ratio1= float_to_list(Ratio, [{decimals, 2}, compact]),
372-
list_to_binary(Ratio1)
373-
;
374-
_ -> integer_to_binary(counters:get(CRef, Index))
382+
ratio ->
383+
Value = counters:get(CRef, Index) / 100,
384+
Formatted = float_to_list(Value, [{decimals, 2}, compact]),
385+
list_to_binary(Formatted);
386+
time_ms ->
387+
Value = counters:get(CRef, Index) / 1000, % ms to s
388+
Formatted = float_to_list(Value, [{decimals, 3}, compact]),
389+
list_to_binary(Formatted);
390+
time_s ->
391+
Value = counters:get(CRef, Index) * 1.0, % ensure float
392+
Formatted = float_to_list(Value, [{decimals, 2}, compact]),
393+
list_to_binary(Formatted);
394+
_ -> % counter or gauge
395+
integer_to_binary(counters:get(CRef, Index))
375396
end,
376-
NameBin = <<PrefixBin/binary, (atom_to_binary(Name, utf8))/binary, ComputedUnit/binary>>,
397+
NameBin = <<PrefixBin/binary, (atom_to_binary(Name, utf8))/binary, ComputedUnit/binary>>,
377398
Line = <<NameBin/binary, "{", LabelsBin/binary, "} ", ComputedValue/binary>>,
378399
case maps:get(Name, Acc0, <<>>) of
379400
<<>> ->

test/seshat_test.erl

Lines changed: 77 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ test_suite_test_() ->
2424
fun format_one/0,
2525
fun format_with_many_labels/0,
2626
fun format_ratio/0,
27+
fun format_time_metrics/0,
2728
fun format_selected_metrics/0,
2829
fun text_format_selected_metrics/0,
2930
fun invalid_fields/0 ]}.
@@ -92,7 +93,7 @@ counters_with_persistent_term_field_spec() ->
9293
ok.
9394

9495
format_group() ->
95-
Group = widgets,
96+
Group = ?FUNCTION_NAME,
9697
Counters = [{reads, 1, counter, "Total reads"}],
9798
seshat:new_group(Group),
9899
seshat:new(Group, widget1, Counters, #{component => widget1}),
@@ -107,7 +108,7 @@ format_group() ->
107108
ok.
108109

109110
format_one() ->
110-
Group = widgets,
111+
Group = ?FUNCTION_NAME,
111112
Counters = [{reads, 1, counter, "Total reads"}],
112113
seshat:new_group(Group),
113114
seshat:new(Group, widget1, Counters, #{component => widget1}),
@@ -120,7 +121,7 @@ format_one() ->
120121
ok.
121122

122123
format_with_many_labels() ->
123-
Group = widgets,
124+
Group = ?FUNCTION_NAME,
124125
Counters = [{reads, 1, counter, "Total reads"}],
125126
seshat:new_group(Group),
126127
seshat:new(Group, widget1, Counters, #{component => "widget1", status => up}),
@@ -136,7 +137,7 @@ format_with_many_labels() ->
136137
ok.
137138

138139
format_selected_metrics() ->
139-
Group = widgets,
140+
Group = ?FUNCTION_NAME,
140141
Counters = [
141142
{reads, 1, counter, "Total reads"},
142143
{writes, 2, counter, "Total writes"},
@@ -168,7 +169,7 @@ invalid_fields() ->
168169
ok.
169170

170171
format_ratio() ->
171-
Group = widgets,
172+
Group = ?FUNCTION_NAME,
172173
Counters = [{pings, 1, ratio, "Some ratio that happens to be 0%"},
173174
{pongs, 2, ratio, "Some ratio that happens to be 17%"},
174175
{pangs, 3, ratio, "Some ratio that happens to be 33%"},
@@ -197,27 +198,88 @@ format_ratio() ->
197198
?assertEqual(ExpectedPrometheusFormat, PrometheusFormat),
198199
ok.
199200

201+
format_time_metrics() ->
202+
Group = ?FUNCTION_NAME,
203+
Counters = [
204+
{request_latency, 1, time_ms, "Request latency"},
205+
{job_duration, 2, time_s, "Job duration"},
206+
{short_latency, 3, time_ms, "Short latency"}
207+
],
208+
seshat:new_group(Group),
209+
Labels = #{component => test},
210+
seshat:new(Group, test_component, Counters, Labels),
211+
212+
% Set values (1500 ms, 30 s, 5 ms)
213+
set_value(Group, test_component, request_latency, 1500),
214+
set_value(Group, test_component, job_duration, 30),
215+
set_value(Group, test_component, short_latency, 5),
216+
217+
MapFormat = seshat:format(Group),
218+
ExpectedMapFormat = #{
219+
request_latency => #{type => gauge,
220+
help => "Request latency",
221+
values => #{Labels => 1.5}}, % value in seconds
222+
job_duration => #{type => gauge,
223+
help => "Job duration",
224+
values => #{Labels => 30.0}}, % value in seconds
225+
short_latency => #{type => gauge,
226+
help => "Short latency",
227+
values => #{Labels => 0.005}} % value in seconds
228+
},
229+
?assertEqual(ExpectedMapFormat, MapFormat),
230+
231+
Prefix = "myapp",
232+
MetricNames = [request_latency, job_duration, short_latency], % Added new metric name
233+
ResultAsList = binary_to_list(seshat:text_format(Group, Prefix, MetricNames)),
234+
235+
% Expected format needs sorting because order isn't guaranteed
236+
ExpectedLines = [
237+
"# HELP myapp_request_latency_seconds Request latency",
238+
"# TYPE myapp_request_latency_seconds gauge",
239+
"myapp_request_latency_seconds{component=\"test\"} 1.5",
240+
"# HELP myapp_job_duration_seconds Job duration",
241+
"# TYPE myapp_job_duration_seconds gauge",
242+
"myapp_job_duration_seconds{component=\"test\"} 30.0",
243+
"# HELP myapp_short_latency_seconds Short latency",
244+
"# TYPE myapp_short_latency_seconds gauge",
245+
"myapp_short_latency_seconds{component=\"test\"} 0.005"
246+
],
247+
ExpectedSortedText = lists:sort(ExpectedLines),
248+
249+
% Split and sort the actual result for comparison
250+
ResultLines = string:split(ResultAsList, "\n", all),
251+
FilteredResultLines = [Line || Line <- ResultLines, Line /= ""],
252+
SortedResultText = lists:sort(FilteredResultLines),
253+
254+
?assertEqual(ExpectedSortedText, SortedResultText),
255+
256+
ok.
257+
200258
text_format_selected_metrics() ->
201259
Group = widgets,
202260
Counters = [
203261
{reads, 1, counter, "Total reads"},
204262
{writes, 2, counter, "Total writes"},
205-
{cached, 3, ratio, "Ratio of things served from cache"}
206-
],
263+
{cached, 3, ratio, "Ratio of things served from cache"},
264+
{latency, 4, time_ms, "Latency"}
265+
],
207266
seshat:new_group(Group),
208267
seshat:new(Group, thing1, Counters, #{component => "thing1", version => "1.2.3"}),
209268
seshat:new(Group, thing2, Counters, #{component => "thing2", some_atom => atom_value}),
210269
seshat:new(Group, thing3, Counters, #{component => "thing3", some_binary => <<"binary_value">>}),
211270
set_value(Group, thing1, reads, 1),
212271
set_value(Group, thing1, writes, 2),
213272
set_value(Group, thing1, cached, 10),
273+
set_value(Group, thing1, latency, 5),
214274
set_value(Group, thing2, reads, 3),
215275
set_value(Group, thing2, writes, 4),
216276
set_value(Group, thing2, cached, 100),
277+
set_value(Group, thing2, latency, 6),
217278
set_value(Group, thing3, reads, 1234),
218279
set_value(Group, thing3, writes, 4321),
219280
set_value(Group, thing3, cached, 17),
220-
PrometheusFormat = binary_to_list(seshat:text_format(Group, "acme", [reads, writes, cached])),
281+
set_value(Group, thing3, latency, 7),
282+
ResultAsList = binary_to_list(seshat:text_format(Group, "acme", [reads, writes, cached,latency])),
221283
ExpectedPrometheusFormat = "# HELP acme_reads Total reads\n"
222284
"# TYPE acme_reads counter\n"
223285
"acme_reads{version=\"1.2.3\",component=\"thing1\"} 1\n"
@@ -232,9 +294,14 @@ text_format_selected_metrics() ->
232294
"# TYPE acme_cached_ratio gauge\n"
233295
"acme_cached_ratio{version=\"1.2.3\",component=\"thing1\"} 0.1\n"
234296
"acme_cached_ratio{component=\"thing2\",some_atom=\"atom_value\"} 1.0\n"
235-
"acme_cached_ratio{component=\"thing3\",some_binary=\"binary_value\"} 0.17\n",
297+
"acme_cached_ratio{component=\"thing3\",some_binary=\"binary_value\"} 0.17\n"
298+
"# HELP acme_latency_seconds Latency\n"
299+
"# TYPE acme_latency_seconds gauge\n"
300+
"acme_latency_seconds{version=\"1.2.3\",component=\"thing1\"} 0.005\n"
301+
"acme_latency_seconds{component=\"thing2\",some_atom=\"atom_value\"} 0.006\n"
302+
"acme_latency_seconds{component=\"thing3\",some_binary=\"binary_value\"} 0.007\n",
236303

237-
?assertEqual(ExpectedPrometheusFormat, PrometheusFormat),
304+
?assertEqual(ExpectedPrometheusFormat, ResultAsList),
238305
ok.
239306

240307
%% test helpers

0 commit comments

Comments
 (0)