UNPKG

9.63 kBJavaScriptView Raw
1"use strict";
2/**
3 * Copyright 2018, OpenCensus Authors
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17Object.defineProperty(exports, "__esModule", { value: true });
18const defaultLogger = require("../common/console-logger");
19const time_util_1 = require("../common/time-util");
20const types_1 = require("../metrics/export/types");
21const validation_1 = require("../tags/validation");
22const bucket_boundaries_1 = require("./bucket-boundaries");
23const metric_utils_1 = require("./metric-utils");
24const recorder_1 = require("./recorder");
25const types_2 = require("./types");
26const RECORD_SEPARATOR = String.fromCharCode(30);
27/**
28 * A View specifies an aggregation and a set of tag keys. The aggregation will
29 * be broken down by the unique set of matching tag values for each measure.
30 */
31class BaseView {
32 /**
33 * Creates a new View instance. This constructor is used by Stats. User should
34 * prefer using Stats.createView() instead.
35 * @param name The view name
36 * @param measure The view measure
37 * @param aggregation The view aggregation type
38 * @param tagsKeys The Tags' keys that view will have
39 * @param description The view description
40 * @param bucketBoundaries The view bucket boundaries for a distribution
41 * aggregation type
42 * @param logger
43 */
44 constructor(name, measure, aggregation, tagsKeys, description, bucketBoundaries, logger = defaultLogger) {
45 /**
46 * A map of stringified tags representing columns labels or tag keys, concept
47 * similar to dimensions on multidimensional modeling, to AggregationData.
48 * If no Tags are provided, then, all data is recorded in a single
49 * aggregation.
50 */
51 this.tagValueAggregationMap = {};
52 /** true if the view was registered */
53 this.registered = false;
54 if (aggregation === types_2.AggregationType.DISTRIBUTION && !bucketBoundaries) {
55 throw new Error('No bucketBoundaries specified');
56 }
57 this.logger = logger.logger();
58 this.name = name;
59 this.description = description;
60 this.measure = measure;
61 this.columns = this.validateTagKeys(tagsKeys);
62 this.aggregation = aggregation;
63 this.startTime = Date.now();
64 if (bucketBoundaries) {
65 this.bucketBoundaries = new bucket_boundaries_1.BucketBoundaries(bucketBoundaries);
66 }
67 this.metricDescriptor = metric_utils_1.MetricUtils.viewToMetricDescriptor(this);
68 }
69 /** Gets the view's tag keys */
70 getColumns() {
71 return this.columns;
72 }
73 /**
74 * Records a measurement in the proper view's row. This method is used by
75 * Stats. User should prefer using Stats.record() instead.
76 *
77 * Measurements with measurement type INT64 will have its value truncated.
78 * @param measurement The measurement to record
79 * @param tags The tags to which the value is applied
80 * @param attachments optional The contextual information associated with an
81 * example value. The contextual information is represented as key - value
82 * string pairs.
83 */
84 recordMeasurement(measurement, tags, attachments) {
85 const tagValues = recorder_1.Recorder.getTagValues(tags.tags, this.columns);
86 const encodedTags = this.encodeTagValues(tagValues);
87 if (!this.tagValueAggregationMap[encodedTags]) {
88 this.tagValueAggregationMap[encodedTags] = this.createAggregationData(tagValues);
89 }
90 recorder_1.Recorder.addMeasurement(this.tagValueAggregationMap[encodedTags], measurement, attachments);
91 }
92 /**
93 * Encodes a TagValue object into a value sorted string.
94 * @param tagValues The tagValues to encode
95 */
96 encodeTagValues(tagValues) {
97 return tagValues
98 .map(tagValue => (tagValue ? tagValue.value : null))
99 .sort()
100 .join(RECORD_SEPARATOR);
101 }
102 /**
103 * Creates an empty aggregation data for a given tags.
104 * @param tagValues The tags for that aggregation data
105 */
106 createAggregationData(tagValues) {
107 const aggregationMetadata = { tagValues, timestamp: Date.now() };
108 switch (this.aggregation) {
109 case types_2.AggregationType.DISTRIBUTION:
110 const { buckets, bucketCounts } = this.bucketBoundaries;
111 const bucketsCopy = Object.assign([], buckets);
112 const bucketCountsCopy = Object.assign([], bucketCounts);
113 const exemplars = new Array(bucketCounts.length);
114 return Object.assign({}, aggregationMetadata, { type: types_2.AggregationType.DISTRIBUTION, startTime: this.startTime, count: 0, sum: 0, mean: 0, stdDeviation: 0, sumOfSquaredDeviation: 0, buckets: bucketsCopy, bucketCounts: bucketCountsCopy, exemplars });
115 case types_2.AggregationType.SUM:
116 return Object.assign({}, aggregationMetadata, { type: types_2.AggregationType.SUM, value: 0 });
117 case types_2.AggregationType.COUNT:
118 return Object.assign({}, aggregationMetadata, { type: types_2.AggregationType.COUNT, value: 0 });
119 default:
120 return Object.assign({}, aggregationMetadata, { type: types_2.AggregationType.LAST_VALUE, value: 0 });
121 }
122 }
123 /**
124 * Gets view`s metric
125 * @param start The start timestamp in epoch milliseconds
126 * @returns The Metric.
127 */
128 getMetric(start) {
129 const { type } = this.metricDescriptor;
130 let startTimestamp;
131 // The moment when this point was recorded.
132 const now = time_util_1.getTimestampWithProcessHRTime();
133 if (type !== types_1.MetricDescriptorType.GAUGE_INT64 &&
134 type !== types_1.MetricDescriptorType.GAUGE_DOUBLE) {
135 startTimestamp = time_util_1.timestampFromMillis(start);
136 }
137 const timeseries = [];
138 Object.keys(this.tagValueAggregationMap).forEach(key => {
139 const { tagValues } = this.tagValueAggregationMap[key];
140 const labelValues = metric_utils_1.MetricUtils.tagValuesToLabelValues(tagValues);
141 const point = this.toPoint(now, this.getSnapshot(tagValues));
142 if (startTimestamp) {
143 timeseries.push({ startTimestamp, labelValues, points: [point] });
144 }
145 else {
146 timeseries.push({ labelValues, points: [point] });
147 }
148 });
149 return { descriptor: this.metricDescriptor, timeseries };
150 }
151 /**
152 * Converts snapshot to point
153 * @param timestamp The timestamp
154 * @param data The aggregated data
155 * @returns The Point.
156 */
157 toPoint(timestamp, data) {
158 if (data.type === types_2.AggregationType.DISTRIBUTION) {
159 const { count, sum, sumOfSquaredDeviation, exemplars } = data;
160 const buckets = [];
161 if (data.bucketCounts) {
162 for (let bucket = 0; bucket < data.bucketCounts.length; bucket++) {
163 const bucketCount = data.bucketCounts[bucket];
164 const statsExemplar = exemplars ? exemplars[bucket] : undefined;
165 buckets.push(this.getMetricBucket(bucketCount, statsExemplar));
166 }
167 }
168 const value = {
169 count,
170 sum,
171 sumOfSquaredDeviation,
172 buckets,
173 bucketOptions: { explicit: { bounds: data.buckets } },
174 };
175 return { timestamp, value };
176 }
177 else {
178 const value = data.value;
179 return { timestamp, value };
180 }
181 }
182 /**
183 * Returns a snapshot of an AggregationData for that tags/labels values.
184 * @param tags The desired data's tags
185 * @returns The AggregationData.
186 */
187 getSnapshot(tagValues) {
188 return this.tagValueAggregationMap[this.encodeTagValues(tagValues)];
189 }
190 /** Returns a Bucket with count and examplar (if present) */
191 getMetricBucket(bucketCount, statsExemplar) {
192 if (statsExemplar) {
193 // Bucket with an Exemplar.
194 return {
195 count: bucketCount,
196 exemplar: {
197 value: statsExemplar.value,
198 timestamp: time_util_1.timestampFromMillis(statsExemplar.timestamp),
199 attachments: statsExemplar.attachments,
200 },
201 };
202 }
203 // Bucket with no Exemplar.
204 return { count: bucketCount };
205 }
206 /** Determines whether the given TagKeys are valid. */
207 validateTagKeys(tagKeys) {
208 const tagKeysCopy = Object.assign([], tagKeys);
209 tagKeysCopy.forEach(tagKey => {
210 if (!validation_1.isValidTagKey(tagKey)) {
211 throw new Error(`Invalid TagKey name: ${tagKey}`);
212 }
213 });
214 const tagKeysSet = new Set(tagKeysCopy.map((tagKey) => tagKey.name));
215 if (tagKeysSet.size !== tagKeysCopy.length) {
216 throw new Error('Columns have duplicate');
217 }
218 return tagKeysCopy;
219 }
220}
221exports.BaseView = BaseView;
222//# sourceMappingURL=view.js.map
\No newline at end of file