Home | History | Annotate | Download | only in signalfx
      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