| /* |
| * 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 com.google.common.collect.Sets; |
| import io.opencensus.common.Clock; |
| import io.opencensus.common.Timestamp; |
| import io.opencensus.implcore.internal.CurrentState.State; |
| import io.opencensus.metrics.Metric; |
| import io.opencensus.stats.Measure; |
| import io.opencensus.stats.Measurement; |
| import io.opencensus.stats.View; |
| import io.opencensus.stats.ViewData; |
| import io.opencensus.tags.TagContext; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Map.Entry; |
| import java.util.Set; |
| import javax.annotation.concurrent.GuardedBy; |
| |
| /*>>> |
| import org.checkerframework.checker.nullness.qual.Nullable; |
| */ |
| |
| /** A class that stores a singleton map from {@code MeasureName}s to {@link MutableViewData}s. */ |
| @SuppressWarnings("deprecation") |
| 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(); |
| |
| // Cached set of exported views. It must be set to null whenever a view is registered or |
| // unregistered. |
| @javax.annotation.Nullable private volatile Set<View> exportedViews; |
| |
| /** Returns a {@link ViewData} corresponding to the given {@link View.Name}. */ |
| @javax.annotation.Nullable |
| synchronized ViewData getView(View.Name viewName, Clock clock, State state) { |
| MutableViewData view = getMutableViewData(viewName); |
| return view == null ? null : view.toViewData(clock.now(), state); |
| } |
| |
| Set<View> getExportedViews() { |
| Set<View> views = exportedViews; |
| if (views == null) { |
| synchronized (this) { |
| exportedViews = views = filterExportedViews(registeredViews.values()); |
| } |
| } |
| return views; |
| } |
| |
| // Returns the subset of the given views that should be exported |
| private static Set<View> filterExportedViews(Collection<View> allViews) { |
| Set<View> views = Sets.newHashSet(); |
| for (View view : allViews) { |
| if (view.getWindow() instanceof View.AggregationWindow.Cumulative) { |
| views.add(view); |
| } |
| } |
| return Collections.unmodifiableSet(views); |
| } |
| |
| /** Enable stats collection for the given {@link View}. */ |
| synchronized void registerView(View view, Clock clock) { |
| exportedViews = null; |
| 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); |
| } |
| Timestamp now = clock.now(); |
| mutableMap.put(view.getMeasure().getName(), MutableViewData.create(view, now)); |
| } |
| |
| @javax.annotation.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); |
| } |
| |
| // Records stats with a set of tags. |
| synchronized void record(TagContext tags, MeasureMapInternal stats, Timestamp timestamp) { |
| Iterator<Measurement> iterator = stats.iterator(); |
| Map<String, String> attachments = stats.getAttachments(); |
| while (iterator.hasNext()) { |
| Measurement measurement = iterator.next(); |
| Measure measure = measurement.getMeasure(); |
| if (!measure.equals(registeredMeasures.get(measure.getName()))) { |
| // unregistered measures will be ignored. |
| continue; |
| } |
| Collection<MutableViewData> viewDataCollection = mutableMap.get(measure.getName()); |
| for (MutableViewData viewData : viewDataCollection) { |
| viewData.record( |
| tags, RecordUtils.getDoubleValueFromMeasurement(measurement), timestamp, attachments); |
| } |
| } |
| } |
| |
| synchronized List<Metric> getMetrics(Clock clock, State state) { |
| List<Metric> metrics = new ArrayList<Metric>(); |
| Timestamp now = clock.now(); |
| for (Entry<String, MutableViewData> entry : mutableMap.entries()) { |
| Metric metric = entry.getValue().toMetric(now, state); |
| if (metric != null) { |
| metrics.add(metric); |
| } |
| } |
| return metrics; |
| } |
| |
| // 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); |
| } |
| } |
| } |
| } |