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

Commit

Permalink
Gauge: Add support for constant labels (#468)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
mayurkale22 committed Apr 4, 2019
1 parent f219ec7 commit 6f5c6b3
Show file tree
Hide file tree
Showing 8 changed files with 208 additions and 29 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ All notable changes to this project will be documented in this file.

## Unreleased


- Gauge: Add support for constant labels.

## 0.0.10 - 2019-04-03
- Add optional `compressedSize` and `uncompressedSize` params to `Span.addMessageEvent`
Expand Down
23 changes: 23 additions & 0 deletions packages/opencensus-core/src/common/validations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
* limitations under the License.
*/

import {LabelKey, LabelValue} from '../metrics/export/types';

/**
* Validates that an object reference passed as a parameter to the calling
* method is not null.
Expand Down Expand Up @@ -43,3 +45,24 @@ export function validateArrayElementsNotNull<T>(
throw new Error(`${errorMessage} elements should not be a NULL`);
}
}

/** Throws an error if any of the map elements is null. */
export function validateMapElementNotNull<T>(
map: Map<T, T>, errorMessage: string) {
for (const [key, value] of map.entries()) {
if (key == null || value == null) {
throw new Error(`${errorMessage} elements should not be a NULL`);
}
}
}

/** Throws an error if any of the array element present in the map. */
export function validateDuplicateKeys(
keys: LabelKey[], constantLabels: Map<LabelKey, LabelValue>) {
const keysAndConstantKeys =
new Set([...keys, ...constantLabels.keys()].map(k => k.key));
if (keysAndConstantKeys.size !== (keys.length + constantLabels.size)) {
throw new Error(
`The keys from LabelKeys should not be present in constantLabels or LabelKeys should not contains duplicate keys`);
}
}
15 changes: 12 additions & 3 deletions packages/opencensus-core/src/metrics/gauges/derived-gauge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ export class DerivedGauge implements types.Meter {
private labelKeysLength: number;
private registeredPoints: Map<string, GaugeEntry> = new Map();
private extractor?: ValueExtractor;
private readonly constantLabelValues: LabelValue[];

private static readonly LABEL_VALUE = 'labelValue';
private static readonly LABEL_VALUES = 'labelValues';
Expand All @@ -94,12 +95,19 @@ export class DerivedGauge implements types.Meter {
* @param {string} unit The unit of the metric.
* @param {MetricDescriptorType} type The type of metric.
* @param {LabelKey[]} labelKeys The list of the label keys.
* @param {Map<LabelKey, LabelValue>} constantLabels The map of constant
* labels for the Metric.
*/
constructor(
name: string, description: string, unit: string,
type: MetricDescriptorType, labelKeys: LabelKey[]) {
this.metricDescriptor = {name, description, unit, type, labelKeys};
type: MetricDescriptorType, labelKeys: LabelKey[],
readonly constantLabels: Map<LabelKey, LabelValue>) {
this.labelKeysLength = labelKeys.length;
const keysAndConstantKeys = [...labelKeys, ...constantLabels.keys()];
this.constantLabelValues = [...constantLabels.values()];

this.metricDescriptor =
{name, description, unit, type, labelKeys: keysAndConstantKeys};
}

// Checks if the specified collection is a LengthAttributeInterface.
Expand Down Expand Up @@ -211,7 +219,8 @@ export class DerivedGauge implements types.Meter {
timeseries: Array.from(
this.registeredPoints,
([_, gaugeEntry]) => ({
labelValues: gaugeEntry.labelValues,
labelValues:
[...gaugeEntry.labelValues, ...this.constantLabelValues],
points: [{value: gaugeEntry.extractor(), timestamp}]
} as TimeSeries))
};
Expand Down
14 changes: 11 additions & 3 deletions packages/opencensus-core/src/metrics/gauges/gauge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export class Gauge implements types.Meter {
private labelKeysLength: number;
private defaultLabelValues: LabelValue[];
private registeredPoints: Map<string, types.Point> = new Map();
private readonly constantLabelValues: LabelValue[];

private static readonly LABEL_VALUE = 'labelValue';
private static readonly LABEL_VALUES = 'labelValues';
Expand All @@ -42,12 +43,19 @@ export class Gauge implements types.Meter {
* @param {string} unit The unit of the metric.
* @param {MetricDescriptorType} type The type of metric.
* @param {LabelKey[]} labelKeys The list of the label keys.
* @param {Map<LabelKey, LabelValue>} constantLabels The map of constant
* labels for the Metric.
*/
constructor(
name: string, description: string, unit: string,
type: MetricDescriptorType, readonly labelKeys: LabelKey[]) {
this.metricDescriptor = {name, description, unit, type, labelKeys};
type: MetricDescriptorType, readonly labelKeys: LabelKey[],
readonly constantLabels: Map<LabelKey, LabelValue>) {
this.labelKeysLength = labelKeys.length;
const keysAndConstantKeys = [...labelKeys, ...constantLabels.keys()];
this.constantLabelValues = [...constantLabels.values()];

this.metricDescriptor =
{name, description, unit, type, labelKeys: keysAndConstantKeys};
this.defaultLabelValues = initializeDefaultLabels(this.labelKeysLength);
}

Expand Down Expand Up @@ -116,7 +124,7 @@ export class Gauge implements types.Meter {
throw new Error(Gauge.ERROR_MESSAGE_INVALID_SIZE);
}

const point = new PointEntry(labelValues);
const point = new PointEntry([...labelValues, ...this.constantLabelValues]);
this.registeredPoints.set(hash, point);
return point;
}
Expand Down
42 changes: 32 additions & 10 deletions packages/opencensus-core/src/metrics/metric-registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
* limitations under the License.
*/

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

private static readonly NAME = 'name';
private static readonly LABEL_KEY = 'labelKey';
private static readonly CONSTANT_LABELS = 'constantLabels';
private static readonly DEFAULT_DESCRIPTION = '';
private static readonly DEFAULT_UNIT = MeasureUnit.UNIT;
private static readonly DEFAULT_LABEL_KEYS = [];
private static readonly DEFAULT_CONSTANT_LABEL = new Map();

constructor() {
this.metricProducer = new MetricProducerForRegistry(this.registeredMetrics);
Expand All @@ -54,13 +56,18 @@ export class MetricRegistry {
const unit = (options && options.unit) || MetricRegistry.DEFAULT_UNIT;
const labelKeys =
(options && options.labelKeys) || MetricRegistry.DEFAULT_LABEL_KEYS;
// TODO (mayurkale): Add support for constantLabels
const constantLabels = (options && options.constantLabels) ||
MetricRegistry.DEFAULT_CONSTANT_LABEL;
// TODO(mayurkale): Add support for resource

validateArrayElementsNotNull(labelKeys, MetricRegistry.LABEL_KEY);
validateMapElementNotNull(constantLabels, MetricRegistry.CONSTANT_LABELS);
validateDuplicateKeys(labelKeys, constantLabels);

const labelKeysCopy = Object.assign([], labelKeys);
const int64Gauge = new Gauge(
validateNotNull(name, MetricRegistry.NAME), description, unit,
MetricDescriptorType.GAUGE_INT64, labelKeysCopy);
MetricDescriptorType.GAUGE_INT64, labelKeysCopy, constantLabels);
this.registerMetric(name, int64Gauge);
return int64Gauge;
}
Expand All @@ -80,13 +87,18 @@ export class MetricRegistry {
const unit = (options && options.unit) || MetricRegistry.DEFAULT_UNIT;
const labelKeys =
(options && options.labelKeys) || MetricRegistry.DEFAULT_LABEL_KEYS;
// TODO (mayurkale): Add support for constantLabels
const constantLabels = (options && options.constantLabels) ||
MetricRegistry.DEFAULT_CONSTANT_LABEL;
// TODO(mayurkale): Add support for resource

validateArrayElementsNotNull(labelKeys, MetricRegistry.LABEL_KEY);
validateMapElementNotNull(constantLabels, MetricRegistry.CONSTANT_LABELS);
validateDuplicateKeys(labelKeys, constantLabels);

const labelKeysCopy = Object.assign([], labelKeys);
const doubleGauge = new Gauge(
validateNotNull(name, MetricRegistry.NAME), description, unit,
MetricDescriptorType.GAUGE_DOUBLE, labelKeysCopy);
MetricDescriptorType.GAUGE_DOUBLE, labelKeysCopy, constantLabels);
this.registerMetric(name, doubleGauge);
return doubleGauge;
}
Expand All @@ -106,13 +118,18 @@ export class MetricRegistry {
const unit = (options && options.unit) || MetricRegistry.DEFAULT_UNIT;
const labelKeys =
(options && options.labelKeys) || MetricRegistry.DEFAULT_LABEL_KEYS;
// TODO (mayurkale): Add support for constantLabels
const constantLabels = (options && options.constantLabels) ||
MetricRegistry.DEFAULT_CONSTANT_LABEL;
// TODO(mayurkale): Add support for resource

validateArrayElementsNotNull(labelKeys, MetricRegistry.LABEL_KEY);
validateMapElementNotNull(constantLabels, MetricRegistry.CONSTANT_LABELS);
validateDuplicateKeys(labelKeys, constantLabels);

const labelKeysCopy = Object.assign([], labelKeys);
const derivedInt64Gauge = new DerivedGauge(
validateNotNull(name, MetricRegistry.NAME), description, unit,
MetricDescriptorType.GAUGE_INT64, labelKeysCopy);
MetricDescriptorType.GAUGE_INT64, labelKeysCopy, constantLabels);
this.registerMetric(name, derivedInt64Gauge);
return derivedInt64Gauge;
}
Expand All @@ -132,13 +149,18 @@ export class MetricRegistry {
const unit = (options && options.unit) || MetricRegistry.DEFAULT_UNIT;
const labelKeys =
(options && options.labelKeys) || MetricRegistry.DEFAULT_LABEL_KEYS;
// TODO (mayurkale): Add support for constantLabels
const constantLabels = (options && options.constantLabels) ||
MetricRegistry.DEFAULT_CONSTANT_LABEL;
// TODO(mayurkale): Add support for resource

validateArrayElementsNotNull(labelKeys, MetricRegistry.LABEL_KEY);
validateMapElementNotNull(constantLabels, MetricRegistry.CONSTANT_LABELS);
validateDuplicateKeys(labelKeys, constantLabels);

const labelKeysCopy = Object.assign([], labelKeys);
const derivedDoubleGauge = new DerivedGauge(
validateNotNull(name, MetricRegistry.NAME), description, unit,
MetricDescriptorType.GAUGE_DOUBLE, labelKeysCopy);
MetricDescriptorType.GAUGE_DOUBLE, labelKeysCopy, constantLabels);
this.registerMetric(name, derivedDoubleGauge);
return derivedDoubleGauge;
}
Expand Down
43 changes: 40 additions & 3 deletions packages/opencensus-core/test/test-derived-gauge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
*/

import * as assert from 'assert';

import {TEST_ONLY} from '../src/common/time-util';
import {LabelKey, LabelValue, MetricDescriptorType, Timestamp} from '../src/metrics/export/types';
import {DerivedGauge} from '../src/metrics/gauges/derived-gauge';
Expand All @@ -29,6 +28,9 @@ const LABEL_KEYS: LabelKey[] = [{key: 'code', description: 'desc'}];
const LABEL_VALUES_200: LabelValue[] = [{value: '200'}];
const LABEL_VALUES_400: LabelValue[] = [{value: '400'}];
const LABEL_VALUES_EXRTA: LabelValue[] = [{value: '200'}, {value: '400'}];
const EMPTY_CONSTANT_LABELS = new Map();
const CONSTANT_LABELS = new Map();
CONSTANT_LABELS.set({key: 'host', description: 'host'}, {value: 'localhost'});

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

beforeEach(() => {
instance = new DerivedGauge(
METRIC_NAME, METRIC_DESCRIPTION, UNIT, GAUGE_INT64, LABEL_KEYS);
METRIC_NAME, METRIC_DESCRIPTION, UNIT, GAUGE_INT64, LABEL_KEYS,
EMPTY_CONSTANT_LABELS);

process.hrtime = () => [100, 1e7];
Date.now = () => 1450000000000;
Expand Down Expand Up @@ -148,7 +151,8 @@ describe('DerivedGauge', () => {
}
const obj = new QueueManager();
const doubleInstance = new DerivedGauge(
METRIC_NAME, METRIC_DESCRIPTION, UNIT, GAUGE_DOUBLE, LABEL_KEYS);
METRIC_NAME, METRIC_DESCRIPTION, UNIT, GAUGE_DOUBLE, LABEL_KEYS,
EMPTY_CONSTANT_LABELS);
doubleInstance.createTimeSeries(LABEL_VALUES_200, obj);
const metric = doubleInstance.getMetric();
assert.notEqual(metric, null);
Expand All @@ -171,6 +175,39 @@ describe('DerivedGauge', () => {
}]);
});

