Home | History | Annotate | Download | only in stats
      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.stats;
     18 
     19 import io.opencensus.common.Functions;
     20 import io.opencensus.common.Timestamp;
     21 import io.opencensus.internal.Utils;
     22 import io.opencensus.stats.Measure.MeasureDouble;
     23 import io.opencensus.stats.Measure.MeasureLong;
     24 import io.opencensus.tags.TagContext;
     25 import io.opencensus.tags.TagValue;
     26 import java.util.Collection;
     27 import java.util.Collections;
     28 import java.util.HashMap;
     29 import java.util.HashSet;
     30 import java.util.List;
     31 import java.util.Map;
     32 import java.util.Set;
     33 import javax.annotation.concurrent.GuardedBy;
     34 import javax.annotation.concurrent.Immutable;
     35 import javax.annotation.concurrent.ThreadSafe;
     36 
     37 /*>>>
     38 import org.checkerframework.checker.nullness.qual.Nullable;
     39 */
     40 
     41 /** No-op implementations of stats classes. */
     42 final class NoopStats {
     43 
     44   private NoopStats() {}
     45 
     46   /**
     47    * Returns a {@code StatsComponent} that has a no-op implementation for {@link StatsRecorder}.
     48    *
     49    * @return a {@code StatsComponent} that has a no-op implementation for {@code StatsRecorder}.
     50    */
     51   static StatsComponent newNoopStatsComponent() {
     52     return new NoopStatsComponent();
     53   }
     54 
     55   /**
     56    * Returns a {@code StatsRecorder} that does not record any data.
     57    *
     58    * @return a {@code StatsRecorder} that does not record any data.
     59    */
     60   static StatsRecorder getNoopStatsRecorder() {
     61     return NoopStatsRecorder.INSTANCE;
     62   }
     63 
     64   /**
     65    * Returns a {@code MeasureMap} that ignores all calls to {@link MeasureMap#put}.
     66    *
     67    * @return a {@code MeasureMap} that ignores all calls to {@code MeasureMap#put}.
     68    */
     69   static MeasureMap getNoopMeasureMap() {
     70     return NoopMeasureMap.INSTANCE;
     71   }
     72 
     73   /**
     74    * Returns a {@code ViewManager} that maintains a map of views, but always returns empty {@link
     75    * ViewData}s.
     76    *
     77    * @return a {@code ViewManager} that maintains a map of views, but always returns empty {@code
     78    *     ViewData}s.
     79    */
     80   static ViewManager newNoopViewManager() {
     81     return new NoopViewManager();
     82   }
     83 
     84   @ThreadSafe
     85   private static final class NoopStatsComponent extends StatsComponent {
     86     private final ViewManager viewManager = newNoopViewManager();
     87     private volatile boolean isRead;
     88 
     89     @Override
     90     public ViewManager getViewManager() {
     91       return viewManager;
     92     }
     93 
     94     @Override
     95     public StatsRecorder getStatsRecorder() {
     96       return getNoopStatsRecorder();
     97     }
     98 
     99     @Override
    100     public StatsCollectionState getState() {
    101       isRead = true;
    102       return StatsCollectionState.DISABLED;
    103     }
    104 
    105     @Override
    106     @Deprecated
    107     public void setState(StatsCollectionState state) {
    108       Utils.checkNotNull(state, "state");
    109       Utils.checkState(!isRead, "State was already read, cannot set state.");
    110     }
    111   }
    112 
    113   @Immutable
    114   private static final class NoopStatsRecorder extends StatsRecorder {
    115     static final StatsRecorder INSTANCE = new NoopStatsRecorder();
    116 
    117     @Override
    118     public MeasureMap newMeasureMap() {
    119       return getNoopMeasureMap();
    120     }
    121   }
    122 
    123   @Immutable
    124   private static final class NoopMeasureMap extends MeasureMap {
    125     static final MeasureMap INSTANCE = new NoopMeasureMap();
    126 
    127     @Override
    128     public MeasureMap put(MeasureDouble measure, double value) {
    129       return this;
    130     }
    131 
    132     @Override
    133     public MeasureMap put(MeasureLong measure, long value) {
    134       return this;
    135     }
    136 
    137     @Override
    138     public void record() {}
    139 
    140     @Override
    141     public void record(TagContext tags) {
    142       Utils.checkNotNull(tags, "tags");
    143     }
    144   }
    145 
    146   @ThreadSafe
    147   private static final class NoopViewManager extends ViewManager {
    148     private static final Timestamp ZERO_TIMESTAMP = Timestamp.create(0, 0);
    149 
    150     @GuardedBy("registeredViews")
    151     private final Map<View.Name, View> registeredViews = new HashMap<View.Name, View>();
    152 
    153     // Cached set of exported views. It must be set to null whenever a view is registered or
    154     // unregistered.
    155     @javax.annotation.Nullable private volatile Set<View> exportedViews;
    156 
    157     @Override
    158     public void registerView(View newView) {
    159       Utils.checkNotNull(newView, "newView");
    160       synchronized (registeredViews) {
    161         exportedViews = null;
    162         View existing = registeredViews.get(newView.getName());
    163         Utils.checkArgument(
    164             existing == null || newView.equals(existing),
    165             "A different view with the same name already exists.");
    166         if (existing == null) {
    167           registeredViews.put(newView.getName(), newView);
    168         }
    169       }
    170     }
    171 
    172     @Override
    173     @javax.annotation.Nullable
    174     @SuppressWarnings("deprecation")
    175     public ViewData getView(View.Name name) {
    176       Utils.checkNotNull(name, "name");
    177       synchronized (registeredViews) {
    178         View view = registeredViews.get(name);
    179         if (view == null) {
    180           return null;
    181         } else {
    182           return ViewData.create(
    183               view,
    184               Collections.<List</*@Nullable*/ TagValue>, AggregationData>emptyMap(),
    185               view.getWindow()
    186                   .match(
    187                       Functions.<ViewData.AggregationWindowData>returnConstant(
    188                           ViewData.AggregationWindowData.CumulativeData.create(
    189                               ZERO_TIMESTAMP, ZERO_TIMESTAMP)),
    190                       Functions.<ViewData.AggregationWindowData>returnConstant(
    191                           ViewData.AggregationWindowData.IntervalData.create(ZERO_TIMESTAMP)),
    192                       Functions.<ViewData.AggregationWindowData>throwAssertionError()));
    193         }
    194       }
    195     }
    196 
    197     @Override
    198     public Set<View> getAllExportedViews() {
    199       Set<View> views = exportedViews;
    200       if (views == null) {
    201         synchronized (registeredViews) {
    202           exportedViews = views = filterExportedViews(registeredViews.values());
    203         }
    204       }
    205       return views;
    206     }
    207 
    208     // Returns the subset of the given views that should be exported
    209     @SuppressWarnings("deprecation")
    210     private static Set<View> filterExportedViews(Collection<View> allViews) {
    211       Set<View> views = new HashSet<View>();
    212       for (View view : allViews) {
    213         if (view.getWindow() instanceof View.AggregationWindow.Interval) {
    214           continue;
    215         }
    216         views.add(view);
    217       }
    218       return Collections.unmodifiableSet(views);
    219     }
    220   }
    221 }
    222