Copyright 2018, OpenCensus Authors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

package stackdriver
The code in this file is responsible for converting OpenCensus Proto metricsdirectly to Stackdriver Metrics.

import (
	
	
	
	
	

	

	commonpb 
	metricspb 
	resourcepb 
	timestamppb 
	distributionpb 
	labelpb 
	googlemetricpb 
	monitoredrespb 
	monitoringpb 
)

var errNilMetricOrMetricDescriptor = errors.New("non-nil metric or metric descriptor")
var percentileLabelKey = &metricspb.LabelKey{
	Key:         "percentile",
	Description: "the value at a given percentile of a distribution",
}
var globalResource = &resource.Resource{Type: "global"}
var domains = []string{"googleapis.com", "kubernetes.io", "istio.io", "knative.dev"}
PushMetricsProto exports OpenCensus Metrics Proto to Stackdriver Monitoring synchronously, without de-duping or adding proto metrics to the bundler.
func ( *statsExporter) ( context.Context,  *commonpb.Node,  *resourcepb.Resource,  []*metricspb.Metric) (int, error) {
	if len() == 0 {
		return 0, errNilMetricOrMetricDescriptor
	}
Caches the resources seen so far
	 := make(map[*resourcepb.Resource]*monitoredrespb.MonitoredResource)

	 := newMetricsBatcher(, .o.ProjectID, .o.NumberOfWorkers, .c, .o.Timeout)
	for ,  := range  {
No TimeSeries to export, skip this metric.
			continue
		}
		 := .getResource(, , )
		if .GetMetricDescriptor().GetType() == metricspb.MetricDescriptor_SUMMARY {
			 := .convertSummaryMetrics()
			for ,  := range  {
				if  := .createMetricDescriptorFromMetricProto(, );  != nil {
					.recordDroppedTimeseries(len(.GetTimeseries()), )
					continue
				}
				.protoMetricToTimeSeries(, , , )
			}
		} else {
			if  := .createMetricDescriptorFromMetricProto(, );  != nil {
				.recordDroppedTimeseries(len(.GetTimeseries()), )
				continue
			}
			.protoMetricToTimeSeries(, , , )
		}
	}

	return .droppedTimeSeries, .close()
}

