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 synchronized void add(String name, MutableMetric metric) { 285 checkMetricName(name); 286 metricsMap.put(name, metric); 287 } 288 289 /** 290 * Add sample to a stat metric by name. 291 * @param name of the metric 292 * @param value of the snapshot to add 293 */ 294 public synchronized void add(String name, long value) { 295 MutableMetric m = metricsMap.get(name); 296 297 if (m != null) { 298 if (m instanceof MutableStat) { 299 ((MutableStat) m).add(value); 300 } 301 else { 302 throw new MetricsException("Unsupported add(value) for metric "+ name); 303 } 304 } 305 else { 306 metricsMap.put(name, newRate(name)); // default is a rate metric 307 add(name, value); 308 } 309 } 310 311 /** 312 * Set the metrics context tag 313 * @param name of the context 314 * @return the registry itself as a convenience 315 */ 316 public MetricsRegistry setContext(String name) { 317 return tag(MsInfo.Context, name, true); 318 } 319 320 /** 321 * Add a tag to the metrics 322 * @param name of the tag 323 * @param description of the tag 324 * @param value of the tag 325 * @return the registry (for keep adding tags) 326 */ 327 public MetricsRegistry tag(String name, String description, String value) { 328 return tag(name, description, value, false); 329 } 330 331 /** 332 * Add a tag to the metrics 333 * @param name of the tag 334 * @param description of the tag 335 * @param value of the tag 336 * @param override existing tag if true 337 * @return the registry (for keep adding tags) 338 */ 339 public MetricsRegistry tag(String name, String description, String value, 340 boolean override) { 341 return tag(Interns.info(name, description), value, override); 342 } 343 344 /** 345 * Add a tag to the metrics 346 * @param info metadata of the tag 347 * @param value of the tag 348 * @param override existing tag if true 349 * @return the registry (for keep adding tags etc.) 350 */ 351 public synchronized 352 MetricsRegistry tag(MetricsInfo info, String value, boolean override) { 353 if (!override) checkTagName(info.name()); 354 tagsMap.put(info.name(), Interns.tag(info, value)); 355 return this; 356 } 357 358 public MetricsRegistry tag(MetricsInfo info, String value) { 359 return tag(info, value, false); 360 } 361 362 Collection<MetricsTag> tags() { 363 return tagsMap.values(); 364 } 365 366 Collection<MutableMetric> metrics() { 367 return metricsMap.values(); 368 } 369 370 private void checkMetricName(String name) { 371 // Check for invalid characters in metric name 372 boolean foundWhitespace = false; 373 for (int i = 0; i < name.length(); i++) { 374 char c = name.charAt(i); 375 if (Character.isWhitespace(c)) { 376 foundWhitespace = true; 377 break; 378 } 379 } 380 if (foundWhitespace) { 381 throw new MetricsException("Metric name '"+ name + 382 "' contains illegal whitespace character"); 383 } 384 // Check if name has already been registered 385 if (metricsMap.containsKey(name)) { 386 throw new MetricsException("Metric name "+ name +" already exists!"); 387 } 388 } 389 390 private void checkTagName(String name) { 391 if (tagsMap.containsKey(name)) { 392 throw new MetricsException("Tag "+ name +" already exists!"); 393 } 394 } 395 396 /** 397 * Sample all the mutable metrics and put the snapshot in the builder 398 * @param builder to contain the metrics snapshot 399 * @param all get all the metrics even if the values are not changed. 400 */ 401 public synchronized void snapshot(MetricsRecordBuilder builder, boolean all) { 402 for (MetricsTag tag : tags()) { 403 builder.add(tag); 404 } 405 for (MutableMetric metric : metrics()) { 406 metric.snapshot(builder, all); 407 } 408 } 409 410 @Override public String toString() { 411 return Objects.toStringHelper(this) 412 .add("info", metricsInfo).add("tags", tags()).add("metrics", metrics()) 413 .toString(); 414 } 415}