// Copyright 2019 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package ocagent import ( "fmt" "time" "golang.org/x/tools/internal/telemetry" "golang.org/x/tools/internal/telemetry/export/ocagent/wire" "golang.org/x/tools/internal/telemetry/metric" ) // dataToMetricDescriptor return a *wire.MetricDescriptor based on data. func dataToMetricDescriptor(data telemetry.MetricData) *wire.MetricDescriptor { if data == nil { return nil } descriptor := &wire.MetricDescriptor{ Name: data.Handle(), Description: getDescription(data), // TODO: Unit? Type: dataToMetricDescriptorType(data), LabelKeys: getLabelKeys(data), } return descriptor } // getDescription returns the description of data. func getDescription(data telemetry.MetricData) string { switch d := data.(type) { case *metric.Int64Data: return d.Info.Description case *metric.Float64Data: return d.Info.Description case *metric.HistogramInt64Data: return d.Info.Description case *metric.HistogramFloat64Data: return d.Info.Description } return "" } // getLabelKeys returns a slice of *wire.LabelKeys based on the keys // in data. func getLabelKeys(data telemetry.MetricData) []*wire.LabelKey { switch d := data.(type) { case *metric.Int64Data: return infoKeysToLabelKeys(d.Info.Keys) case *metric.Float64Data: return infoKeysToLabelKeys(d.Info.Keys) case *metric.HistogramInt64Data: return infoKeysToLabelKeys(d.Info.Keys) case *metric.HistogramFloat64Data: return infoKeysToLabelKeys(d.Info.Keys) } return nil } // dataToMetricDescriptorType returns a wire.MetricDescriptor_Type based on the // underlying type of data. func dataToMetricDescriptorType(data telemetry.MetricData) wire.MetricDescriptor_Type { switch d := data.(type) { case *metric.Int64Data: if d.IsGauge { return wire.MetricDescriptor_GAUGE_INT64 } return wire.MetricDescriptor_CUMULATIVE_INT64 case *metric.Float64Data: if d.IsGauge { return wire.MetricDescriptor_GAUGE_DOUBLE } return wire.MetricDescriptor_CUMULATIVE_DOUBLE case *metric.HistogramInt64Data: return wire.MetricDescriptor_CUMULATIVE_DISTRIBUTION case *metric.HistogramFloat64Data: return wire.MetricDescriptor_CUMULATIVE_DISTRIBUTION } return wire.MetricDescriptor_UNSPECIFIED } // dataToTimeseries returns a slice of *wire.TimeSeries based on the // points in data. func dataToTimeseries(data telemetry.MetricData, start time.Time) []*wire.TimeSeries { if data == nil { return nil } numRows := numRows(data) startTimestamp := convertTimestamp(start) timeseries := make([]*wire.TimeSeries, 0, numRows) for i := 0; i < numRows; i++ { timeseries = append(timeseries, &wire.TimeSeries{ StartTimestamp: &startTimestamp, // TODO: labels? Points: dataToPoints(data, i), }) } return timeseries } // numRows returns the number of rows in data. func numRows(data telemetry.MetricData) int { switch d := data.(type) { case *metric.Int64Data: return len(d.Rows) case *metric.Float64Data: return len(d.Rows) case *metric.HistogramInt64Data: return len(d.Rows) case *metric.HistogramFloat64Data: return len(d.Rows) } return 0 } // dataToPoints returns an array of *wire.Points based on the point(s) // in data at index i. func dataToPoints(data telemetry.MetricData, i int) []*wire.Point { switch d := data.(type) { case *metric.Int64Data: timestamp := convertTimestamp(*d.EndTime) return []*wire.Point{ { Value: wire.PointInt64Value{ Int64Value: d.Rows[i], }, Timestamp: ×tamp, }, } case *metric.Float64Data: timestamp := convertTimestamp(*d.EndTime) return []*wire.Point{ { Value: wire.PointDoubleValue{ DoubleValue: d.Rows[i], }, Timestamp: ×tamp, }, } case *metric.HistogramInt64Data: row := d.Rows[i] bucketBounds := make([]float64, len(d.Info.Buckets)) for i, val := range d.Info.Buckets { bucketBounds[i] = float64(val) } return distributionToPoints(row.Values, row.Count, float64(row.Sum), bucketBounds, *d.EndTime) case *metric.HistogramFloat64Data: row := d.Rows[i] return distributionToPoints(row.Values, row.Count, row.Sum, d.Info.Buckets, *d.EndTime) } return nil } // distributionToPoints returns an array of *wire.Points containing a // wire.PointDistributionValue representing a distribution with the // supplied counts, count, and sum. func distributionToPoints(counts []int64, count int64, sum float64, bucketBounds []float64, end time.Time) []*wire.Point { buckets := make([]*wire.Bucket, len(counts)) for i := 0; i < len(counts); i++ { buckets[i] = &wire.Bucket{ Count: counts[i], } } timestamp := convertTimestamp(end) return []*wire.Point{ { Value: wire.PointDistributionValue{ DistributionValue: &wire.DistributionValue{ Count: count, Sum: sum, // TODO: SumOfSquaredDeviation? Buckets: buckets, BucketOptions: wire.BucketOptionsExplicit{ Bounds: bucketBounds, }, }, }, Timestamp: ×tamp, }, } } // infoKeysToLabelKeys returns an array of *wire.LabelKeys containing the // string values of the elements of labelKeys. func infoKeysToLabelKeys(infoKeys []interface{}) []*wire.LabelKey { labelKeys := make([]*wire.LabelKey, 0, len(infoKeys)) for _, key := range infoKeys { labelKeys = append(labelKeys, &wire.LabelKey{ Key: fmt.Sprintf("%v", key), }) } return labelKeys }