func ( *statsExporter) ( *metricspb.Metric) []*metricspb.Metric {
	var  []*metricspb.Metric

	for ,  := range .Timeseries {
		var  []*metricspb.TimeSeries
		var  []*metricspb.TimeSeries
		var  []*metricspb.TimeSeries
		 := .GetLabelValues()

		 := .StartTimestamp
		for ,  := range .GetPoints() {
			 := .GetTimestamp()
			 := .GetSummaryValue()
			if .Sum != nil {
				 := &metricspb.TimeSeries{
					LabelValues:    ,
					StartTimestamp: ,
					Points: []*metricspb.Point{
						{
							Value: &metricspb.Point_DoubleValue{
								DoubleValue: .Sum.Value,
							},
							Timestamp: ,
						},
					},
				}
				 = append(, )
			}

			if .Count != nil {
				 := &metricspb.TimeSeries{
					LabelValues:    ,
					StartTimestamp: ,
					Points: []*metricspb.Point{
						{
							Value: &metricspb.Point_Int64Value{
								Int64Value: .Count.Value,
							},
							Timestamp: ,
						},
					},
				}
				 = append(, )
			}

			 := .GetSnapshot()
			for ,  := range .GetPercentileValues() {
				 := [0:]
				 = append(, &metricspb.LabelValue{
					HasValue: true,
					Value:    fmt.Sprintf("%f", .Percentile),
				})
				 := &metricspb.TimeSeries{
					LabelValues:    ,
					StartTimestamp: nil,
					Points: []*metricspb.Point{
						{
							Value: &metricspb.Point_DoubleValue{
								DoubleValue: .Value,
							},
							Timestamp: ,
						},
					},
				}
				 = append(, )
			}
		}

		if len() > 0 {
			 := &metricspb.Metric{
				MetricDescriptor: &metricspb.MetricDescriptor{
					Name:        fmt.Sprintf("%s_summary_sum", .GetMetricDescriptor().GetName()),
					Description: .GetMetricDescriptor().GetDescription(),
					Type:        metricspb.MetricDescriptor_CUMULATIVE_DOUBLE,
					Unit:        .GetMetricDescriptor().GetUnit(),
					LabelKeys:   .GetMetricDescriptor().GetLabelKeys(),
				},
				Timeseries: ,
				Resource:   .Resource,
			}
			 = append(, )
		}
		if len() > 0 {
			 := &metricspb.Metric{
				MetricDescriptor: &metricspb.MetricDescriptor{
					Name:        fmt.Sprintf("%s_summary_count", .GetMetricDescriptor().GetName()),
					Description: .GetMetricDescriptor().GetDescription(),
					Type:        metricspb.MetricDescriptor_CUMULATIVE_INT64,
					Unit:        "1",
					LabelKeys:   .GetMetricDescriptor().GetLabelKeys(),
				},
				Timeseries: ,
				Resource:   .Resource,
			}
			 = append(, )
		}
		if len() > 0 {
			 := .GetMetricDescriptor().GetLabelKeys()[0:]
			 = append(, percentileLabelKey)
			 := &metricspb.Metric{
				MetricDescriptor: &metricspb.MetricDescriptor{
					Name:        fmt.Sprintf("%s_summary_percentile", .GetMetricDescriptor().GetName()),
					Description: .GetMetricDescriptor().GetDescription(),
					Type:        metricspb.MetricDescriptor_GAUGE_DOUBLE,
					Unit:        .GetMetricDescriptor().GetUnit(),
					LabelKeys:   ,
				},
				Timeseries: ,
				Resource:   .Resource,
			}
			 = append(, )
		}
	}
	return 
}

func ( *statsExporter) ( *resourcepb.Resource,  *metricspb.Metric,  map[*resourcepb.Resource]*monitoredrespb.MonitoredResource) *monitoredrespb.MonitoredResource {
	var  = 
	if .Resource != nil {
		 = .Resource
	}
	,  := []
	if ! {
		 = .o.MapResource(resourcepbToResource())
		[] = 
	}
	return 
}

func ( *resourcepb.Resource) *resource.Resource {
	if  == nil {
		return globalResource
	}
	 := &resource.Resource{
		Type:   .Type,
		Labels: make(map[string]string, len(.Labels)),
	}

	for ,  := range .Labels {
		.Labels[] = 
	}
	return 
}
protoMetricToTimeSeries converts a metric into a Stackdriver Monitoring v3 API CreateTimeSeriesRequest but it doesn't invoke any remote API.
func ( *statsExporter) ( context.Context,  *monitoredrespb.MonitoredResource,  *metricspb.Metric,  *metricsBatcher) {
	if  == nil || .MetricDescriptor == nil {
		.recordDroppedTimeseries(len(.GetTimeseries()), errNilMetricOrMetricDescriptor)
	}

	 := .metricTypeFromProto(.GetMetricDescriptor().GetName())
	 := .GetMetricDescriptor().GetLabelKeys()
	,  := protoMetricDescriptorTypeToMetricKind()
	 := make([]string, 0, len())
	for ,  := range  {
		 = append(, sanitize(.GetKey()))
	}

	for ,  := range .Timeseries {
No points to send just move forward.
			continue
		}

		,  := .protoTimeSeriesToMonitoringPoints(, )
		if  != nil {
			.recordDroppedTimeseries(1, )
			continue
		}
Each TimeSeries has labelValues which MUST be correlated with that from the MetricDescriptor
		,  := labelsPerTimeSeries(.defaultLabels, , .GetLabelValues())
		if  != nil {
			.recordDroppedTimeseries(1, )
			continue
		}
		.addTimeSeries(&monitoringpb.TimeSeries{
			Metric: &googlemetricpb.Metric{
				Type:   ,
				Labels: ,
			},
			MetricKind: ,
			ValueType:  ,
			Resource:   ,
			Points:     ,
		})
	}
}

