1717import * as defaultLogger from '../common/console-logger' ;
1818import { getTimestampWithProcessHRTime , timestampFromMillis } from '../common/time-util' ;
1919import * as loggerTypes from '../common/types' ;
20- import { DistributionValue , LabelValue , Metric , MetricDescriptor , MetricDescriptorType , Point , TimeSeries , Timestamp } from '../metrics/export/types' ;
20+ import { Bucket as metricBucket , DistributionValue , LabelValue , Metric , MetricDescriptor , MetricDescriptorType , Point , TimeSeries , Timestamp } from '../metrics/export/types' ;
2121import { TagMap } from '../tags/tag-map' ;
2222import { TagKey , TagValue } from '../tags/types' ;
2323import { isValidTagKey } from '../tags/validation' ;
24+
2425import { BucketBoundaries } from './bucket-boundaries' ;
2526import { MetricUtils } from './metric-utils' ;
2627import { Recorder } from './recorder' ;
27- import { AggregationData , AggregationType , Measure , Measurement , View } from './types' ;
28+ import { AggregationData , AggregationType , Measure , Measurement , StatsExemplar , View } from './types' ;
2829
2930const RECORD_SEPARATOR = String . fromCharCode ( 30 ) ;
3031
@@ -114,8 +115,13 @@ export class BaseView implements View {
114115 * Measurements with measurement type INT64 will have its value truncated.
115116 * @param measurement The measurement to record
116117 * @param tags The tags to which the value is applied
118+ * @param attachments optional The contextual information associated with an
119+ * example value. The contextual information is represented as key - value
120+ * string pairs.
117121 */
118- recordMeasurement ( measurement : Measurement , tags : TagMap ) {
122+ recordMeasurement (
123+ measurement : Measurement , tags : TagMap ,
124+ attachments ?: { [ key : string ] : string } ) {
119125 const tagValues = Recorder . getTagValues ( tags . tags , this . columns ) ;
120126 const encodedTags = this . encodeTagValues ( tagValues ) ;
121127 if ( ! this . tagValueAggregationMap [ encodedTags ] ) {
@@ -124,7 +130,7 @@ export class BaseView implements View {
124130 }
125131
126132 Recorder . addMeasurement (
127- this . tagValueAggregationMap [ encodedTags ] , measurement ) ;
133+ this . tagValueAggregationMap [ encodedTags ] , measurement , attachments ) ;
128134 }
129135
130136 /**
@@ -143,12 +149,14 @@ export class BaseView implements View {
143149 */
144150 private createAggregationData ( tagValues : TagValue [ ] ) : AggregationData {
145151 const aggregationMetadata = { tagValues, timestamp : Date . now ( ) } ;
146- const { buckets, bucketCounts} = this . bucketBoundaries ;
147- const bucketsCopy = Object . assign ( [ ] , buckets ) ;
148- const bucketCountsCopy = Object . assign ( [ ] , bucketCounts ) ;
149152
150153 switch ( this . aggregation ) {
151154 case AggregationType . DISTRIBUTION :
155+ const { buckets, bucketCounts} = this . bucketBoundaries ;
156+ const bucketsCopy = Object . assign ( [ ] , buckets ) ;
157+ const bucketCountsCopy = Object . assign ( [ ] , bucketCounts ) ;
158+ const exemplars = new Array ( bucketCounts . length ) ;
159+
152160 return {
153161 ...aggregationMetadata ,
154162 type : AggregationType . DISTRIBUTION ,
@@ -159,7 +167,8 @@ export class BaseView implements View {
159167 stdDeviation : null as number ,
160168 sumOfSquaredDeviation : null as number ,
161169 buckets : bucketsCopy ,
162- bucketCounts : bucketCountsCopy
170+ bucketCounts : bucketCountsCopy ,
171+ exemplars
163172 } ;
164173 case AggregationType . SUM :
165174 return { ...aggregationMetadata , type : AggregationType . SUM , value : 0 } ;
@@ -221,18 +230,21 @@ export class BaseView implements View {
221230 */
222231 private toPoint ( timestamp : Timestamp , data : AggregationData ) : Point {
223232 let value ;
224-
225233 if ( data . type === AggregationType . DISTRIBUTION ) {
226- // TODO: Add examplar transition
227- const { count, sum, sumOfSquaredDeviation} = data ;
234+ const { count, sum, sumOfSquaredDeviation, exemplars} = data ;
235+ const buckets = [ ] ;
236+ for ( let bucket = 0 ; bucket < data . bucketCounts . length ; bucket ++ ) {
237+ const bucketCount = data . bucketCounts [ bucket ] ;
238+ const statsExemplar = exemplars ? exemplars [ bucket ] : undefined ;
239+ buckets . push ( this . getMetricBucket ( statsExemplar , bucketCount ) ) ;
240+ }
241+
228242 value = {
229243 count,
230244 sum,
231245 sumOfSquaredDeviation,
246+ buckets,
232247 bucketOptions : { explicit : { bounds : data . buckets } } ,
233- // Bucket without an Exemplar.
234- buckets :
235- data . bucketCounts . map ( bucketCount => ( { count : bucketCount } ) )
236248 } as DistributionValue ;
237249 } else {
238250 value = data . value as number ;
@@ -249,6 +261,24 @@ export class BaseView implements View {
249261 return this . tagValueAggregationMap [ this . encodeTagValues ( tagValues ) ] ;
250262 }
251263
264+ /** Returns a Bucket with count and examplar (if present) */
265+ private getMetricBucket ( statsExemplar : StatsExemplar , bucketCount : number ) :
266+ metricBucket {
267+ if ( statsExemplar ) {
268+ // Bucket with an Exemplar.
269+ return {
270+ count : bucketCount ,
271+ exemplar : {
272+ value : statsExemplar . value ,
273+ timestamp : timestampFromMillis ( statsExemplar . timestamp ) ,
274+ attachments : statsExemplar . attachments
275+ }
276+ } ;
277+ }
278+ // Bucket with no Exemplar.
279+ return { count : bucketCount } ;
280+ }
281+
252282 /** Determines whether the given TagKeys are valid. */
253283 private validateTagKeys ( tagKeys : TagKey [ ] ) : TagKey [ ] {
254284 const tagKeysCopy = Object . assign ( [ ] , tagKeys ) ;
0 commit comments