diff options
Diffstat (limited to 'impl_core/src/main/java/io/opencensus/implcore/stats/MeasureToViewMap.java')
-rw-r--r-- | impl_core/src/main/java/io/opencensus/implcore/stats/MeasureToViewMap.java | 185 |
1 files changed, 185 insertions, 0 deletions
diff --git a/impl_core/src/main/java/io/opencensus/implcore/stats/MeasureToViewMap.java b/impl_core/src/main/java/io/opencensus/implcore/stats/MeasureToViewMap.java new file mode 100644 index 00000000..50cb236a --- /dev/null +++ b/impl_core/src/main/java/io/opencensus/implcore/stats/MeasureToViewMap.java @@ -0,0 +1,185 @@ +/* + * Copyright 2017, 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 io.opencensus.implcore.stats; + +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Maps; +import com.google.common.collect.Multimap; +import io.opencensus.common.Clock; +import io.opencensus.common.Function; +import io.opencensus.common.Functions; +import io.opencensus.common.Timestamp; +import io.opencensus.stats.Measure; +import io.opencensus.stats.Measurement; +import io.opencensus.stats.Measurement.MeasurementDouble; +import io.opencensus.stats.Measurement.MeasurementLong; +import io.opencensus.stats.StatsCollectionState; +import io.opencensus.stats.View; +import io.opencensus.stats.ViewData; +import io.opencensus.tags.TagContext; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; +import javax.annotation.Nullable; +import javax.annotation.concurrent.GuardedBy; + +/** A class that stores a singleton map from {@code MeasureName}s to {@link MutableViewData}s. */ +final class MeasureToViewMap { + + /* + * A synchronized singleton map that stores the one-to-many mapping from Measures + * to MutableViewDatas. + */ + @GuardedBy("this") + private final Multimap<String, MutableViewData> mutableMap = + HashMultimap.<String, MutableViewData>create(); + + @GuardedBy("this") + private final Map<View.Name, View> registeredViews = new HashMap<View.Name, View>(); + + // TODO(songya): consider adding a Measure.Name class + @GuardedBy("this") + private final Map<String, Measure> registeredMeasures = Maps.newHashMap(); + + /** Returns a {@link ViewData} corresponding to the given {@link View.Name}. */ + synchronized ViewData getView(View.Name viewName, Clock clock, StatsCollectionState state) { + MutableViewData view = getMutableViewData(viewName); + return view == null ? null : view.toViewData(clock.now(), state); + } + + @Nullable + private synchronized MutableViewData getMutableViewData(View.Name viewName) { + View view = registeredViews.get(viewName); + if (view == null) { + return null; + } + Collection<MutableViewData> views = mutableMap.get(view.getMeasure().getName()); + for (MutableViewData viewData : views) { + if (viewData.getView().getName().equals(viewName)) { + return viewData; + } + } + throw new AssertionError( + "Internal error: Not recording stats for view: \"" + + viewName + + "\" registeredViews=" + + registeredViews + + ", mutableMap=" + + mutableMap); + } + + /** Enable stats collection for the given {@link View}. */ + synchronized void registerView(View view, Clock clock) { + View existing = registeredViews.get(view.getName()); + if (existing != null) { + if (existing.equals(view)) { + // Ignore views that are already registered. + return; + } else { + throw new IllegalArgumentException( + "A different view with the same name is already registered: " + existing); + } + } + Measure measure = view.getMeasure(); + Measure registeredMeasure = registeredMeasures.get(measure.getName()); + if (registeredMeasure != null && !registeredMeasure.equals(measure)) { + throw new IllegalArgumentException( + "A different measure with the same name is already registered: " + registeredMeasure); + } + registeredViews.put(view.getName(), view); + if (registeredMeasure == null) { + registeredMeasures.put(measure.getName(), measure); + } + mutableMap.put(view.getMeasure().getName(), MutableViewData.create(view, clock.now())); + } + + // Records stats with a set of tags. + synchronized void record(TagContext tags, MeasureMapInternal stats, Timestamp timestamp) { + Iterator<Measurement> iterator = stats.iterator(); + while (iterator.hasNext()) { + Measurement measurement = iterator.next(); + Measure measure = measurement.getMeasure(); + if (!measure.equals(registeredMeasures.get(measure.getName()))) { + // unregistered measures will be ignored. + return; + } + Collection<MutableViewData> views = mutableMap.get(measure.getName()); + for (MutableViewData view : views) { + measurement.match( + new RecordDoubleValueFunc(tags, view, timestamp), + new RecordLongValueFunc(tags, view, timestamp), + Functions.<Void>throwAssertionError()); + } + } + } + + // Clear stats for all the current MutableViewData + synchronized void clearStats() { + for (Entry<String, Collection<MutableViewData>> entry : mutableMap.asMap().entrySet()) { + for (MutableViewData mutableViewData : entry.getValue()) { + mutableViewData.clearStats(); + } + } + } + + // Resume stats collection for all MutableViewData. + synchronized void resumeStatsCollection(Timestamp now) { + for (Entry<String, Collection<MutableViewData>> entry : mutableMap.asMap().entrySet()) { + for (MutableViewData mutableViewData : entry.getValue()) { + mutableViewData.resumeStatsCollection(now); + } + } + } + + private static final class RecordDoubleValueFunc implements Function<MeasurementDouble, Void> { + @Override + public Void apply(MeasurementDouble arg) { + view.record(tags, arg.getValue(), timestamp); + return null; + } + + private final TagContext tags; + private final MutableViewData view; + private final Timestamp timestamp; + + private RecordDoubleValueFunc(TagContext tags, MutableViewData view, Timestamp timestamp) { + this.tags = tags; + this.view = view; + this.timestamp = timestamp; + } + } + + private static final class RecordLongValueFunc implements Function<MeasurementLong, Void> { + @Override + public Void apply(MeasurementLong arg) { + view.record(tags, arg.getValue(), timestamp); + return null; + } + + private final TagContext tags; + private final MutableViewData view; + private final Timestamp timestamp; + + private RecordLongValueFunc(TagContext tags, MutableViewData view, Timestamp timestamp) { + this.tags = tags; + this.view = view; + this.timestamp = timestamp; + } + } +} |