func ( map[string]labelValue,  []string,  []*metricspb.LabelValue) (map[string]string, error) {
	if len() != len() {
		return nil, fmt.Errorf("length mismatch: len(labelKeys)=%d len(labelValues)=%d", len(), len())
	}

No labels for this metric
		return nil, nil
	}

Fill in the defaults firstly, irrespective of if the labelKeys and labelValues are mismatched.
	for ,  := range  {
		[] = .val
	}

	for ,  := range  {
		 := []
		if !.GetHasValue() {
			continue
		}
		[] = .GetValue()
	}

	return , nil
}

Skip create metric descriptor if configured
	if .o.SkipCMD {
		return nil
	}

	,  := newContextWithTimeout(, .o.Timeout)
	defer ()

	.protoMu.Lock()
	defer .protoMu.Unlock()

	 := .GetMetricDescriptor().GetName()
	if ,  := .protoMetricDescriptors[];  {
		return nil
	}

	if builtinMetric(.metricTypeFromProto()) {
		.protoMetricDescriptors[] = true
		return nil
	}
Otherwise, we encountered a cache-miss and should create the metric descriptor remotely.
	,  := .protoToMonitoringMetricDescriptor(, .defaultLabels)
	if  != nil {
		return 
	}

	if  = .createMetricDescriptor(, );  != nil {
		return 
	}

	.protoMetricDescriptors[] = true
	return nil
}

func ( *statsExporter) ( *metricspb.TimeSeries,  googlemetricpb.MetricDescriptor_MetricKind) ([]*monitoringpb.Point, error) {
	 := make([]*monitoringpb.Point, 0, len(.Points))
If we have a last value aggregation point i.e. MetricDescriptor_GAUGE StartTime should be nil.
		 := .StartTimestamp
		if  == googlemetricpb.MetricDescriptor_GAUGE {
			 = nil
		}
		,  := fromProtoPoint(, )
		if  != nil {
			return nil, 
		}
		 = append(, )
	}
	return , nil
}

func ( *statsExporter) ( *metricspb.Metric,  map[string]labelValue) (*googlemetricpb.MetricDescriptor, error) {
	if  == nil || .MetricDescriptor == nil {
		return nil, errNilMetricOrMetricDescriptor
	}

	 := .GetMetricDescriptor()
	 := .GetName()
	 := .GetUnit()
	 := .GetDescription()
	 := .metricTypeFromProto()
	 := .displayName()
	,  := protoMetricDescriptorTypeToMetricKind()

	 := &googlemetricpb.MetricDescriptor{
		Name:        fmt.Sprintf("projects/%s/metricDescriptors/%s", .o.ProjectID, ),
		DisplayName: ,
		Description: ,
		Unit:        ,
		Type:        ,
		MetricKind:  ,
		ValueType:   ,
		Labels:      labelDescriptorsFromProto(, .GetMetricDescriptor().GetLabelKeys()),
	}

	return , nil
}

func ( map[string]labelValue,  []*metricspb.LabelKey) []*labelpb.LabelDescriptor {
	 := make([]*labelpb.LabelDescriptor, 0, len()+len())
Fill in the defaults first.
	for ,  := range  {
		 = append(, &labelpb.LabelDescriptor{
			Key:         sanitize(),
			Description: .desc,
			ValueType:   labelpb.LabelDescriptor_STRING,
		})
	}
Now fill in those from the metric.
	for ,  := range  {
		 = append(, &labelpb.LabelDescriptor{
			Key:         sanitize(.GetKey()),
			Description: .GetDescription(),
			ValueType:   labelpb.LabelDescriptor_STRING, // We only use string tags
		})
	}
	return 
}