it('should return a Metric (Double) - custom object', () => {
class QueueManager {
getValue(): number {
return 0.7;
}
}
const obj = new QueueManager();
const doubleInstance = new DerivedGauge(
METRIC_NAME, METRIC_DESCRIPTION, UNIT, GAUGE_DOUBLE, LABEL_KEYS,
CONSTANT_LABELS);
doubleInstance.createTimeSeries(LABEL_VALUES_200, obj);
const metric = doubleInstance.getMetric();
assert.notEqual(metric, null);
assert.deepStrictEqual(metric!.descriptor, {
name: METRIC_NAME,
description: METRIC_DESCRIPTION,
unit: UNIT,
type: GAUGE_DOUBLE,
labelKeys: [...LABEL_KEYS, ...Array.from(CONSTANT_LABELS.keys())]
});
assert.equal(metric!.timeseries.length, 1);
assert.deepStrictEqual(
metric!.timeseries, [{
labelValues:
[...LABEL_VALUES_200, ...Array.from(CONSTANT_LABELS.values())],
points: [{
value: 0.7,
timestamp:
{nanos: mockedTime.nanos, seconds: mockedTime.seconds}
}]
}]);
});

it('should not create same timeseries again', () => {
const map = new Map();
instance.createTimeSeries(LABEL_VALUES_200, map);
Expand Down
Loading

0 comments on commit 6f5c6b3

Please sign in to comment.