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

Commit

Permalink
Derived Gauge: Add support for extracting value from a function (#469)
Browse files Browse the repository at this point in the history
* Derived Gauge: Add support for extracting value from a function

* fix review comment

* Add new type ValueExtractorOrFunction

* Add strongly-typed function
  • Loading branch information
mayurkale22 committed Apr 12, 2019
1 parent 9235b50 commit 1b03ba7
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 21 deletions.
6 changes: 3 additions & 3 deletions examples/gauge/derived-gauge-quickstart.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ const { Metrics, MeasureUnit } = require('@opencensus/core');
// To instrument a queue's depth.
class QueueManager {
constructor () { this.depth = 0; }
getValue () { return this.depth; }
getPendingJobs () { return this.depth; }
addJob () { this.depth++; }
}

Expand All @@ -54,6 +54,6 @@ const gauge = metricRegistry.addDerivedInt64Gauge('active_handles_total', metric
const queue = new QueueManager();
queue.addJob();

// The value of the gauge is observed from the obj whenever metrics are
// The value of the gauge is observed from a function whenever metrics are
// collected. In this case it will be 1.
gauge.createTimeSeries(labelValues, queue);
gauge.createTimeSeries(labelValues, () => queue.getPendingJobs());
47 changes: 29 additions & 18 deletions packages/opencensus-core/src/metrics/gauges/derived-gauge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,13 @@ interface GaugeEntry {
readonly extractor: ValueExtractor;
}

export type AccessorInterface = LengthAttributeInterface|LengthMethodInterface|
SizeAttributeInterface|SizeMethodInterface|ToValueInterface;
interface AccessorFunction {
(): number;
}

export type AccessorInterface =
LengthAttributeInterface|LengthMethodInterface|SizeAttributeInterface|
SizeMethodInterface|ToValueInterface|AccessorFunction;

/**
* DerivedGauge metric
Expand Down Expand Up @@ -145,19 +150,23 @@ export class DerivedGauge implements types.Meter {

/**
* Creates a TimeSeries. The value of a single point in the TimeSeries is
* observed from a obj. The ValueExtractor is invoked whenever
* observed from a obj or a function. The ValueExtractor is invoked whenever
* metrics are collected, meaning the reported value is up-to-date.
*
* @param {LabelValue[]} labelValues The list of the label values.
* @param obj The obj to get the size or length or value from. If multiple
* options are available, the value (ToValueInterface) takes precedence
* first, followed by length and size. e.g value -> length -> size.
* @param objOrFn obj The obj to get the size or length or value from. If
* multiple options are available, the value (ToValueInterface) takes
* precedence first, followed by length and size. e.g value -> length ->
* size.
* fn is the function that will be called to get the current value
* of the gauge.
*/
createTimeSeries(labelValues: LabelValue[], obj: AccessorInterface): void {
createTimeSeries(labelValues: LabelValue[], objOrFn: AccessorInterface):
void {
validateArrayElementsNotNull(
validateNotNull(labelValues, DerivedGauge.LABEL_VALUES),
DerivedGauge.LABEL_VALUE);
validateNotNull(obj, DerivedGauge.OBJECT);
validateNotNull(objOrFn, DerivedGauge.OBJECT);

const hash = hashLabelValues(labelValues);
if (this.registeredPoints.has(hash)) {
Expand All @@ -167,16 +176,18 @@ export class DerivedGauge implements types.Meter {
throw new Error(DerivedGauge.ERROR_MESSAGE_INVALID_SIZE);
}

if (DerivedGauge.isToValueInterface(obj)) {
this.extractor = () => obj.getValue();
} else if (DerivedGauge.isLengthAttributeInterface(obj)) {
this.extractor = () => obj.length;
} else if (DerivedGauge.isLengthMethodInterface(obj)) {
this.extractor = () => obj.length();
} else if (DerivedGauge.isSizeAttributeInterface(obj)) {
this.extractor = () => obj.size;
} else if (DerivedGauge.isSizeMethodInterface(obj)) {
this.extractor = () => obj.size();
if (objOrFn instanceof Function) {
this.extractor = objOrFn;
} else if (DerivedGauge.isToValueInterface(objOrFn)) {
this.extractor = () => objOrFn.getValue();
} else if (DerivedGauge.isLengthAttributeInterface(objOrFn)) {
this.extractor = () => objOrFn.length;
} else if (DerivedGauge.isLengthMethodInterface(objOrFn)) {
this.extractor = () => objOrFn.length();
} else if (DerivedGauge.isSizeAttributeInterface(objOrFn)) {
this.extractor = () => objOrFn.size;
} else if (DerivedGauge.isSizeMethodInterface(objOrFn)) {
this.extractor = () => objOrFn.size();
} else {
throw new Error(DerivedGauge.ERROR_MESSAGE_UNKNOWN_INTERFACE);
}
Expand Down
44 changes: 44 additions & 0 deletions packages/opencensus-core/test/test-derived-gauge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,50 @@ describe('DerivedGauge', () => {
}]
}]);
});

it('should return a Metric value from a function', () => {
class QueueManager {
private depth = 0;
get pendingJobs() {
return this.depth;
}
addJob() {
this.depth++;
}
}
const queue = new QueueManager();
queue.addJob();
instance.createTimeSeries(LABEL_VALUES_200, () => {
return queue.pendingJobs;
});

let metric = instance.getMetric();
assert.notEqual(metric, null);
assert.deepStrictEqual(metric!.descriptor, expectedMetricDescriptor);
assert.equal(metric!.timeseries.length, 1);
assert.deepStrictEqual(
metric!.timeseries, [{
labelValues: LABEL_VALUES_200,
points: [{
value: 1,
timestamp:
{nanos: mockedTime.nanos, seconds: mockedTime.seconds}
}]
}]);
// Simulate a adding multiple jobs in queue
queue.addJob();
queue.addJob();
queue.addJob();
metric = instance.getMetric();
assert.equal(metric!.timeseries.length, 1);
assert.deepStrictEqual(
metric!.timeseries[0].points, [{
value: 4,
timestamp:
{nanos: mockedTime.nanos, seconds: mockedTime.seconds}
}]);
});

it('should return a Metric (Double) - custom object', () => {
class QueueManager {
getValue(): number {
Expand Down

0 comments on commit 1b03ba7

Please sign in to comment.