func ( *statsExporter) ( string) string {
	 := .o.MetricPrefix
	if .o.GetMetricPrefix != nil {
		 = .o.GetMetricPrefix()
	}
	if  != "" {
		 = path.Join(, )
	}
Still needed because the name may or may not have a "/" at the beginning.
		 = path.Join(defaultDomain, )
	}
	return 
}
hasDomain checks if the metric name already has a domain in it.
func ( string) bool {
	for ,  := range domains {
		if strings.Contains(, ) {
			return true
		}
	}
	return false
}

func ( *timestamppb.Timestamp,  *metricspb.Point) (*monitoringpb.Point, error) {
	if  == nil {
		return nil, nil
	}

	,  := protoToMetricPoint(.Value)
	if  != nil {
		return nil, 
	}

	return &monitoringpb.Point{
		Value: ,
		Interval: &monitoringpb.TimeInterval{
			StartTime: ,
			EndTime:   .Timestamp,
		},
	}, nil
}

func ( interface{}) (*monitoringpb.TypedValue, error) {
	if  == nil {
		return nil, nil
	}

	switch v := .(type) {
All the other types are not yet handled. TODO: (@odeke-em, @songy23) talk to the Stackdriver team to determine the use cases for: *TypedValue_BoolValue *TypedValue_StringValue and then file feature requests on OpenCensus-Specs and then OpenCensus-Proto, lest we shall error here. TODO: Add conversion from SummaryValue when https://github.com/census-ecosystem/opencensus-go-exporter-stackdriver/issues/66 has been figured out.
The first bucket bound should be 0.0 because the Metrics first bucket is [0, first_bound) but Stackdriver monitoring bucket bounds begin with -infinity (first bucket is (-infinity, 0))
								Bounds: addZeroBoundOnCondition(, .Explicit.Bounds...),
							},
						},
					}
				}
			}
			.DistributionValue.BucketCounts = addZeroBucketCountOnCondition(, bucketCounts(.Buckets)...)

		}
		return &monitoringpb.TypedValue{Value: }, nil
	}
}

func ( []*metricspb.DistributionValue_Bucket) []int64 {
	 := make([]int64, len())
	for ,  := range  {
		if  != nil {
			[] = .Count
		}
	}
	return 
}

func ( *metricspb.Metric) (googlemetricpb.MetricDescriptor_MetricKind, googlemetricpb.MetricDescriptor_ValueType) {
	 := .GetMetricDescriptor()
	if  == nil {
		return googlemetricpb.MetricDescriptor_METRIC_KIND_UNSPECIFIED, googlemetricpb.MetricDescriptor_VALUE_TYPE_UNSPECIFIED
	}

	switch .Type {
	case metricspb.MetricDescriptor_CUMULATIVE_INT64:
		return googlemetricpb.MetricDescriptor_CUMULATIVE, googlemetricpb.MetricDescriptor_INT64

	case metricspb.MetricDescriptor_CUMULATIVE_DOUBLE:
		return googlemetricpb.MetricDescriptor_CUMULATIVE, googlemetricpb.MetricDescriptor_DOUBLE

	case metricspb.MetricDescriptor_CUMULATIVE_DISTRIBUTION:
		return googlemetricpb.MetricDescriptor_CUMULATIVE, googlemetricpb.MetricDescriptor_DISTRIBUTION

	case metricspb.MetricDescriptor_GAUGE_DOUBLE:
		return googlemetricpb.MetricDescriptor_GAUGE, googlemetricpb.MetricDescriptor_DOUBLE

	case metricspb.MetricDescriptor_GAUGE_INT64:
		return googlemetricpb.MetricDescriptor_GAUGE, googlemetricpb.MetricDescriptor_INT64

	case metricspb.MetricDescriptor_GAUGE_DISTRIBUTION:
		return googlemetricpb.MetricDescriptor_GAUGE, googlemetricpb.MetricDescriptor_DISTRIBUTION

	default:
		return googlemetricpb.MetricDescriptor_METRIC_KIND_UNSPECIFIED, googlemetricpb.MetricDescriptor_VALUE_TYPE_UNSPECIFIED
	}