001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License. You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018
019 package org.apache.hadoop.metrics2.util;
020
021 import java.util.Collection;
022 import java.util.LinkedHashMap;
023 import java.util.Map;
024 import java.util.Set;
025
026 import org.apache.commons.logging.Log;
027 import org.apache.commons.logging.LogFactory;
028 import org.apache.hadoop.classification.InterfaceAudience;
029 import org.apache.hadoop.classification.InterfaceStability;
030 import org.apache.hadoop.metrics2.AbstractMetric;
031 import org.apache.hadoop.metrics2.MetricsRecord;
032 import org.apache.hadoop.metrics2.MetricsTag;
033
034 import com.google.common.base.Objects;
035 import com.google.common.collect.Maps;
036
037 /**
038 * A metrics cache for sinks that don't support sparse updates.
039 */
040 @InterfaceAudience.Public
041 @InterfaceStability.Evolving
042 public class MetricsCache {
043 static final Log LOG = LogFactory.getLog(MetricsCache.class);
044 static final int MAX_RECS_PER_NAME_DEFAULT = 1000;
045
046 private final Map<String, RecordCache> map = Maps.newHashMap();
047 private final int maxRecsPerName;
048
049 class RecordCache
050 extends LinkedHashMap<Collection<MetricsTag>, Record> {
051 private static final long serialVersionUID = 1L;
052 private boolean gotOverflow = false;
053
054 @Override
055 protected boolean removeEldestEntry(Map.Entry<Collection<MetricsTag>,
056 Record> eldest) {
057 boolean overflow = size() > maxRecsPerName;
058 if (overflow && !gotOverflow) {
059 LOG.warn("Metrics cache overflow at "+ size() +" for "+ eldest);
060 gotOverflow = true;
061 }
062 return overflow;
063 }
064 }
065
066 /**
067 * Cached record
068 */
069 public static class Record {
070 final Map<String, String> tags = Maps.newHashMap();
071 final Map<String, AbstractMetric> metrics = Maps.newHashMap();
072
073 /**
074 * Lookup a tag value
075 * @param key name of the tag
076 * @return the tag value
077 */
078 public String getTag(String key) {
079 return tags.get(key);
080 }
081
082 /**
083 * Lookup a metric value
084 * @param key name of the metric
085 * @return the metric value
086 */
087 public Number getMetric(String key) {
088 AbstractMetric metric = metrics.get(key);
089 return metric != null ? metric.value() : null;
090 }
091
092 /**
093 * Lookup a metric instance
094 * @param key name of the metric
095 * @return the metric instance
096 */
097 public AbstractMetric getMetricInstance(String key) {
098 return metrics.get(key);
099 }
100
101 /**
102 * @return the entry set of the tags of the record
103 */
104 public Set<Map.Entry<String, String>> tags() {
105 return tags.entrySet();
106 }
107
108 /**
109 * @deprecated use metricsEntrySet() instead
110 * @return entry set of metrics
111 */
112 @Deprecated
113 public Set<Map.Entry<String, Number>> metrics() {
114 Map<String, Number> map = new LinkedHashMap<String, Number>(
115 metrics.size());
116 for (Map.Entry<String, AbstractMetric> mapEntry : metrics.entrySet()) {
117 map.put(mapEntry.getKey(), mapEntry.getValue().value());
118 }
119 return map.entrySet();
120 }
121
122 /**
123 * @return entry set of metrics
124 */
125 public Set<Map.Entry<String, AbstractMetric>> metricsEntrySet() {
126 return metrics.entrySet();
127 }
128
129 @Override public String toString() {
130 return Objects.toStringHelper(this)
131 .add("tags", tags).add("metrics", metrics)
132 .toString();
133 }
134 }
135
136 public MetricsCache() {
137 this(MAX_RECS_PER_NAME_DEFAULT);
138 }
139
140 /**
141 * Construct a metrics cache
142 * @param maxRecsPerName limit of the number records per record name
143 */
144 public MetricsCache(int maxRecsPerName) {
145 this.maxRecsPerName = maxRecsPerName;
146 }
147
148 /**
149 * Update the cache and return the current cached record
150 * @param mr the update record
151 * @param includingTags cache tag values (for later lookup by name) if true
152 * @return the updated cache record
153 */
154 public Record update(MetricsRecord mr, boolean includingTags) {
155 String name = mr.name();
156 RecordCache recordCache = map.get(name);
157 if (recordCache == null) {
158 recordCache = new RecordCache();
159 map.put(name, recordCache);
160 }
161 Collection<MetricsTag> tags = mr.tags();
162 Record record = recordCache.get(tags);
163 if (record == null) {
164 record = new Record();
165 recordCache.put(tags, record);
166 }
167 for (AbstractMetric m : mr.metrics()) {
168 record.metrics.put(m.name(), m);
169 }
170 if (includingTags) {
171 // mostly for some sinks that include tags as part of a dense schema
172 for (MetricsTag t : mr.tags()) {
173 record.tags.put(t.name(), t.value());
174 }
175 }
176 return record;
177 }
178
179 /**
180 * Update the cache and return the current cache record
181 * @param mr the update record
182 * @return the updated cache record
183 */
184 public Record update(MetricsRecord mr) {
185 return update(mr, false);
186 }
187
188 /**
189 * Get the cached record
190 * @param name of the record
191 * @param tags of the record
192 * @return the cached record or null
193 */
194 public Record get(String name, Collection<MetricsTag> tags) {
195 RecordCache rc = map.get(name);
196 if (rc == null) return null;
197 return rc.get(tags);
198 }
199 }