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

Commit 26ad2cf

Browse files
authored
Add Double and INT64 Gauges APIs (#207)
* Add Double and INT64 Gauges APIs * fix reviews * Add ctor doc and static strings * restore process.hrtime after each test * fix comments and rebase
1 parent a03a33b commit 26ad2cf

File tree

9 files changed

+1592
-270
lines changed

9 files changed

+1592
-270
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ All notable changes to this project will be documented in this file.
88
- Add Resource API.
99
- Add Metrics API.
1010
- Remove support for `min`/`max` in the stats Distribution to make it compatible with Metrics.
11+
- Add Gauges (`DoubleGauge`, `LongGauge`) APIs.
1112

1213
## 0.0.7 - 2018-11-12
1314
**Contains API breaking changes for stats/metrics implementations**

packages/opencensus-core/package-lock.json

Lines changed: 839 additions & 267 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,9 @@ export function validateNotNull<T>(reference: T, errorMessage: string): T {
3737
*/
3838
export function validateArrayElementsNotNull<T>(
3939
array: T[], errorMessage: string) {
40-
if (array.every(
41-
element => element !== null || typeof element !== 'undefined')) {
40+
const areAllDefined = array.every(
41+
element => element !== null && typeof element !== 'undefined');
42+
if (!areAllDefined) {
4243
throw new Error(`${errorMessage} elements should not be a NULL`);
4344
}
4445
}

packages/opencensus-core/src/metrics/export/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ export interface TimeSeries {
121121
* interval (start_timestamp, timestamp]. If not specified, the backend can
122122
* use the previous recorded value.
123123
*/
124-
readonly startTimestamp: Timestamp;
124+
readonly startTimestamp?: Timestamp;
125125
/**
126126
* The set of label values that uniquely identify this timeseries. Applies to
127127
* all points. The order of label values must match that of label keys in the
Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
/**
2+
* Copyright 2018, OpenCensus Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import {validateArrayElementsNotNull, validateNotNull} from '../../common/validations';
18+
import {LabelKey, LabelValue, Metric, MetricDescriptor, MetricDescriptorType, TimeSeries, Timestamp} from '../export/types';
19+
import * as types from '../gauges/types';
20+
import {hashLabelValues, initializeDefaultLabels} from '../utils';
21+
22+
/**
23+
* Gauge metric
24+
*/
25+
export class Gauge implements types.Meter {
26+
private readonly metricDescriptor: MetricDescriptor;
27+
private labelKeysLength: number;
28+
private defaultLabelValues: LabelValue[];
29+
private registeredPoints: Map<string, types.Point> = new Map();
30+
31+
private static readonly LABEL_VALUE = 'labelValue';
32+
private static readonly LABEL_VALUES = 'labelValues';
33+
private static readonly ERROR_MESSAGE_INVALID_SIZE =
34+
'Label Keys and Label Values don\'t have same size';
35+
36+
/**
37+
* Constructs a new Gauge instance.
38+
*
39+
* @param {string} name The name of the metric.
40+
* @param {string} description The description of the metric.
41+
* @param {string} unit The unit of the metric.
42+
* @param {MetricDescriptorType} type The type of metric.
43+
* @param {LabelKey[]} labelKeys The list of the label keys.
44+
*/
45+
constructor(
46+
name: string, description: string, unit: string,
47+
type: MetricDescriptorType, readonly labelKeys: LabelKey[]) {
48+
this.metricDescriptor = {name, description, unit, type, labelKeys};
49+
this.labelKeysLength = labelKeys.length;
50+
this.defaultLabelValues = initializeDefaultLabels(this.labelKeysLength);
51+
}
52+
53+
/**
54+
* Creates a TimeSeries and returns a Point if the specified
55+
* labelValues is not already associated with this gauge, else returns an
56+
* existing Point.
57+
*
58+
* It is recommended to keep a reference to the Point instead of always
59+
* calling this method for manual operations.
60+
*
61+
* @param {LabelValue[]} labelValues The list of the label values.
62+
* @returns {types.Point} The value of single gauge.
63+
*/
64+
getOrCreateTimeSeries(labelValues: LabelValue[]): types.Point {
65+
validateArrayElementsNotNull(
66+
validateNotNull(labelValues, Gauge.LABEL_VALUES), Gauge.LABEL_VALUE);
67+
return this.registerTimeSeries(labelValues);
68+
}
69+
70+
/**
71+
* Returns a Point for a gauge with all labels not set, or default
72+
* labels.
73+
*
74+
* @returns {types.Point} The value of single gauge.
75+
*/
76+
getDefaultTimeSeries(): types.Point {
77+
return this.registerTimeSeries(this.defaultLabelValues);
78+
}
79+
80+
/**
81+
* Removes the TimeSeries from the gauge metric, if it is present. i.e.
82+
* references to previous Point objects are invalid (not part of the
83+
* metric).
84+
*
85+
* @param {LabelValue[]} labelValues The list of label values.
86+
*/
87+
removeTimeSeries(labelValues: LabelValue[]): void {
88+
validateNotNull(labelValues, Gauge.LABEL_VALUES);
89+
this.registeredPoints.delete(hashLabelValues(labelValues));
90+
}
91+
92+
/**
93+
* Removes all TimeSeries from the gauge metric. i.e. references to all
94+
* previous Point objects are invalid (not part of the metric).
95+
*/
96+
clear(): void {
97+
this.registeredPoints.clear();
98+
}
99+
100+
/**
101+
* Registers a TimeSeries and returns a Point if the specified
102+
* labelValues is not already associated with this gauge, else returns an
103+
* existing Point.
104+
*
105+
* @param {LabelValue[]} labelValues The list of the label values.
106+
* @returns {types.Point} The value of single gauge.
107+
*/
108+
private registerTimeSeries(labelValues: LabelValue[]): types.Point {
109+
const hash = hashLabelValues(labelValues);
110+
// return if the specified labelValues is already associated with the point.
111+
if (this.registeredPoints.has(hash)) {
112+
return this.registeredPoints.get(hash);
113+
}
114+
if (this.labelKeysLength !== labelValues.length) {
115+
throw new Error(Gauge.ERROR_MESSAGE_INVALID_SIZE);
116+
}
117+
118+
const point = new PointEntry(labelValues);
119+
this.registeredPoints.set(hash, point);
120+
return point;
121+
}
122+
123+
/**
124+
* Provides a Metric with one or more TimeSeries.
125+
*
126+
* @returns {Metric} The Metric.
127+
*/
128+
getMetric(): Metric {
129+
if (this.registeredPoints.size === 0) {
130+
return null;
131+
}
132+
const [seconds, nanos] = process.hrtime();
133+
return {
134+
descriptor: this.metricDescriptor,
135+
timeseries: Array.from(
136+
this.registeredPoints,
137+
([_, point]) => point.getTimeSeries({seconds, nanos}))
138+
};
139+
}
140+
}
141+
142+
/**
143+
* The value of a single point in the Gauge.TimeSeries.
144+
*/
145+
export class PointEntry implements types.Point {
146+
private readonly labelValues: LabelValue[];
147+
private value = 0;
148+
149+
constructor(labelValues: LabelValue[]) {
150+
this.labelValues = labelValues;
151+
}
152+
153+
/**
154+
* Adds the given value to the current value. The values can be negative.
155+
*
156+
* @param {number} amt The value to add.
157+
*/
158+
add(amt: number): void {
159+
this.value = this.value + amt;
160+
}
161+
162+
/**
163+
* Sets the given value.
164+
*
165+
* @param {number} val The new value.
166+
*/
167+
set(val: number): void {
168+
this.value = val;
169+
}
170+
171+
/**
172+
* Returns the TimeSeries with one or more Point.
173+
*
174+
* @param {Timestamp} timestamp The time at which the gauge is recorded.
175+
* @returns {TimeSeries} The TimeSeries.
176+
*/
177+
getTimeSeries(timestamp: Timestamp): TimeSeries {
178+
return {
179+
labelValues: this.labelValues,
180+
points: [{value: this.value, timestamp}]
181+
};
182+
}
183+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/**
2+
* Copyright 2018, OpenCensus Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import {Metric, TimeSeries, Timestamp} from '../export/types';
18+
19+
export interface Meter {
20+
/**
21+
* Provides a Metric with one or more TimeSeries.
22+
*
23+
* @returns {Metric} The Metric.
24+
*/
25+
getMetric(): Metric;
26+
}
27+
28+
export interface Point {
29+
/**
30+
* Adds the given value to the current value. The values can be negative.
31+
*
32+
* @param {number} amt The value to add.
33+
*/
34+
add(amt: number): void;
35+
36+
/**
37+
* Sets the given value.
38+
*
39+
* @param {number} val The new value.
40+
*/
41+
set(val: number): void;
42+
43+
/**
44+
* Returns the TimeSeries with one or more Point.
45+
*
46+
* @param {Timestamp} timestamp The time at which the gauge is recorded.
47+
* @returns {TimeSeries} The TimeSeries.
48+
*/
49+
getTimeSeries(timestamp: Timestamp): TimeSeries;
50+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/**
2+
* Copyright 2018, OpenCensus Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import {LabelValue} from './export/types';
18+
19+
const COMMA_SEPARATOR = ',';
20+
const UNSET_LABEL_VALUE: LabelValue = {
21+
value: null
22+
};
23+
24+
/**
25+
* Returns a string(comma separated) from the list of label values.
26+
*
27+
* @param {LabelValue[]} labelValues The list of the label values.
28+
* @returns {string} The hashed label values string.
29+
*/
30+
export function hashLabelValues(labelValues: LabelValue[]): string {
31+
return labelValues.map(lv => lv.value).sort().join(COMMA_SEPARATOR);
32+
}
33+
34+
/**
35+
* Returns default label values.
36+
*
37+
* @param {number} count The number of label values.
38+
* @returns {LabelValue[]} The list of the label values.
39+
*/
40+
export function initializeDefaultLabels(count: number): LabelValue[] {
41+
return new Array(count).fill(UNSET_LABEL_VALUE);
42+
}

0 commit comments

Comments
 (0)