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

Gauge: Add support for constant labels #468

Merged
merged 4 commits into from
Apr 4, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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