Skip to content
This repository was archived by the owner on Oct 3, 2023. It is now read-only.

Commit 5db22c9

Browse files
authored
Plug-in Cumulative APIs into the Metric-Registry (#505)
* Plug-in Cumulative into the Metric-Registry * fix review comments * update CHANGELOG.md
1 parent 49ef54f commit 5db22c9

File tree

3 files changed

+261
-18
lines changed

3 files changed

+261
-18
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ All notable changes to this project will be documented in this file.
44

55
## Unreleased
66

7+
- Add Cumulative (`DoubleCumulative`, `Int64Cumulative`) APIs.
8+
79
**This release has a breaking change. Please test your code accordingly after upgrading.**
810

911
- removing Tracer's `startChildSpan(name?: string, kind?: types.SpanKind)` interface

packages/opencensus-core/src/metrics/metric-registry.ts

Lines changed: 79 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
import {validateArrayElementsNotNull, validateDuplicateKeys, validateMapElementNotNull, validateNotNull} from '../common/validations';
1818
import {MeasureUnit} from '../stats/types';
19+
import {Cumulative} from './cumulative/cumulative';
1920
import {BaseMetricProducer} from './export/base-metric-producer';
2021
import {Metric, MetricDescriptorType, MetricProducer} from './export/types';
2122
import {DerivedGauge} from './gauges/derived-gauge';
@@ -46,9 +47,9 @@ export class MetricRegistry {
4647
* convenient form when you want to manually increase and decrease values as
4748
* per your service requirements.
4849
*
49-
* @param {string} name The name of the metric.
50-
* @param {MetricOptions} options The options for the metric.
51-
* @returns {Gauge} A Int64 Gauge metric.
50+
* @param name The name of the metric.
51+
* @param options The options for the metric.
52+
* @returns A Int64 Gauge metric.
5253
*/
5354
addInt64Gauge(name: string, options?: MetricOptions): Gauge {
5455
const description =
@@ -77,9 +78,9 @@ export class MetricRegistry {
7778
* convenient form when you want to manually increase and decrease values as
7879
* per your service requirements.
7980
*
80-
* @param {string} name The name of the metric.
81-
* @param {MetricOptions} options The options for the metric.
82-
* @returns {Gauge} A Double Gauge metric.
81+
* @param name The name of the metric.
82+
* @param options The options for the metric.
83+
* @returns A Double Gauge metric.
8384
*/
8485
addDoubleGauge(name: string, options?: MetricOptions): Gauge {
8586
const description =
@@ -108,9 +109,9 @@ export class MetricRegistry {
108109
* convenient form when you want to manually increase and decrease values as
109110
* per your service requirements.
110111
*
111-
* @param {string} name The name of the metric.
112-
* @param {MetricOptions} options The options for the metric.
113-
* @returns {DerivedGauge} A Int64 DerivedGauge metric.
112+
* @param name The name of the metric.
113+
* @param options The options for the metric.
114+
* @returns A Int64 DerivedGauge metric.
114115
*/
115116
addDerivedInt64Gauge(name: string, options?: MetricOptions): DerivedGauge {
116117
const description =
@@ -139,9 +140,9 @@ export class MetricRegistry {
139140
* convenient form when you want to manually increase and decrease values as
140141
* per your service requirements.
141142
*
142-
* @param {string} name The name of the metric.
143-
* @param {MetricOptions} options The options for the metric.
144-
* @returns {DerivedGauge} A Double DerivedGauge metric.
143+
* @param name The name of the metric.
144+
* @param options The options for the metric.
145+
* @returns A Double DerivedGauge metric.
145146
*/
146147
addDerivedDoubleGauge(name: string, options?: MetricOptions): DerivedGauge {
147148
const description =
@@ -165,11 +166,73 @@ export class MetricRegistry {
165166
return derivedDoubleGauge;
166167
}
167168

169+
/**
170+
* Builds a new Int64 cumulative to be added to the registry. This API is
171+
* useful when you want to manually increase and reset values as per service
172+
* requirements.
173+
*
174+
* @param name The name of the metric.
175+
* @param options The options for the metric.
176+
* @returns A Int64 Cumulative metric.
177+
*/
178+
addInt64Cumulative(name: string, options?: MetricOptions): Cumulative {
179+
const description =
180+
(options && options.description) || MetricRegistry.DEFAULT_DESCRIPTION;
181+
const unit = (options && options.unit) || MetricRegistry.DEFAULT_UNIT;
182+
const labelKeys =
183+
(options && options.labelKeys) || MetricRegistry.DEFAULT_LABEL_KEYS;
184+
const constantLabels = (options && options.constantLabels) ||
185+
MetricRegistry.DEFAULT_CONSTANT_LABEL;
186+
// TODO(mayurkale): Add support for resource
187+
188+
validateArrayElementsNotNull(labelKeys, MetricRegistry.LABEL_KEY);
189+
validateMapElementNotNull(constantLabels, MetricRegistry.CONSTANT_LABELS);
190+
validateDuplicateKeys(labelKeys, constantLabels);
191+
192+
const labelKeysCopy = Object.assign([], labelKeys);
193+
const int64Cumulative = new Cumulative(
194+
validateNotNull(name, MetricRegistry.NAME), description, unit,
195+
MetricDescriptorType.CUMULATIVE_INT64, labelKeysCopy, constantLabels);
196+
this.registerMetric(name, int64Cumulative);
197+
return int64Cumulative;
198+
}
199+
200+
/**
201+
* Builds a new double cumulative to be added to the registry. This API is
202+
* useful when you want to manually increase and reset values as per service
203+
* requirements.
204+
*
205+
* @param name The name of the metric.
206+
* @param options The options for the metric.
207+
* @returns A Double Cumulative metric.
208+
*/
209+
addDoubleCumulative(name: string, options?: MetricOptions): Cumulative {
210+
const description =
211+
(options && options.description) || MetricRegistry.DEFAULT_DESCRIPTION;
212+
const unit = (options && options.unit) || MetricRegistry.DEFAULT_UNIT;
213+
const labelKeys =
214+
(options && options.labelKeys) || MetricRegistry.DEFAULT_LABEL_KEYS;
215+
const constantLabels = (options && options.constantLabels) ||
216+
MetricRegistry.DEFAULT_CONSTANT_LABEL;
217+
// TODO(mayurkale): Add support for resource
218+
219+
validateArrayElementsNotNull(labelKeys, MetricRegistry.LABEL_KEY);
220+
validateMapElementNotNull(constantLabels, MetricRegistry.CONSTANT_LABELS);
221+
validateDuplicateKeys(labelKeys, constantLabels);
222+
223+
const labelKeysCopy = Object.assign([], labelKeys);
224+
const doubleCumulative = new Cumulative(
225+
validateNotNull(name, MetricRegistry.NAME), description, unit,
226+
MetricDescriptorType.CUMULATIVE_DOUBLE, labelKeysCopy, constantLabels);
227+
this.registerMetric(name, doubleCumulative);
228+
return doubleCumulative;
229+
}
230+
168231
/**
169232
* Registers metric to register.
170233
*
171-
* @param {string} name The name of the metric.
172-
* @param {Meter} meter The metric to register.
234+
* @param name The name of the metric.
235+
* @param meter The metric to register.
173236
*/
174237
private registerMetric(name: string, meter: Meter): void {
175238
if (this.registeredMetrics.has(name)) {
@@ -182,7 +245,7 @@ export class MetricRegistry {
182245
/**
183246
* Gets a metric producer for registry.
184247
*
185-
* @returns {MetricProducer} The metric producer.
248+
* @returns The metric producer.
186249
*/
187250
getMetricProducer(): MetricProducer {
188251
return this.metricProducer;
@@ -204,7 +267,7 @@ class MetricProducerForRegistry extends BaseMetricProducer {
204267
/**
205268
* Gets a collection of produced Metric`s to be exported.
206269
*
207-
* @returns {Metric[]} The list of metrics.
270+
* @returns The list of metrics.
208271
*/
209272
getMetrics(): Metric[] {
210273
return Array.from(this.registeredMetrics.values())

packages/opencensus-core/test/test-metric-registry.ts

Lines changed: 180 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,170 @@ describe('addDerivedDoubleGauge', () => {
370370
});
371371
});
372372

373+
describe('addInt64Cumulative', () => {
374+
let registry: MetricRegistry;
375+
const realHrtimeFn = process.hrtime;
376+
const realNowFn = Date.now;
377+
const mockedTime: Timestamp = {seconds: 1450000100, nanos: 1e7};
378+
379+
beforeEach(() => {
380+
registry = new MetricRegistry();
381+
382+
process.hrtime = () => [100, 1e7];
383+
Date.now = () => 1450000000000;
384+
// Force the clock to recalibrate the time offset with the mocked time
385+
TEST_ONLY.setHrtimeReference();
386+
});
387+
388+
afterEach(() => {
389+
process.hrtime = realHrtimeFn;
390+
Date.now = realNowFn;
391+
// Reset the hrtime reference so that it uses a real clock again.
392+
TEST_ONLY.resetHrtimeFunctionCache();
393+
});
394+
395+
it('should return a metric', () => {
396+
const int64Gauge = registry.addInt64Cumulative(METRIC_NAME, METRIC_OPTIONS);
397+
const pointEntry = int64Gauge.getOrCreateTimeSeries(LABEL_VALUES_200);
398+
pointEntry.inc();
399+
400+
const metrics = registry.getMetricProducer().getMetrics();
401+
assert.strictEqual(metrics.length, 1);
402+
const [{descriptor, timeseries}] = metrics;
403+
assert.deepStrictEqual(descriptor, {
404+
name: METRIC_NAME,
405+
description: METRIC_DESCRIPTION,
406+
labelKeys: LABEL_KEYS,
407+
unit: UNIT,
408+
type: MetricDescriptorType.CUMULATIVE_INT64
409+
});
410+
assert.strictEqual(timeseries.length, 1);
411+
const [{points}] = timeseries;
412+
const [point] = points;
413+
assert.equal(point.value, 1);
414+
assert.deepStrictEqual(
415+
point.timestamp,
416+
{seconds: mockedTime.seconds, nanos: mockedTime.nanos});
417+
});
418+
419+
it('should return a metric without options', () => {
420+
const int64Gauge = registry.addInt64Cumulative(METRIC_NAME);
421+
const pointEntry = int64Gauge.getDefaultTimeSeries();
422+
pointEntry.inc(100);
423+
424+
const metrics = registry.getMetricProducer().getMetrics();
425+
assert.strictEqual(metrics.length, 1);
426+
const [{descriptor, timeseries}] = metrics;
427+
assert.deepStrictEqual(descriptor, {
428+
name: METRIC_NAME,
429+
description: '',
430+
labelKeys: [],
431+
unit: UNIT,
432+
type: MetricDescriptorType.CUMULATIVE_INT64
433+
});
434+
assert.strictEqual(timeseries.length, 1);
435+
const [{points}] = timeseries;
436+
const [point] = points;
437+
assert.equal(point.value, 100);
438+
assert.deepStrictEqual(
439+
point.timestamp,
440+
{seconds: mockedTime.seconds, nanos: mockedTime.nanos});
441+
});
442+
443+
it('should throw an error when the duplicate keys in labelKeys and constantLabels',
444+
() => {
445+
const constantLabels = new Map();
446+
constantLabels.set({key: 'k1'}, {value: 'v1'});
447+
const labelKeys = [{key: 'k1', description: 'desc'}];
448+
assert.throws(() => {
449+
registry.addInt64Cumulative(METRIC_NAME, {constantLabels, labelKeys});
450+
}, /^Error: The keys from LabelKeys should not be present in constantLabels or LabelKeys should not contains duplicate keys$/);
451+
});
452+
});
453+
454+
describe('addDoubleCumulative', () => {
455+
let registry: MetricRegistry;
456+
const realHrtimeFn = process.hrtime;
457+
const realNowFn = Date.now;
458+
const mockedTime: Timestamp = {seconds: 1450000100, nanos: 1e7};
459+
460+
beforeEach(() => {
461+
registry = new MetricRegistry();
462+
463+
process.hrtime = () => [100, 1e7];
464+
Date.now = () => 1450000000000;
465+
// Force the clock to recalibrate the time offset with the mocked time
466+
TEST_ONLY.setHrtimeReference();
467+
});
468+
469+
afterEach(() => {
470+
process.hrtime = realHrtimeFn;
471+
Date.now = realNowFn;
472+
// Reset the hrtime reference so that it uses a real clock again.
473+
TEST_ONLY.resetHrtimeFunctionCache();
474+
});
475+
476+
it('should return a metric', () => {
477+
const int64Gauge =
478+
registry.addDoubleCumulative(METRIC_NAME, METRIC_OPTIONS);
479+
const pointEntry = int64Gauge.getOrCreateTimeSeries(LABEL_VALUES_200);
480+
pointEntry.inc(1.1);
481+
482+
const metrics = registry.getMetricProducer().getMetrics();
483+
assert.strictEqual(metrics.length, 1);
484+
const [{descriptor, timeseries}] = metrics;
485+
assert.deepStrictEqual(descriptor, {
486+
name: METRIC_NAME,
487+
description: METRIC_DESCRIPTION,
488+
labelKeys: LABEL_KEYS,
489+
unit: UNIT,
490+
type: MetricDescriptorType.CUMULATIVE_DOUBLE
491+
});
492+
assert.strictEqual(timeseries.length, 1);
493+
const [{points}] = timeseries;
494+
const [point] = points;
495+
assert.equal(point.value, 1.1);
496+
assert.deepStrictEqual(
497+
point.timestamp,
498+
{seconds: mockedTime.seconds, nanos: mockedTime.nanos});
499+
});
500+
501+
it('should return a metric without options', () => {
502+
const int64Gauge = registry.addDoubleCumulative(METRIC_NAME);
503+
const pointEntry = int64Gauge.getDefaultTimeSeries();
504+
pointEntry.inc();
505+
pointEntry.inc(100.12);
506+
507+
const metrics = registry.getMetricProducer().getMetrics();
508+
assert.strictEqual(metrics.length, 1);
509+
const [{descriptor, timeseries}] = metrics;
510+
assert.deepStrictEqual(descriptor, {
511+
name: METRIC_NAME,
512+
description: '',
513+
labelKeys: [],
514+
unit: UNIT,
515+
type: MetricDescriptorType.CUMULATIVE_DOUBLE
516+
});
517+
assert.strictEqual(timeseries.length, 1);
518+
const [{points}] = timeseries;
519+
const [point] = points;
520+
assert.equal(point.value, 101.12);
521+
assert.deepStrictEqual(
522+
point.timestamp,
523+
{seconds: mockedTime.seconds, nanos: mockedTime.nanos});
524+
});
525+
526+
it('should throw an error when the duplicate keys in labelKeys and constantLabels',
527+
() => {
528+
const constantLabels = new Map();
529+
constantLabels.set({key: 'k1'}, {value: 'v1'});
530+
const labelKeys = [{key: 'k1', description: 'desc'}];
531+
assert.throws(() => {
532+
registry.addDoubleCumulative(METRIC_NAME, {constantLabels, labelKeys});
533+
}, /^Error: The keys from LabelKeys should not be present in constantLabels or LabelKeys should not contains duplicate keys$/);
534+
});
535+
});
536+
373537
describe('Add multiple gauges', () => {
374538
let registry: MetricRegistry;
375539
const realHrtimeFn = process.hrtime;
@@ -406,9 +570,13 @@ describe('Add multiple gauges', () => {
406570
size: () => arr.length,
407571
});
408572

573+
const int64Cumulative =
574+
registry.addInt64Cumulative('metric-name4', METRIC_OPTIONS);
575+
int64Cumulative.getOrCreateTimeSeries(LABEL_VALUES_200).inc();
576+
409577
const metrics = registry.getMetricProducer().getMetrics();
410-
assert.strictEqual(metrics.length, 3);
411-
const [{descriptor: descriptor1, timeseries: timeseries1}, {descriptor: descriptor2, timeseries: timeseries2}, {descriptor: descriptor3, timeseries: timeseries3}] = metrics;
578+
assert.strictEqual(metrics.length, 4);
579+
const [{descriptor: descriptor1, timeseries: timeseries1}, {descriptor: descriptor2, timeseries: timeseries2}, {descriptor: descriptor3, timeseries: timeseries3}, {descriptor: descriptor4, timeseries: timeseries4}] = metrics;
412580
assert.deepStrictEqual(descriptor1, {
413581
name: 'metric-name1',
414582
description: METRIC_DESCRIPTION,
@@ -430,6 +598,13 @@ describe('Add multiple gauges', () => {
430598
unit: UNIT,
431599
type: MetricDescriptorType.GAUGE_INT64
432600
});
601+
assert.deepStrictEqual(descriptor4, {
602+
name: 'metric-name4',
603+
description: METRIC_DESCRIPTION,
604+
labelKeys: LABEL_KEYS,
605+
unit: UNIT,
606+
type: MetricDescriptorType.CUMULATIVE_INT64
607+
});
433608
assert.strictEqual(timeseries1.length, 1);
434609
assert.strictEqual(timeseries1[0].points.length, 1);
435610
assert.equal(timeseries1[0].points[0].value, 100);
@@ -446,5 +621,8 @@ describe('Add multiple gauges', () => {
446621
timeseries1[0].points[0].timestamp, timeseries2[0].points[0].timestamp);
447622
assert.deepStrictEqual(
448623
timeseries2[0].points[0].timestamp, timeseries3[0].points[0].timestamp);
624+
assert.strictEqual(timeseries4.length, 1);
625+
assert.strictEqual(timeseries4[0].points.length, 1);
626+
assert.equal(timeseries4[0].points[0].value, 1);
449627
});
450628
});

0 commit comments

Comments
 (0)