Skip to content

Commit c418fbb

Browse files
committed
add histogram and tests
1 parent 34ceb63 commit c418fbb

File tree

2 files changed

+266
-8
lines changed

2 files changed

+266
-8
lines changed

lib/histogram.js

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,21 +22,25 @@ const Histogram = class Histogram extends EventEmitter {
2222
};
2323
}
2424

25-
observe(value, ...rest) {
25+
observe(value, options = {}) {
2626
assert(
2727
typeof value === 'number',
2828
`argument 'value' to method .observe() must be of type 'number'`,
2929
);
3030

31-
const labels = this.options.labels.map((label, i) => ({
31+
const labelsObject = {
32+
...(this.options.labels || {}),
33+
...(options.labels || {}),
34+
};
35+
36+
const labels = Object.keys(labelsObject).map(label => ({
3237
name: label,
33-
value: rest[i],
38+
value: labelsObject[label],
3439
}));
3540

3641
const metric = {
3742
name: this.options.name,
3843
description: this.options.description,
39-
timestamp: Date.now() / 1000,
4044
type: 5,
4145
value,
4246
labels,
@@ -51,11 +55,14 @@ const Histogram = class Histogram extends EventEmitter {
5155
this.emit('metric', new Metric(metric));
5256
}
5357

54-
timer(...labelsTimer) {
58+
timer(options = {}) {
5559
const end = timeSpan();
56-
return (...labelsEnd) => {
57-
const lbls = labelsEnd || labelsTimer || [];
58-
this.observe(end.seconds(), ...lbls);
60+
return (opts = {}) => {
61+
const labels = {
62+
...(options.labels || {}),
63+
...(opts.labels || {}),
64+
};
65+
this.observe(end.seconds(), { labels });
5966
};
6067
}
6168
};

test/histogram.js

Lines changed: 251 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,251 @@
1+
'use strict';
2+
3+
const tap = require('tap');
4+
const lolex = require('lolex');
5+
const Histogram = require('../lib/histogram');
6+
7+
tap.test(
8+
'histogram() - creating a basic histogram without options throws',
9+
t => {
10+
t.throws(() => new Histogram());
11+
t.end();
12+
},
13+
);
14+
15+
tap.test(
16+
'histogram().observe() - calling observe with no argument throws',
17+
t => {
18+
const histogram = new Histogram({
19+
name: 'valid_name',
20+
description: 'Valid description',
21+
});
22+
t.throws(() => histogram.observe());
23+
t.end();
24+
},
25+
);
26+
27+
tap.test(
28+
'histogram() - creating a basic histogram with minimal arguments',
29+
t => {
30+
const histogram = new Histogram({
31+
name: 'valid_name',
32+
description: 'Valid description',
33+
});
34+
35+
histogram.on('metric', metric => {
36+
t.equal(metric.name, 'valid_name');
37+
t.equal(metric.description, 'Valid description');
38+
t.equal(metric.type, 5);
39+
t.equal(metric.value, 1);
40+
t.same(metric.labels, []);
41+
t.same(metric.meta, {});
42+
t.end();
43+
});
44+
45+
histogram.observe(1);
46+
},
47+
);
48+
49+
tap.test(
50+
'histogram() - creating a histogram with labels and then not populating them',
51+
t => {
52+
const histogram = new Histogram({
53+
name: 'valid_name',
54+
description: 'Valid description',
55+
labels: { first: undefined, second: undefined, third: undefined },
56+
});
57+
58+
histogram.on('metric', metric => {
59+
t.equal(metric.name, 'valid_name');
60+
t.equal(metric.description, 'Valid description');
61+
t.equal(metric.type, 5);
62+
t.equal(metric.value, 1);
63+
t.same(metric.labels, [
64+
{ name: 'first', value: undefined },
65+
{ name: 'second', value: undefined },
66+
{ name: 'third', value: undefined },
67+
]);
68+
t.same(metric.meta, {});
69+
t.end();
70+
});
71+
72+
histogram.observe(1);
73+
},
74+
);
75+
76+
tap.test(
77+
'histogram() - creating a histogram with labels and then populating them',
78+
t => {
79+
const histogram = new Histogram({
80+
name: 'valid_name',
81+
description: 'Valid description',
82+
labels: { first: undefined, second: undefined, third: undefined },
83+
});
84+
85+
histogram.on('metric', metric => {
86+
t.equal(metric.name, 'valid_name');
87+
t.equal(metric.description, 'Valid description');
88+
t.equal(metric.type, 5);
89+
t.equal(metric.value, 1);
90+
t.same(metric.labels, [
91+
{ name: 'first', value: 'this is first' },
92+
{ name: 'second', value: 'this is second' },
93+
{ name: 'third', value: 'this is third' },
94+
]);
95+
t.same(metric.meta, {});
96+
t.end();
97+
});
98+
99+
histogram.observe(1, {
100+
labels: {
101+
first: 'this is first',
102+
second: 'this is second',
103+
third: 'this is third',
104+
},
105+
});
106+
},
107+
);
108+
109+
tap.test(
110+
'histogram() - creating a histogram without labels and then populating them',
111+
t => {
112+
const histogram = new Histogram({
113+
name: 'valid_name',
114+
description: 'Valid description',
115+
});
116+
117+
histogram.on('metric', metric => {
118+
t.equal(metric.name, 'valid_name');
119+
t.equal(metric.description, 'Valid description');
120+
t.equal(metric.type, 5);
121+
t.equal(metric.value, 1);
122+
t.same(metric.labels, [
123+
{ name: 'first', value: 'this is first' },
124+
{ name: 'second', value: 'this is second' },
125+
{ name: 'third', value: 'this is third' },
126+
]);
127+
t.same(metric.meta, {});
128+
t.end();
129+
});
130+
131+
histogram.observe(1, {
132+
labels: {
133+
first: 'this is first',
134+
second: 'this is second',
135+
third: 'this is third',
136+
},
137+
});
138+
},
139+
);
140+
141+
tap.test(
142+
'histogram() - creating a histogram with some labels and then populating them others (merge)',
143+
t => {
144+
const histogram = new Histogram({
145+
name: 'valid_name',
146+
description: 'Valid description',
147+
labels: { first: 'this is first' },
148+
});
149+
150+
histogram.on('metric', metric => {
151+
t.equal(metric.name, 'valid_name');
152+
t.equal(metric.description, 'Valid description');
153+
t.equal(metric.type, 5);
154+
t.equal(metric.value, 1);
155+
t.same(metric.labels, [
156+
{ name: 'first', value: 'this is first' },
157+
{ name: 'second', value: 'this is second' },
158+
{ name: 'third', value: 'this is third' },
159+
]);
160+
t.same(metric.meta, {});
161+
t.end();
162+
});
163+
164+
histogram.observe(1, {
165+
labels: {
166+
second: 'this is second',
167+
third: 'this is third',
168+
},
169+
});
170+
},
171+
);
172+
173+
tap.test('histogram() - using timer to time a number of seconds', t => {
174+
const clock = lolex.install();
175+
176+
const histogram = new Histogram({
177+
name: 'valid_name',
178+
description: 'Valid description',
179+
labels: { first: 'this is first' },
180+
});
181+
182+
histogram.on('metric', metric => {
183+
t.equal(metric.name, 'valid_name');
184+
t.equal(metric.description, 'Valid description');
185+
t.equal(metric.type, 5);
186+
t.equal(metric.value, 0.231);
187+
t.same(metric.labels, [
188+
{ name: 'first', value: 'this is first' },
189+
{ name: 'second', value: 'this is second' },
190+
{ name: 'third', value: 'this is third' },
191+
]);
192+
t.same(metric.meta, {});
193+
t.end();
194+
});
195+
196+
const end = histogram.timer({
197+
labels: {
198+
second: 'this is second',
199+
third: 'this is third',
200+
},
201+
});
202+
203+
clock.tick(231);
204+
205+
end();
206+
207+
clock.uninstall();
208+
});
209+
210+
tap.test(
211+
'histogram() - using timer to time a number of seconds, label merge',
212+
t => {
213+
const clock = lolex.install();
214+
215+
const histogram = new Histogram({
216+
name: 'valid_name',
217+
description: 'Valid description',
218+
labels: { first: 'this is first' },
219+
});
220+
221+
histogram.on('metric', metric => {
222+
t.equal(metric.name, 'valid_name');
223+
t.equal(metric.description, 'Valid description');
224+
t.equal(metric.type, 5);
225+
t.equal(metric.value, 0.231);
226+
t.same(metric.labels, [
227+
{ name: 'first', value: 'this is first' },
228+
{ name: 'second', value: 'this is second' },
229+
{ name: 'third', value: 'this is third' },
230+
]);
231+
t.same(metric.meta, {});
232+
t.end();
233+
});
234+
235+
const end = histogram.timer({
236+
labels: {
237+
second: 'this is second',
238+
},
239+
});
240+
241+
clock.tick(231);
242+
243+
end({
244+
labels: {
245+
third: 'this is third',
246+
},
247+
});
248+
249+
clock.uninstall();
250+
},
251+
);

0 commit comments

Comments
 (0)