1 /* 2 * Copyright 2017, 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 package io.opencensus.exporter.stats.signalfx; 18 19 import com.google.common.annotations.VisibleForTesting; 20 import com.google.common.base.Preconditions; 21 import com.google.common.base.Strings; 22 import com.signalfx.metrics.protobuf.SignalFxProtocolBuffers.DataPoint; 23 import com.signalfx.metrics.protobuf.SignalFxProtocolBuffers.Datum; 24 import com.signalfx.metrics.protobuf.SignalFxProtocolBuffers.Dimension; 25 import com.signalfx.metrics.protobuf.SignalFxProtocolBuffers.MetricType; 26 import io.opencensus.common.Function; 27 import io.opencensus.stats.Aggregation; 28 import io.opencensus.stats.AggregationData; 29 import io.opencensus.stats.AggregationData.CountData; 30 import io.opencensus.stats.AggregationData.DistributionData; 31 import io.opencensus.stats.AggregationData.LastValueDataDouble; 32 import io.opencensus.stats.AggregationData.LastValueDataLong; 33 import io.opencensus.stats.AggregationData.SumDataDouble; 34 import io.opencensus.stats.AggregationData.SumDataLong; 35 import io.opencensus.stats.View; 36 import io.opencensus.stats.ViewData; 37 import io.opencensus.tags.TagKey; 38 import io.opencensus.tags.TagValue; 39 import java.util.ArrayList; 40 import java.util.Collections; 41 import java.util.List; 42 import java.util.ListIterator; 43 import java.util.Map; 44 45 /*>>> 46 import org.checkerframework.checker.nullness.qual.Nullable; 47 */ 48 49 /** Adapter for a {@code ViewData}'s contents into SignalFx datapoints. */ 50 @SuppressWarnings("deprecation") 51 final class SignalFxSessionAdaptor { 52 53 private SignalFxSessionAdaptor() {} 54 55 /** 56 * Converts the given view data into datapoints that can be sent to SignalFx. 57 * 58 * <p>The view name is used as the metric name, and the aggregation type and aggregation window 59 * type determine the metric type. 60 * 61 * @param data The {@link ViewData} containing the aggregation data of each combination of tag 62 * values. 63 * @return A list of datapoints for the corresponding metric timeseries of this view's metric. 64 */ 65 static List<DataPoint> adapt(ViewData data) { 66 View view = data.getView(); 67 List<TagKey> keys = view.getColumns(); 68 69 MetricType metricType = getMetricTypeForAggregation(view.getAggregation(), view.getWindow()); 70 if (metricType == null) { 71 return Collections.emptyList(); 72 } 73 74 List<DataPoint> datapoints = new ArrayList<>(data.getAggregationMap().size()); 75 for (Map.Entry<List</*@Nullable*/ TagValue>, AggregationData> entry : 76 data.getAggregationMap().entrySet()) { 77 datapoints.add( 78 DataPoint.newBuilder() 79 .setMetric(view.getName().asString()) 80 .setMetricType(metricType) 81 .addAllDimensions(createDimensions(keys, entry.getKey())) 82 .setValue(createDatum(entry.getValue())) 83 .build()); 84 } 85 return datapoints; 86 } 87 88 @VisibleForTesting 89 @javax.annotation.Nullable 90 static MetricType getMetricTypeForAggregation( 91 Aggregation aggregation, View.AggregationWindow window) { 92 if (aggregation instanceof Aggregation.Mean || aggregation instanceof Aggregation.LastValue) { 93 return MetricType.GAUGE; 94 } else if (aggregation instanceof Aggregation.Count || aggregation instanceof Aggregation.Sum) { 95 if (window instanceof View.AggregationWindow.Cumulative) { 96 return MetricType.CUMULATIVE_COUNTER; 97 } 98 // TODO(mpetazzoni): support incremental counters when AggregationWindow.Interval is ready 99 } 100 101 // TODO(mpetazzoni): add support for histograms (Aggregation.Distribution). 102 return null; 103 } 104 105 @VisibleForTesting 106 static Iterable<Dimension> createDimensions( 107 List<TagKey> keys, List</*@Nullable*/ TagValue> values) { 108 Preconditions.checkArgument( 109 keys.size() == values.size(), "TagKeys and TagValues don't have the same size."); 110 List<Dimension> dimensions = new ArrayList<>(keys.size()); 111 for (ListIterator<TagKey> it = keys.listIterator(); it.hasNext(); ) { 112 TagKey key = it.next(); 113 TagValue value = values.get(it.previousIndex()); 114 if (value == null || Strings.isNullOrEmpty(value.asString())) { 115 continue; 116 } 117 dimensions.add(createDimension(key, value)); 118 } 119 return dimensions; 120 } 121 122 @VisibleForTesting 123 static Dimension createDimension(TagKey key, TagValue value) { 124 return Dimension.newBuilder().setKey(key.getName()).setValue(value.asString()).build(); 125 } 126 127 @VisibleForTesting 128 static Datum createDatum(AggregationData data) { 129 final Datum.Builder builder = Datum.newBuilder(); 130 data.match( 131 new Function<SumDataDouble, Void>() { 132 @Override 133 public Void apply(SumDataDouble arg) { 134 builder.setDoubleValue(arg.getSum()); 135 return null; 136 } 137 }, 138 new Function<SumDataLong, Void>() { 139 @Override 140 public Void apply(SumDataLong arg) { 141 builder.setIntValue(arg.getSum()); 142 return null; 143 } 144 }, 145 new Function<CountData, Void>() { 146 @Override 147 public Void apply(CountData arg) { 148 builder.setIntValue(arg.getCount()); 149 return null; 150 } 151 }, 152 new Function<DistributionData, Void>() { 153 @Override 154 public Void apply(DistributionData arg) { 155 // TODO(mpetazzoni): add histogram support. 156 throw new IllegalArgumentException("Distribution aggregations are not supported"); 157 } 158 }, 159 new Function<LastValueDataDouble, Void>() { 160 @Override 161 public Void apply(LastValueDataDouble arg) { 162 builder.setDoubleValue(arg.getLastValue()); 163 return null; 164 } 165 }, 166 new Function<LastValueDataLong, Void>() { 167 @Override 168 public Void apply(LastValueDataLong arg) { 169 builder.setIntValue(arg.getLastValue()); 170 return null; 171 } 172 }, 173 new Function<AggregationData, Void>() { 174 @Override 175 public Void apply(AggregationData arg) { 176 // TODO(songya): remove this once Mean aggregation is completely removed. Before that 177 // we need to continue supporting Mean, since it could still be used by users and some 178 // deprecated RPC views. 179 if (arg instanceof AggregationData.MeanData) { 180 builder.setDoubleValue(((AggregationData.MeanData) arg).getMean()); 181 return null; 182 } 183 throw new IllegalArgumentException("Unknown Aggregation."); 184 } 185 }); 186 return builder.build(); 187 } 188 } 189