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 019package org.apache.hadoop.metrics2.lib; 020 021import java.util.Collection; 022import java.util.Map; 023 024import com.google.common.collect.Maps; 025import com.google.common.base.Objects; 026 027import org.apache.hadoop.classification.InterfaceAudience; 028import org.apache.hadoop.classification.InterfaceStability; 029import org.apache.hadoop.metrics2.MetricsInfo; 030import org.apache.hadoop.metrics2.MetricsException; 031import org.apache.hadoop.metrics2.MetricsRecordBuilder; 032import org.apache.hadoop.metrics2.MetricsTag; 033import org.apache.hadoop.metrics2.impl.MsInfo; 034 035/** 036 * An optional metrics registry class for creating and maintaining a 037 * collection of MetricsMutables, making writing metrics source easier. 038 */ 039@InterfaceAudience.Public 040@InterfaceStability.Evolving 041public class MetricsRegistry { 042 private final Map<String, MutableMetric> metricsMap = Maps.newLinkedHashMap(); 043 private final Map<String, MetricsTag> tagsMap = Maps.newLinkedHashMap(); 044 private final MetricsInfo metricsInfo; 045 046 /** 047 * Construct the registry with a record name 048 * @param name of the record of the metrics 049 */ 050 public MetricsRegistry(String name) { 051 metricsInfo = Interns.info(name, name); 052 } 053 054 /** 055 * Construct the registry with a metadata object 056 * @param info the info object for the metrics record/group 057 */ 058 public MetricsRegistry(MetricsInfo info) { 059 metricsInfo = info; 060 } 061 062 /** 063 * @return the info object of the metrics registry 064 */ 065 public MetricsInfo info() { 066 return metricsInfo; 067 } 068 069 /** 070 * Get a metric by name 071 * @param name of the metric 072 * @return the metric object 073 */ 074 public synchronized MutableMetric get(String name) { 075 return metricsMap.get(name); 076 } 077 078 /** 079 * Get a tag by name 080 * @param name of the tag 081 * @return the tag object 082 */ 083 public synchronized MetricsTag getTag(String name) { 084 return tagsMap.get(name); 085 } 086 087 /** 088 * Create a mutable integer counter 089 * @param name of the metric 090 * @param desc metric description 091 * @param iVal initial value 092 * @return a new counter object 093 */ 094 public MutableCounterInt newCounter(String name, String desc, int iVal) { 095 return newCounter(Interns.info(name, desc), iVal); 096 } 097 098 /** 099 * Create a mutable integer counter 100 * @param info metadata of the metric 101 * @param iVal initial value 102 * @return a new counter object 103 */ 104 public synchronized MutableCounterInt newCounter(MetricsInfo info, int iVal) { 105 checkMetricName(info.name()); 106 MutableCounterInt ret = new MutableCounterInt(info, iVal); 107 metricsMap.put(info.name(), ret); 108 return ret; 109 } 110 111 /** 112 * Create a mutable long integer counter 113 * @param name of the metric 114 * @param desc metric description 115 * @param iVal initial value 116 * @return a new counter object 117 */ 118 public MutableCounterLong newCounter(String name, String desc, long iVal) { 119 return newCounter(Interns.info(name, desc), iVal); 120 } 121 122 /** 123 * Create a mutable long integer counter 124 * @param info metadata of the metric 125 * @param iVal initial value 126 * @return a new counter object 127 */ 128 public synchronized 129 MutableCounterLong newCounter(MetricsInfo info, long iVal) { 130 checkMetricName(info.name()); 131 MutableCounterLong ret = new MutableCounterLong(info, iVal); 132 metricsMap.put(info.name(), ret); 133 return ret; 134 } 135 136 /** 137 * Create a mutable integer gauge 138 * @param name of the metric 139 * @param desc metric description 140 * @param iVal initial value 141 * @return a new gauge object 142 */ 143 public MutableGaugeInt newGauge(String name, String desc, int iVal) { 144 return newGauge(Interns.info(name, desc), iVal); 145 } 146 /** 147 * Create a mutable integer gauge 148 * @param info metadata of the metric 149 * @param iVal initial value 150 * @return a new gauge object 151 */ 152 public synchronized MutableGaugeInt newGauge(MetricsInfo info, int iVal) { 153 checkMetricName(info.name()); 154 MutableGaugeInt ret = new MutableGaugeInt(info, iVal); 155 metricsMap.put(info.name(), ret); 156 return ret; 157 } 158 159 /** 160 * Create a mutable long integer gauge 161 * @param name of the metric 162 * @param desc metric description 163 * @param iVal initial value 164 * @return a new gauge object 165 */ 166 public MutableGaugeLong newGauge(String name, String desc, long iVal) { 167 return newGauge(Interns.info(name, desc), iVal); 168 } 169 170 /** 171 * Create a mutable long integer gauge 172 * @param info metadata of the metric 173 * @param iVal initial value 174 * @return a new gauge object 175 */ 176 public synchronized MutableGaugeLong newGauge(MetricsInfo info, long iVal) { 177 checkMetricName(info.name()); 178 MutableGaugeLong ret = new MutableGaugeLong(info, iVal); 179 metricsMap.put(info.name(), ret); 180 return ret; 181 } 182 183 /** 184 * Create a mutable metric that estimates quantiles of a stream of values 185 * @param name of the metric 186 * @param desc metric description 187 * @param sampleName of the metric (e.g., "Ops") 188 * @param valueName of the metric (e.g., "Time" or "Latency") 189 * @param interval rollover interval of estimator in seconds 190 * @return a new quantile estimator object 191 * @throws MetricsException if interval is not a positive integer 192 */ 193 public synchronized MutableQuantiles newQuantiles(String name, String desc, 194 String sampleName, String valueName, int interval) { 195 checkMetricName(name); 196 if (interval <= 0) { 197 throw new MetricsException("Interval should be positive. Value passed" + 198 " is: " + interval); 199 } 200 MutableQuantiles ret = 201 new MutableQuantiles(name, desc, sampleName, valueName, interval); 202 metricsMap.put(name, ret); 203 return ret; 204 } 205 206 /** 207 * Create a mutable metric with stats 208 * @param name of the metric 209 * @param desc metric description 210 * @param sampleName of the metric (e.g., "Ops") 211 * @param valueName of the metric (e.g., "Time" or "Latency") 212 * @param extended produce extended stat (stdev, min/max etc.) if true. 213 * @return a new mutable stat metric object 214 */ 215 public synchronized MutableStat newStat(String name, String desc, 216 String sampleName, String valueName, boolean extended) { 217 checkMetricName(name); 218 MutableStat ret = 219 new MutableStat(name, desc, sampleName, valueName, extended); 220 metricsMap.put(name, ret); 221 return ret; 222 } 223 224 /** 225 * Create a mutable metric with stats 226 * @param name of the metric 227 * @param desc metric description 228 * @param sampleName of the metric (e.g., "Ops") 229 * @param valueName of the metric (e.g., "Time" or "Latency") 230 * @return a new mutable metric object 231 */ 232 public MutableStat newStat(String name, String desc, 233 String sampleName, String valueName) { 234 return newStat(name, desc, sampleName, valueName, false); 235 } 236 237 /** 238 * Create a mutable rate metric 239 * @param name of the metric 240 * @return a new mutable metric object 241 */ 242 public MutableRate newRate(String name) { 243 return newRate(name, name, false); 244 } 245 246 /** 247 * Create a mutable rate metric 248 * @param name of the metric 249 * @param description of the metric 250 * @return a new mutable rate metric object 251 */ 252 public MutableRate newRate(String name, String description) { 253 return newRate(name, description, false); 254 } 255 256 /** 257 * Create a mutable rate metric (for throughput measurement) 258 * @param name of the metric 259 * @param desc description 260 * @param extended produce extended stat (stdev/min/max etc.) if true 261 * @return a new mutable rate metric object 262 */ 263 public MutableRate newRate(String name, String desc, boolean extended) { 264 return newRate(name, desc, extended, true); 265 } 266 267 @InterfaceAudience.Private 268 public synchronized MutableRate newRate(String name, String desc, 269 boolean extended, boolean returnExisting) { 270 if (returnExisting) { 271 MutableMetric rate = metricsMap.get(name); 272 if (rate != null) { 273 if (rate instanceof MutableRate) return (MutableRate) rate; 274 throw new MetricsException("Unexpected metrics type "+ rate.getClass() 275 +" for "+ name); 276 } 277 } 278 checkMetricName(name); 279 MutableRate ret = new MutableRate(name, desc, extended); 280 metricsMap.put(name, ret); 281 return ret; 282 } 283 284 public synchronized MutableRatesWithAggregation newRatesWithAggregation( 285 String name) { 286 checkMetricName(name); 287 MutableRatesWithAggregation rates = new MutableRatesWithAggregation(); 288 metricsMap.put(name, rates); 289 return rates; 290 } 291 292 synchronized void add(String name, MutableMetric metric) { 293 checkMetricName(name); 294 metricsMap.put(name, metric); 295 } 296 297 /** 298 * Add sample to a stat metric by name. 299 * @param name of the metric 300 * @param value of the snapshot to add 301 */ 302 public synchronized void add(String name, long value) { 303 MutableMetric m = metricsMap.get(name); 304 305 if (m != null) { 306 if (m instanceof MutableStat) { 307 ((MutableStat) m).add(value); 308 } 309 else { 310 throw new MetricsException("Unsupported add(value) for metric "+ name); 311 } 312 } 313 else { 314 metricsMap.put(name, newRate(name)); // default is a rate metric 315 add(name, value); 316 } 317 } 318 319 /** 320 * Set the metrics context tag 321 * @param name of the context 322 * @return the registry itself as a convenience 323 */ 324 public MetricsRegistry setContext(String name) { 325 return tag(MsInfo.Context, name, true); 326 } 327 328 /** 329 * Add a tag to the metrics 330 * @param name of the tag 331 * @param description of the tag 332 * @param value of the tag 333 * @return the registry (for keep adding tags) 334 */ 335 public MetricsRegistry tag(String name, String description, String value) { 336 return tag(name, description, value, false); 337 } 338 339 /** 340 * Add a tag to the metrics 341 * @param name of the tag 342 * @param description of the tag 343 * @param value of the tag 344 * @param override existing tag if true 345 * @return the registry (for keep adding tags) 346 */ 347 public MetricsRegistry tag(String name, String description, String value, 348 boolean override) { 349 return tag(Interns.info(name, description), value, override); 350 } 351 352 /** 353 * Add a tag to the metrics 354 * @param info metadata of the tag 355 * @param value of the tag 356 * @param override existing tag if true 357 * @return the registry (for keep adding tags etc.) 358 */ 359 public synchronized 360 MetricsRegistry tag(MetricsInfo info, String value, boolean override) { 361 if (!override) checkTagName(info.name()); 362 tagsMap.put(info.name(), Interns.tag(info, value)); 363 return this; 364 } 365 366 public MetricsRegistry tag(MetricsInfo info, String value) { 367 return tag(info, value, false); 368 } 369 370 Collection<MetricsTag> tags() { 371 return tagsMap.values(); 372 } 373 374 Collection<MutableMetric> metrics() { 375 return metricsMap.values(); 376 } 377 378 private void checkMetricName(String name) { 379 // Check for invalid characters in metric name 380 boolean foundWhitespace = false; 381 for (int i = 0; i < name.length(); i++) { 382 char c = name.charAt(i); 383 if (Character.isWhitespace(c)) { 384 foundWhitespace = true; 385 break; 386 } 387 } 388 if (foundWhitespace) { 389 throw new MetricsException("Metric name '"+ name + 390 "' contains illegal whitespace character"); 391 } 392 // Check if name has already been registered 393 if (metricsMap.containsKey(name)) { 394 throw new MetricsException("Metric name "+ name +" already exists!"); 395 } 396 } 397 398 private void checkTagName(String name) { 399 if (tagsMap.containsKey(name)) { 400 throw new MetricsException("Tag "+ name +" already exists!"); 401 } 402 } 403 404 /** 405 * Sample all the mutable metrics and put the snapshot in the builder 406 * @param builder to contain the metrics snapshot 407 * @param all get all the metrics even if the values are not changed. 408 */ 409 public synchronized void snapshot(MetricsRecordBuilder builder, boolean all) { 410 for (MetricsTag tag : tags()) { 411 builder.add(tag); 412 } 413 for (MutableMetric metric : metrics()) { 414 metric.snapshot(builder, all); 415 } 416 } 417 418 @Override public String toString() { 419 return Objects.toStringHelper(this) 420 .add("info", metricsInfo).add("tags", tags()).add("metrics", metrics()) 421 .toString(); 422 } 423}