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.lib;
020
021 import java.util.Map;
022 import java.util.LinkedHashMap;
023
024 import org.apache.commons.logging.Log;
025 import org.apache.commons.logging.LogFactory;
026 import org.apache.hadoop.classification.InterfaceAudience;
027 import org.apache.hadoop.classification.InterfaceStability;
028 import org.apache.hadoop.metrics2.MetricsInfo;
029 import org.apache.hadoop.metrics2.MetricsTag;
030
031 /**
032 * Helpers to create interned metrics info
033 */
034 @InterfaceAudience.Public
035 @InterfaceStability.Evolving
036 public class Interns {
037 private static final Log LOG = LogFactory.getLog(Interns.class);
038
039 // A simple intern cache with two keys
040 // (to avoid creating new (combined) key objects for lookup)
041 private static abstract class CacheWith2Keys<K1, K2, V> {
042 private final Map<K1, Map<K2, V>> k1Map =
043 new LinkedHashMap<K1, Map<K2, V>>() {
044 private static final long serialVersionUID = 1L;
045 private boolean gotOverflow = false;
046 @Override
047 protected boolean removeEldestEntry(Map.Entry<K1, Map<K2, V>> e) {
048 boolean overflow = expireKey1At(size());
049 if (overflow && !gotOverflow) {
050 LOG.warn("Metrics intern cache overflow at "+ size() +" for "+ e);
051 gotOverflow = true;
052 }
053 return overflow;
054 }
055 };
056
057 abstract protected boolean expireKey1At(int size);
058 abstract protected boolean expireKey2At(int size);
059 abstract protected V newValue(K1 k1, K2 k2);
060
061 synchronized V add(K1 k1, K2 k2) {
062 Map<K2, V> k2Map = k1Map.get(k1);
063 if (k2Map == null) {
064 k2Map = new LinkedHashMap<K2, V>() {
065 private static final long serialVersionUID = 1L;
066 private boolean gotOverflow = false;
067 @Override protected boolean removeEldestEntry(Map.Entry<K2, V> e) {
068 boolean overflow = expireKey2At(size());
069 if (overflow && !gotOverflow) {
070 LOG.warn("Metrics intern cache overflow at "+ size() +" for "+ e);
071 gotOverflow = true;
072 }
073 return overflow;
074 }
075 };
076 k1Map.put(k1, k2Map);
077 }
078 V v = k2Map.get(k2);
079 if (v == null) {
080 v = newValue(k1, k2);
081 k2Map.put(k2, v);
082 }
083 return v;
084 }
085 }
086
087 // Sanity limits in case of misuse/abuse.
088 static final int MAX_INFO_NAMES = 2010;
089 static final int MAX_INFO_DESCS = 100; // distinct per name
090
091 enum Info {
092 INSTANCE;
093
094 final CacheWith2Keys<String, String, MetricsInfo> cache =
095 new CacheWith2Keys<String, String, MetricsInfo>() {
096
097 @Override protected boolean expireKey1At(int size) {
098 return size > MAX_INFO_NAMES;
099 }
100
101 @Override protected boolean expireKey2At(int size) {
102 return size > MAX_INFO_DESCS;
103 }
104
105 @Override protected MetricsInfo newValue(String name, String desc) {
106 return new MetricsInfoImpl(name, desc);
107 }
108 };
109 }
110
111 /**
112 * Get a metric info object
113 * @param name
114 * @param description
115 * @return an interned metric info object
116 */
117 public static MetricsInfo info(String name, String description) {
118 return Info.INSTANCE.cache.add(name, description);
119 }
120
121 // Sanity limits
122 static final int MAX_TAG_NAMES = 100;
123 static final int MAX_TAG_VALUES = 1000; // distinct per name
124
125 enum Tags {
126 INSTANCE;
127
128 final CacheWith2Keys<MetricsInfo, String, MetricsTag> cache =
129 new CacheWith2Keys<MetricsInfo, String, MetricsTag>() {
130
131 @Override protected boolean expireKey1At(int size) {
132 return size > MAX_TAG_NAMES;
133 }
134
135 @Override protected boolean expireKey2At(int size) {
136 return size > MAX_TAG_VALUES;
137 }
138
139 @Override protected MetricsTag newValue(MetricsInfo info, String value) {
140 return new MetricsTag(info, value);
141 }
142 };
143 }
144
145 /**
146 * Get a metrics tag
147 * @param info of the tag
148 * @param value of the tag
149 * @return an interned metrics tag
150 */
151 public static MetricsTag tag(MetricsInfo info, String value) {
152 return Tags.INSTANCE.cache.add(info, value);
153 }
154
155 /**
156 * Get a metrics tag
157 * @param name of the tag
158 * @param description of the tag
159 * @param value of the tag
160 * @return an interned metrics tag
161 */
162 public static MetricsTag tag(String name, String description, String value) {
163 return Tags.INSTANCE.cache.add(info(name, description), value);
164 }
165 }