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