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 */ 018package org.apache.hadoop.metrics2.lib; 019 020import java.util.Collection; 021import java.util.concurrent.ConcurrentMap; 022import org.apache.hadoop.hbase.metrics.Interns; 023import org.apache.hadoop.metrics2.MetricsException; 024import org.apache.hadoop.metrics2.MetricsInfo; 025import org.apache.hadoop.metrics2.MetricsRecordBuilder; 026import org.apache.hadoop.metrics2.MetricsTag; 027import org.apache.hadoop.metrics2.impl.MsInfo; 028import org.apache.yetus.audience.InterfaceAudience; 029 030import org.apache.hbase.thirdparty.com.google.common.base.MoreObjects; 031import org.apache.hbase.thirdparty.com.google.common.collect.Maps; 032 033/** 034 * An optional metrics registry class for creating and maintaining a collection of MetricsMutables, 035 * making writing metrics source easier. NOTE: this is a copy of 036 * org.apache.hadoop.metrics2.lib.MetricsRegistry with added one feature: metrics can be removed. 037 * When HADOOP-8313 is fixed, usages of this class should be substituted with 038 * org.apache.hadoop.metrics2.lib.MetricsRegistry. This implementation also provides handy methods 039 * for creating metrics dynamically. Another difference is that metricsMap implementation is 040 * substituted with thread-safe map, as we allow dynamic metrics additions/removals. 041 */ 042@InterfaceAudience.Private 043public class DynamicMetricsRegistry { 044 045 private final ConcurrentMap<String, MutableMetric> metricsMap = Maps.newConcurrentMap(); 046 private final ConcurrentMap<String, MetricsTag> tagsMap = Maps.newConcurrentMap(); 047 private final MetricsInfo metricsInfo; 048 private final DefaultMetricsSystemHelper helper = new DefaultMetricsSystemHelper(); 049 private final static String[] histogramSuffixes = new String[] { "_num_ops", "_min", "_max", 050 "_median", "_75th_percentile", "_90th_percentile", "_95th_percentile", "_99th_percentile" }; 051 052 /** 053 * Construct the registry with a record name 054 * @param name of the record of the metrics 055 */ 056 public DynamicMetricsRegistry(String name) { 057 this(Interns.info(name, name)); 058 } 059 060 /** 061 * Construct the registry with a metadata object 062 * @param info the info object for the metrics record/group 063 */ 064 public DynamicMetricsRegistry(MetricsInfo info) { 065 metricsInfo = info; 066 } 067 068 /** Returns the info object of the metrics registry */ 069 public MetricsInfo info() { 070 return metricsInfo; 071 } 072 073 /** 074 * Get a metric by name 075 * @param name of the metric 076 * @return the metric object 077 */ 078 public MutableMetric get(String name) { 079 return metricsMap.get(name); 080 } 081 082 /** 083 * Get a tag by name 084 * @param name of the tag 085 * @return the tag object 086 */ 087 public MetricsTag getTag(String name) { 088 return tagsMap.get(name); 089 } 090 091 /** 092 * Create a mutable long integer counter 093 * @param name of the metric 094 * @param desc metric description 095 * @param iVal initial value 096 * @return a new counter object 097 */ 098 public MutableFastCounter newCounter(String name, String desc, long iVal) { 099 return newCounter(new MetricsInfoImpl(name, desc), iVal); 100 } 101 102 /** 103 * Create a mutable long integer counter 104 * @param info metadata of the metric 105 * @param iVal initial value 106 * @return a new counter object 107 */ 108 public MutableFastCounter newCounter(MetricsInfo info, long iVal) { 109 MutableFastCounter ret = new MutableFastCounter(info, iVal); 110 return addNewMetricIfAbsent(info.name(), ret, MutableFastCounter.class); 111 } 112 113 /** 114 * Create a mutable long integer gauge 115 * @param name of the metric 116 * @param desc metric description 117 * @param iVal initial value 118 * @return a new gauge object 119 */ 120 public MutableGaugeLong newGauge(String name, String desc, long iVal) { 121 return newGauge(new MetricsInfoImpl(name, desc), iVal); 122 } 123 124 /** 125 * Create a mutable long integer gauge 126 * @param info metadata of the metric 127 * @param iVal initial value 128 * @return a new gauge object 129 */ 130 public MutableGaugeLong newGauge(MetricsInfo info, long iVal) { 131 MutableGaugeLong ret = new MutableGaugeLong(info, iVal); 132 return addNewMetricIfAbsent(info.name(), ret, MutableGaugeLong.class); 133 } 134 135 /** 136 * Create a mutable metric with stats 137 * @param name of the metric 138 * @param desc metric description 139 * @param sampleName of the metric (e.g., "Ops") 140 * @param valueName of the metric (e.g., "Time" or "Latency") 141 * @param extended produce extended stat (stdev, min/max etc.) if true. 142 * @return a new mutable stat metric object 143 */ 144 public MutableStat newStat(String name, String desc, String sampleName, String valueName, 145 boolean extended) { 146 MutableStat ret = new MutableStat(name, desc, sampleName, valueName, extended); 147 return addNewMetricIfAbsent(name, ret, MutableStat.class); 148 } 149 150 /** 151 * Create a mutable metric with stats 152 * @param name of the metric 153 * @param desc metric description 154 * @param sampleName of the metric (e.g., "Ops") 155 * @param valueName of the metric (e.g., "Time" or "Latency") 156 * @return a new mutable metric object 157 */ 158 public MutableStat newStat(String name, String desc, String sampleName, String valueName) { 159 return newStat(name, desc, sampleName, valueName, false); 160 } 161 162 /** 163 * Create a mutable rate metric 164 * @param name of the metric 165 * @return a new mutable metric object 166 */ 167 public MutableRate newRate(String name) { 168 return newRate(name, name, false); 169 } 170 171 /** 172 * Create a mutable rate metric 173 * @param name of the metric 174 * @param description of the metric 175 * @return a new mutable rate metric object 176 */ 177 public MutableRate newRate(String name, String description) { 178 return newRate(name, description, false); 179 } 180 181 /** 182 * Create a mutable rate metric (for throughput measurement) 183 * @param name of the metric 184 * @param desc description 185 * @param extended produce extended stat (stdev/min/max etc.) if true 186 * @return a new mutable rate metric object 187 */ 188 public MutableRate newRate(String name, String desc, boolean extended) { 189 return newRate(name, desc, extended, true); 190 } 191 192 @InterfaceAudience.Private 193 public MutableRate newRate(String name, String desc, boolean extended, boolean returnExisting) { 194 if (returnExisting) { 195 MutableMetric rate = metricsMap.get(name); 196 if (rate != null) { 197 if (rate instanceof MutableRate) { 198 return (MutableRate) rate; 199 } 200 201 throw new MetricsException("Unexpected metrics type " + rate.getClass() + " for " + name); 202 } 203 } 204 MutableRate ret = new MutableRate(name, desc, extended); 205 return addNewMetricIfAbsent(name, ret, MutableRate.class); 206 } 207 208 /** 209 * Create a new histogram. 210 * @param name Name of the histogram. 211 * @return A new MutableHistogram 212 */ 213 public MutableHistogram newHistogram(String name) { 214 return newHistogram(name, ""); 215 } 216 217 /** 218 * Create a new histogram. 219 * @param name The name of the histogram 220 * @param desc The description of the data in the histogram. 221 * @return A new MutableHistogram 222 */ 223 public MutableHistogram newHistogram(String name, String desc) { 224 MutableHistogram histo = new MutableHistogram(name, desc); 225 return addNewMetricIfAbsent(name, histo, MutableHistogram.class); 226 } 227 228 /** 229 * Create a new histogram with time range counts. 230 * @param name Name of the histogram. 231 * @return A new MutableTimeHistogram 232 */ 233 public MutableTimeHistogram newTimeHistogram(String name) { 234 return newTimeHistogram(name, ""); 235 } 236 237 /** 238 * Create a new histogram with time range counts. 239 * @param name The name of the histogram 240 * @param desc The description of the data in the histogram. 241 * @return A new MutableTimeHistogram 242 */ 243 public MutableTimeHistogram newTimeHistogram(String name, String desc) { 244 MutableTimeHistogram histo = new MutableTimeHistogram(name, desc); 245 return addNewMetricIfAbsent(name, histo, MutableTimeHistogram.class); 246 } 247 248 /** 249 * Create a new histogram with size range counts. 250 * @param name Name of the histogram. 251 * @return A new MutableSizeHistogram 252 */ 253 public MutableSizeHistogram newSizeHistogram(String name) { 254 return newSizeHistogram(name, ""); 255 } 256 257 /** 258 * Create a new histogram with size range counts. 259 * @param name The name of the histogram 260 * @param desc The description of the data in the histogram. 261 * @return A new MutableSizeHistogram 262 */ 263 public MutableSizeHistogram newSizeHistogram(String name, String desc) { 264 MutableSizeHistogram histo = new MutableSizeHistogram(name, desc); 265 return addNewMetricIfAbsent(name, histo, MutableSizeHistogram.class); 266 } 267 268 synchronized void add(String name, MutableMetric metric) { 269 addNewMetricIfAbsent(name, metric, MutableMetric.class); 270 } 271 272 /** 273 * Add sample to a stat metric by name. 274 * @param name of the metric 275 * @param value of the snapshot to add 276 */ 277 public void add(String name, long value) { 278 MutableMetric m = metricsMap.get(name); 279 280 if (m != null) { 281 if (m instanceof MutableStat) { 282 ((MutableStat) m).add(value); 283 } else { 284 throw new MetricsException("Unsupported add(value) for metric " + name); 285 } 286 } else { 287 metricsMap.put(name, newRate(name)); // default is a rate metric 288 add(name, value); 289 } 290 } 291 292 /** 293 * Set the metrics context tag 294 * @param name of the context 295 * @return the registry itself as a convenience 296 */ 297 public DynamicMetricsRegistry setContext(String name) { 298 return tag(MsInfo.Context, name, true); 299 } 300 301 /** 302 * Add a tag to the metrics 303 * @param name of the tag 304 * @param description of the tag 305 * @param value of the tag 306 * @return the registry (for keep adding tags) 307 */ 308 public DynamicMetricsRegistry tag(String name, String description, String value) { 309 return tag(name, description, value, false); 310 } 311 312 /** 313 * Add a tag to the metrics 314 * @param name of the tag 315 * @param description of the tag 316 * @param value of the tag 317 * @param override existing tag if true 318 * @return the registry (for keep adding tags) 319 */ 320 public DynamicMetricsRegistry tag(String name, String description, String value, 321 boolean override) { 322 return tag(new MetricsInfoImpl(name, description), value, override); 323 } 324 325 /** 326 * Add a tag to the metrics 327 * @param info metadata of the tag 328 * @param value of the tag 329 * @param override existing tag if true 330 * @return the registry (for keep adding tags etc.) 331 */ 332 public DynamicMetricsRegistry tag(MetricsInfo info, String value, boolean override) { 333 MetricsTag tag = Interns.tag(info, value); 334 335 if (!override) { 336 MetricsTag existing = tagsMap.putIfAbsent(info.name(), tag); 337 if (existing != null) { 338 throw new MetricsException("Tag " + info.name() + " already exists!"); 339 } 340 return this; 341 } 342 343 tagsMap.put(info.name(), tag); 344 345 return this; 346 } 347 348 public DynamicMetricsRegistry tag(MetricsInfo info, String value) { 349 return tag(info, value, false); 350 } 351 352 Collection<MetricsTag> tags() { 353 return tagsMap.values(); 354 } 355 356 Collection<MutableMetric> metrics() { 357 return metricsMap.values(); 358 } 359 360 /** 361 * Sample all the mutable metrics and put the snapshot in the builder 362 * @param builder to contain the metrics snapshot 363 * @param all get all the metrics even if the values are not changed. 364 */ 365 public void snapshot(MetricsRecordBuilder builder, boolean all) { 366 for (MetricsTag tag : tags()) { 367 builder.add(tag); 368 } 369 for (MutableMetric metric : metrics()) { 370 metric.snapshot(builder, all); 371 } 372 } 373 374 @Override 375 public String toString() { 376 return MoreObjects.toStringHelper(this).add("info", metricsInfo).add("tags", tags()) 377 .add("metrics", metrics()).toString(); 378 } 379 380 /** 381 * Removes metric by name 382 * @param name name of the metric to remove 383 */ 384 public void removeMetric(String name) { 385 helper.removeObjectName(name); 386 metricsMap.remove(name); 387 } 388 389 public void removeHistogramMetrics(String baseName) { 390 for (String suffix : histogramSuffixes) { 391 removeMetric(baseName + suffix); 392 } 393 } 394 395 /** 396 * Get a MetricMutableGaugeLong from the storage. If it is not there atomically put it. 397 * @param gaugeName name of the gauge to create or get. 398 * @param potentialStartingValue value of the new gauge if we have to create it. 399 */ 400 public MutableGaugeLong getGauge(String gaugeName, long potentialStartingValue) { 401 // Try and get the guage. 402 MutableMetric metric = metricsMap.get(gaugeName); 403 404 // If it's not there then try and put a new one in the storage. 405 if (metric == null) { 406 407 // Create the potential new gauge. 408 MutableGaugeLong newGauge = 409 new MutableGaugeLong(new MetricsInfoImpl(gaugeName, ""), potentialStartingValue); 410 411 // Try and put the gauge in. This is atomic. 412 metric = metricsMap.putIfAbsent(gaugeName, newGauge); 413 414 // If the value we get back is null then the put was successful and we will return that. 415 // otherwise gaugeLong should contain the thing that was in before the put could be completed. 416 if (metric == null) { 417 return newGauge; 418 } 419 } 420 421 if (!(metric instanceof MutableGaugeLong)) { 422 throw new MetricsException("Metric already exists in registry for metric name: " + gaugeName 423 + " and not of type MetricMutableGaugeLong"); 424 } 425 426 return (MutableGaugeLong) metric; 427 } 428 429 /** 430 * Get a MetricMutableGaugeInt from the storage. If it is not there atomically put it. 431 * @param gaugeName name of the gauge to create or get. 432 * @param potentialStartingValue value of the new gauge if we have to create it. 433 */ 434 public MutableGaugeInt getGaugeInt(String gaugeName, int potentialStartingValue) { 435 // Try and get the guage. 436 MutableMetric metric = metricsMap.get(gaugeName); 437 438 // If it's not there then try and put a new one in the storage. 439 if (metric == null) { 440 // Create the potential new gauge. 441 MutableGaugeInt newGauge = 442 new MutableGaugeInt(new MetricsInfoImpl(gaugeName, ""), potentialStartingValue); 443 444 // Try and put the gauge in. This is atomic. 445 metric = metricsMap.putIfAbsent(gaugeName, newGauge); 446 447 // If the value we get back is null then the put was successful and we will return that. 448 // otherwise gaugeInt should contain the thing that was in before the put could be completed. 449 if (metric == null) { 450 return newGauge; 451 } 452 } 453 454 if (!(metric instanceof MutableGaugeInt)) { 455 throw new MetricsException("Metric already exists in registry for metric name: " + gaugeName 456 + " and not of type MetricMutableGaugeInr"); 457 } 458 459 return (MutableGaugeInt) metric; 460 } 461 462 /** 463 * Get a MetricMutableCounterLong from the storage. If it is not there atomically put it. 464 * @param counterName Name of the counter to get 465 * @param potentialStartingValue starting value if we have to create a new counter 466 */ 467 public MutableFastCounter getCounter(String counterName, long potentialStartingValue) { 468 // See getGauge for description on how this works. 469 MutableMetric counter = metricsMap.get(counterName); 470 if (counter == null) { 471 MutableFastCounter newCounter = 472 new MutableFastCounter(new MetricsInfoImpl(counterName, ""), potentialStartingValue); 473 counter = metricsMap.putIfAbsent(counterName, newCounter); 474 if (counter == null) { 475 return newCounter; 476 } 477 } 478 479 if (!(counter instanceof MutableCounter)) { 480 throw new MetricsException("Metric already exists in registry for metric name: " + counterName 481 + " and not of type MutableCounter"); 482 } 483 484 return (MutableFastCounter) counter; 485 } 486 487 public MutableHistogram getHistogram(String histoName) { 488 // See getGauge for description on how this works. 489 MutableMetric histo = metricsMap.get(histoName); 490 if (histo == null) { 491 MutableHistogram newCounter = new MutableHistogram(new MetricsInfoImpl(histoName, "")); 492 histo = metricsMap.putIfAbsent(histoName, newCounter); 493 if (histo == null) { 494 return newCounter; 495 } 496 } 497 498 if (!(histo instanceof MutableHistogram)) { 499 throw new MetricsException("Metric already exists in registry for metric name: " + histoName 500 + " and not of type MutableHistogram"); 501 } 502 503 return (MutableHistogram) histo; 504 } 505 506 private <T extends MutableMetric> T addNewMetricIfAbsent(String name, T ret, 507 Class<T> metricClass) { 508 // If the value we get back is null then the put was successful and we will 509 // return that. Otherwise metric should contain the thing that was in 510 // before the put could be completed. 511 MutableMetric metric = metricsMap.putIfAbsent(name, ret); 512 if (metric == null) { 513 return ret; 514 } 515 516 return returnExistingWithCast(metric, metricClass, name); 517 } 518 519 @SuppressWarnings("unchecked") 520 private <T> T returnExistingWithCast(MutableMetric metric, Class<T> metricClass, String name) { 521 if (!metricClass.isAssignableFrom(metric.getClass())) { 522 throw new MetricsException("Metric already exists in registry for metric name: " + name 523 + " and not of type " + metricClass + " but instead of type " + metric.getClass()); 524 } 525 526 return (T) metric; 527 } 528 529 public void clearMetrics() { 530 for (String name : metricsMap.keySet()) { 531 helper.removeObjectName(name); 532 } 533 metricsMap.clear(); 534 } 535}