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

Commit 6f5c6b3

Browse files
authored
Gauge: Add support for constant labels (#468)
* Gauge: Add support for constant labels * update CHANGELOG.md * fix review comments 1. Remove map -> constantLabels 2. Make labels values and keys merging code consistent. 3. Update error message * use to test for both null and undefined
1 parent f219ec7 commit 6f5c6b3

File tree

8 files changed

+208
-29
lines changed

8 files changed

+208
-29
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ All notable changes to this project will be documented in this file.
44

55
## Unreleased
66

7-
7+
- Gauge: Add support for constant labels.
88

99
## 0.0.10 - 2019-04-03
1010
- Add optional `compressedSize` and `uncompressedSize` params to `Span.addMessageEvent`

packages/opencensus-core/src/common/validations.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
* limitations under the License.
1515
*/
1616

17+
import {LabelKey, LabelValue} from '../metrics/export/types';
18+
1719
/**
1820
* Validates that an object reference passed as a parameter to the calling
1921
* method is not null.
@@ -43,3 +45,24 @@ export function validateArrayElementsNotNull<T>(
4345
throw new Error(`${errorMessage} elements should not be a NULL`);
4446
}
4547
}
48+
49+
/** Throws an error if any of the map elements is null. */
50+
export function validateMapElementNotNull<T>(
51+
map: Map<T, T>, errorMessage: string) {
52+
for (const [key, value] of map.entries()) {
53+
if (key == null || value == null) {
54+
throw new Error(`${errorMessage} elements should not be a NULL`);
55+
}
56+
}
57+
}
58+
59+
/** Throws an error if any of the array element present in the map. */
60+
export function validateDuplicateKeys(
61+
keys: LabelKey[], constantLabels: Map<LabelKey, LabelValue>) {
62+
const keysAndConstantKeys =
63+
new Set([...keys, ...constantLabels.keys()].map(k => k.key));
64+
if (keysAndConstantKeys.size !== (keys.length + constantLabels.size)) {
65+
throw new Error(
66+
`The keys from LabelKeys should not be present in constantLabels or LabelKeys should not contains duplicate keys`);
67+
}
68+
}

packages/opencensus-core/src/metrics/gauges/derived-gauge.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ export class DerivedGauge implements types.Meter {
7373
private labelKeysLength: number;
7474
private registeredPoints: Map<string, GaugeEntry> = new Map();
7575
private extractor?: ValueExtractor;
76+
private readonly constantLabelValues: LabelValue[];
7677

7778
private static readonly LABEL_VALUE = 'labelValue';
7879
private static readonly LABEL_VALUES = 'labelValues';
@@ -94,12 +95,19 @@ export class DerivedGauge implements types.Meter {
9495
* @param {string} unit The unit of the metric.
9596
* @param {MetricDescriptorType} type The type of metric.
9697
* @param {LabelKey[]} labelKeys The list of the label keys.
98+
* @param {Map<LabelKey, LabelValue>} constantLabels The map of constant
99+
* labels for the Metric.
97100
*/
98101
constructor(
99102
name: string, description: string, unit: string,
100-
type: MetricDescriptorType, labelKeys: LabelKey[]) {
101-
this.metricDescriptor = {name, description, unit, type, labelKeys};
103+
type: MetricDescriptorType, labelKeys: LabelKey[],
104+
readonly constantLabels: Map<LabelKey, LabelValue>) {
102105
this.labelKeysLength = labelKeys.length;
106+
const keysAndConstantKeys = [...labelKeys, ...constantLabels.keys()];
107+
this.constantLabelValues = [...constantLabels.values()];
108+
109+
this.metricDescriptor =
110+
{name, description, unit, type, labelKeys: keysAndConstantKeys};
103111
}
104112

105113
// Checks if the specified collection is a LengthAttributeInterface.
@@ -211,7 +219,8 @@ export class DerivedGauge implements types.Meter {
211219
timeseries: Array.from(
212220
this.registeredPoints,
213221
([_, gaugeEntry]) => ({
214-
labelValues: gaugeEntry.labelValues,
222+
labelValues:
223+
[...gaugeEntry.labelValues, ...this.constantLabelValues],
215224
points: [{value: gaugeEntry.extractor(), timestamp}]
216225
} as TimeSeries))
217226
};

packages/opencensus-core/src/metrics/gauges/gauge.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ export class Gauge implements types.Meter {
2828
private labelKeysLength: number;
2929
private defaultLabelValues: LabelValue[];
3030
private registeredPoints: Map<string, types.Point> = new Map();
31+
private readonly constantLabelValues: LabelValue[];
3132

3233
private static readonly LABEL_VALUE = 'labelValue';
3334
private static readonly LABEL_VALUES = 'labelValues';
@@ -42,12 +43,19 @@ export class Gauge implements types.Meter {
4243
* @param {string} unit The unit of the metric.
4344
* @param {MetricDescriptorType} type The type of metric.
4445
* @param {LabelKey[]} labelKeys The list of the label keys.
46+
* @param {Map<LabelKey, LabelValue>} constantLabels The map of constant
47+
* labels for the Metric.
4548
*/
4649
constructor(
4750
name: string, description: string, unit: string,
48-
type: MetricDescriptorType, readonly labelKeys: LabelKey[]) {
49-
this.metricDescriptor = {name, description, unit, type, labelKeys};
51+
type: MetricDescriptorType, readonly labelKeys: LabelKey[],
52+
readonly constantLabels: Map<LabelKey, LabelValue>) {
5053
this.labelKeysLength = labelKeys.length;
54+
const keysAndConstantKeys = [...labelKeys, ...constantLabels.keys()];
55+
this.constantLabelValues = [...constantLabels.values()];
56+
57+
this.metricDescriptor =
58+
{name, description, unit, type, labelKeys: keysAndConstantKeys};
5159
this.defaultLabelValues = initializeDefaultLabels(this.labelKeysLength);
5260
}
5361

@@ -116,7 +124,7 @@ export class Gauge implements types.Meter {
116124
throw new Error(Gauge.ERROR_MESSAGE_INVALID_SIZE);
117125
}
118126

119-
const point = new PointEntry(labelValues);
127+
const point = new PointEntry([...labelValues, ...this.constantLabelValues]);
120128
this.registeredPoints.set(hash, point);
121129
return point;
122130
}

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

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@
1414
* limitations under the License.
1515
*/
1616

17-
import {validateArrayElementsNotNull, validateNotNull} from '../common/validations';
18-
import {MeasureUnit,} from '../stats/types';
17+
import {validateArrayElementsNotNull, validateDuplicateKeys, validateMapElementNotNull, validateNotNull} from '../common/validations';
18+
import {MeasureUnit} from '../stats/types';
1919
import {BaseMetricProducer} from './export/base-metric-producer';
2020
import {Metric, MetricDescriptorType, MetricProducer} from './export/types';
2121
import {DerivedGauge} from './gauges/derived-gauge';
@@ -31,9 +31,11 @@ export class MetricRegistry {
3131

3232
private static readonly NAME = 'name';
3333
private static readonly LABEL_KEY = 'labelKey';
34+
private static readonly CONSTANT_LABELS = 'constantLabels';
3435
private static readonly DEFAULT_DESCRIPTION = '';
3536
private static readonly DEFAULT_UNIT = MeasureUnit.UNIT;
3637
private static readonly DEFAULT_LABEL_KEYS = [];
38+
private static readonly DEFAULT_CONSTANT_LABEL = new Map();
3739

3840
constructor() {
3941
this.metricProducer = new MetricProducerForRegistry(this.registeredMetrics);
@@ -54,13 +56,18 @@ export class MetricRegistry {
5456
const unit = (options && options.unit) || MetricRegistry.DEFAULT_UNIT;
5557
const labelKeys =
5658
(options && options.labelKeys) || MetricRegistry.DEFAULT_LABEL_KEYS;
57-
// TODO (mayurkale): Add support for constantLabels
59+
const constantLabels = (options && options.constantLabels) ||
60+
MetricRegistry.DEFAULT_CONSTANT_LABEL;
61+
// TODO(mayurkale): Add support for resource
5862

5963
validateArrayElementsNotNull(labelKeys, MetricRegistry.LABEL_KEY);
64+
validateMapElementNotNull(constantLabels, MetricRegistry.CONSTANT_LABELS);
65+
validateDuplicateKeys(labelKeys, constantLabels);
66+
6067
const labelKeysCopy = Object.assign([], labelKeys);
6168
const int64Gauge = new Gauge(
6269
validateNotNull(name, MetricRegistry.NAME), description, unit,
63-
MetricDescriptorType.GAUGE_INT64, labelKeysCopy);
70+
MetricDescriptorType.GAUGE_INT64, labelKeysCopy, constantLabels);
6471
this.registerMetric(name, int64Gauge);
6572
return int64Gauge;
6673
}
@@ -80,13 +87,18 @@ export class MetricRegistry {
8087
const unit = (options && options.unit) || MetricRegistry.DEFAULT_UNIT;
8188
const labelKeys =
8289
(options && options.labelKeys) || MetricRegistry.DEFAULT_LABEL_KEYS;
83-
// TODO (mayurkale): Add support for constantLabels
90+
const constantLabels = (options && options.constantLabels) ||
91+
MetricRegistry.DEFAULT_CONSTANT_LABEL;
92+
// TODO(mayurkale): Add support for resource
8493

8594
validateArrayElementsNotNull(labelKeys, MetricRegistry.LABEL_KEY);
95+
validateMapElementNotNull(constantLabels, MetricRegistry.CONSTANT_LABELS);
96+
validateDuplicateKeys(labelKeys, constantLabels);
97+
8698
const labelKeysCopy = Object.assign([], labelKeys);
8799
const doubleGauge = new Gauge(
88100
validateNotNull(name, MetricRegistry.NAME), description, unit,
89-
MetricDescriptorType.GAUGE_DOUBLE, labelKeysCopy);
101+
MetricDescriptorType.GAUGE_DOUBLE, labelKeysCopy, constantLabels);
90102
this.registerMetric(name, doubleGauge);
91103
return doubleGauge;
92104
}
@@ -106,13 +118,18 @@ export class MetricRegistry {
106118
const unit = (options && options.unit) || MetricRegistry.DEFAULT_UNIT;
107119
const labelKeys =
108120
(options && options.labelKeys) || MetricRegistry.DEFAULT_LABEL_KEYS;
109-
// TODO (mayurkale): Add support for constantLabels
121+
const constantLabels = (options && options.constantLabels) ||
122+
MetricRegistry.DEFAULT_CONSTANT_LABEL;
123+
// TODO(mayurkale): Add support for resource
110124

111125
validateArrayElementsNotNull(labelKeys, MetricRegistry.LABEL_KEY);
126+
validateMapElementNotNull(constantLabels, MetricRegistry.CONSTANT_LABELS);
127+
validateDuplicateKeys(labelKeys, constantLabels);
128+
112129
const labelKeysCopy = Object.assign([], labelKeys);
113130
const derivedInt64Gauge = new DerivedGauge(
114131
validateNotNull(name, MetricRegistry.NAME), description, unit,
115-
MetricDescriptorType.GAUGE_INT64, labelKeysCopy);
132+
MetricDescriptorType.GAUGE_INT64, labelKeysCopy, constantLabels);
116133
this.registerMetric(name, derivedInt64Gauge);
117134
return derivedInt64Gauge;
118135
}
@@ -132,13 +149,18 @@ export class MetricRegistry {
132149
const unit = (options && options.unit) || MetricRegistry.DEFAULT_UNIT;
133150
const labelKeys =
134151
(options && options.labelKeys) || MetricRegistry.DEFAULT_LABEL_KEYS;
135-
// TODO (mayurkale): Add support for constantLabels
152+
const constantLabels = (options && options.constantLabels) ||
153+
MetricRegistry.DEFAULT_CONSTANT_LABEL;
154+
// TODO(mayurkale): Add support for resource
136155

137156
validateArrayElementsNotNull(labelKeys, MetricRegistry.LABEL_KEY);
157+
validateMapElementNotNull(constantLabels, MetricRegistry.CONSTANT_LABELS);
158+
validateDuplicateKeys(labelKeys, constantLabels);
159+
138160
const labelKeysCopy = Object.assign([], labelKeys);
139161
const derivedDoubleGauge = new DerivedGauge(
140162
validateNotNull(name, MetricRegistry.NAME), description, unit,
141-
MetricDescriptorType.GAUGE_DOUBLE, labelKeysCopy);
163+
MetricDescriptorType.GAUGE_DOUBLE, labelKeysCopy, constantLabels);
142164
this.registerMetric(name, derivedDoubleGauge);
143165
return derivedDoubleGauge;
144166
}

packages/opencensus-core/test/test-derived-gauge.ts

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
*/
1616

1717
import * as assert from 'assert';
18-
1918
import {TEST_ONLY} from '../src/common/time-util';
2019
import {LabelKey, LabelValue, MetricDescriptorType, Timestamp} from '../src/metrics/export/types';
2120
import {DerivedGauge} from '../src/metrics/gauges/derived-gauge';
@@ -29,6 +28,9 @@ const LABEL_KEYS: LabelKey[] = [{key: 'code', description: 'desc'}];
2928
const LABEL_VALUES_200: LabelValue[] = [{value: '200'}];
3029
const LABEL_VALUES_400: LabelValue[] = [{value: '400'}];
3130
const LABEL_VALUES_EXRTA: LabelValue[] = [{value: '200'}, {value: '400'}];
31+
const EMPTY_CONSTANT_LABELS = new Map();
32+
const CONSTANT_LABELS = new Map();
33+
CONSTANT_LABELS.set({key: 'host', description: 'host'}, {value: 'localhost'});
3234

3335
describe('DerivedGauge', () => {
3436
let instance: DerivedGauge;
@@ -45,7 +47,8 @@ describe('DerivedGauge', () => {
4547

4648
beforeEach(() => {
4749
instance = new DerivedGauge(
48-
METRIC_NAME, METRIC_DESCRIPTION, UNIT, GAUGE_INT64, LABEL_KEYS);
50+
METRIC_NAME, METRIC_DESCRIPTION, UNIT, GAUGE_INT64, LABEL_KEYS,
51+
EMPTY_CONSTANT_LABELS);
4952

5053
process.hrtime = () => [100, 1e7];
5154
Date.now = () => 1450000000000;
@@ -148,7 +151,8 @@ describe('DerivedGauge', () => {
148151
}
149152
const obj = new QueueManager();
150153
const doubleInstance = new DerivedGauge(
151-
METRIC_NAME, METRIC_DESCRIPTION, UNIT, GAUGE_DOUBLE, LABEL_KEYS);
154+
METRIC_NAME, METRIC_DESCRIPTION, UNIT, GAUGE_DOUBLE, LABEL_KEYS,
155+
EMPTY_CONSTANT_LABELS);
152156
doubleInstance.createTimeSeries(LABEL_VALUES_200, obj);
153157
const metric = doubleInstance.getMetric();
154158
assert.notEqual(metric, null);
@@ -171,6 +175,39 @@ describe('DerivedGauge', () => {
171175
}]);
172176
});
173177

178+
it('should return a Metric (Double) - custom object', () => {
179+
class QueueManager {
180+
getValue(): number {
181+
return 0.7;
182+
}
183+
}
184+
const obj = new QueueManager();
185+
const doubleInstance = new DerivedGauge(
186+
METRIC_NAME, METRIC_DESCRIPTION, UNIT, GAUGE_DOUBLE, LABEL_KEYS,
187+
CONSTANT_LABELS);
188+
doubleInstance.createTimeSeries(LABEL_VALUES_200, obj);
189+
const metric = doubleInstance.getMetric();
190+
assert.notEqual(metric, null);
191+
assert.deepStrictEqual(metric!.descriptor, {
192+
name: METRIC_NAME,
193+
description: METRIC_DESCRIPTION,
194+
unit: UNIT,
195+
type: GAUGE_DOUBLE,
196+
labelKeys: [...LABEL_KEYS, ...Array.from(CONSTANT_LABELS.keys())]
197+
});
198+
assert.equal(metric!.timeseries.length, 1);
199+
assert.deepStrictEqual(
200+
metric!.timeseries, [{
201+
labelValues:
202+
[...LABEL_VALUES_200, ...Array.from(CONSTANT_LABELS.values())],
203+
points: [{
204+
value: 0.7,
205+
timestamp:
206+
{nanos: mockedTime.nanos, seconds: mockedTime.seconds}
207+
}]
208+
}]);
209+
});
210+
174211
it('should not create same timeseries again', () => {
175212
const map = new Map();
176213
instance.createTimeSeries(LABEL_VALUES_200, map);

0 commit comments

Comments
 (0)