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