/metrics-core/src/main/java/com/yammer/metrics/core/MetricsRegistry.java

https://github.com/jmhodges/metrics · Java · 519 lines · 223 code · 42 blank · 254 comment · 21 complexity · 5dde1c1346852b79278aaa0a7f81d29b MD5 · raw file

  1. package com.yammer.metrics.core;
  2. import com.yammer.metrics.core.Histogram.SampleType;
  3. import java.util.*;
  4. import java.util.concurrent.*;
  5. /**
  6. * A registry of metric instances.
  7. */
  8. public class MetricsRegistry {
  9. private static final int EXPECTED_METRIC_COUNT = 1024;
  10. private final Clock clock;
  11. private final ConcurrentMap<MetricName, Metric> metrics;
  12. private final ThreadPools threadPools;
  13. private final List<MetricsRegistryListener> listeners;
  14. /**
  15. * Creates a new {@link MetricsRegistry}.
  16. */
  17. public MetricsRegistry() {
  18. this(Clock.defaultClock());
  19. }
  20. /**
  21. * Creates a new {@link MetricsRegistry} with the given {@link Clock} instance.
  22. *
  23. * @param clock a {@link Clock} instance
  24. */
  25. public MetricsRegistry(Clock clock) {
  26. this.clock = clock;
  27. this.metrics = newMetricsMap();
  28. this.threadPools = new ThreadPools();
  29. this.listeners = new CopyOnWriteArrayList<MetricsRegistryListener>();
  30. }
  31. /**
  32. * Given a new {@link Gauge}, registers it under the given class and name.
  33. *
  34. * @param klass the class which owns the metric
  35. * @param name the name of the metric
  36. * @param metric the metric
  37. * @param <T> the type of the value returned by the metric
  38. * @return {@code metric}
  39. */
  40. public <T> Gauge<T> newGauge(Class<?> klass,
  41. String name,
  42. Gauge<T> metric) {
  43. return newGauge(klass, name, null, metric);
  44. }
  45. /**
  46. * Given a new {@link Gauge}, registers it under the given class and name.
  47. *
  48. * @param klass the class which owns the metric
  49. * @param name the name of the metric
  50. * @param scope the scope of the metric
  51. * @param metric the metric
  52. * @param <T> the type of the value returned by the metric
  53. * @return {@code metric}
  54. */
  55. public <T> Gauge<T> newGauge(Class<?> klass,
  56. String name,
  57. String scope,
  58. Gauge<T> metric) {
  59. return newGauge(createName(klass, name, scope), metric);
  60. }
  61. /**
  62. * Given a new {@link Gauge}, registers it under the given metric name.
  63. *
  64. * @param metricName the name of the metric
  65. * @param metric the metric
  66. * @param <T> the type of the value returned by the metric
  67. * @return {@code metric}
  68. */
  69. public <T> Gauge<T> newGauge(MetricName metricName,
  70. Gauge<T> metric) {
  71. return getOrAdd(metricName, metric);
  72. }
  73. /**
  74. * Creates a new {@link Counter} and registers it under the given class and name.
  75. *
  76. * @param klass the class which owns the metric
  77. * @param name the name of the metric
  78. * @return a new {@link Counter}
  79. */
  80. public Counter newCounter(Class<?> klass,
  81. String name) {
  82. return newCounter(klass, name, null);
  83. }
  84. /**
  85. * Creates a new {@link Counter} and registers it under the given class and name.
  86. *
  87. * @param klass the class which owns the metric
  88. * @param name the name of the metric
  89. * @param scope the scope of the metric
  90. * @return a new {@link Counter}
  91. */
  92. public Counter newCounter(Class<?> klass,
  93. String name,
  94. String scope) {
  95. return newCounter(createName(klass, name, scope));
  96. }
  97. /**
  98. * Creates a new {@link Counter} and registers it under the given metric name.
  99. *
  100. * @param metricName the name of the metric
  101. * @return a new {@link Counter}
  102. */
  103. public Counter newCounter(MetricName metricName) {
  104. return getOrAdd(metricName, new Counter());
  105. }
  106. /**
  107. * Creates a new {@link Histogram} and registers it under the given class and name.
  108. *
  109. * @param klass the class which owns the metric
  110. * @param name the name of the metric
  111. * @param biased whether or not the histogram should be biased
  112. * @return a new {@link Histogram}
  113. */
  114. public Histogram newHistogram(Class<?> klass,
  115. String name,
  116. boolean biased) {
  117. return newHistogram(klass, name, null, biased);
  118. }
  119. /**
  120. * Creates a new {@link Histogram} and registers it under the given class, name, and scope.
  121. *
  122. * @param klass the class which owns the metric
  123. * @param name the name of the metric
  124. * @param scope the scope of the metric
  125. * @param biased whether or not the histogram should be biased
  126. * @return a new {@link Histogram}
  127. */
  128. public Histogram newHistogram(Class<?> klass,
  129. String name,
  130. String scope,
  131. boolean biased) {
  132. return newHistogram(createName(klass, name, scope), biased);
  133. }
  134. /**
  135. * Creates a new non-biased {@link Histogram} and registers it under the given class and name.
  136. *
  137. * @param klass the class which owns the metric
  138. * @param name the name of the metric
  139. * @return a new {@link Histogram}
  140. */
  141. public Histogram newHistogram(Class<?> klass,
  142. String name) {
  143. return newHistogram(klass, name, false);
  144. }
  145. /**
  146. * Creates a new non-biased {@link Histogram} and registers it under the given class, name, and
  147. * scope.
  148. *
  149. * @param klass the class which owns the metric
  150. * @param name the name of the metric
  151. * @param scope the scope of the metric
  152. * @return a new {@link Histogram}
  153. */
  154. public Histogram newHistogram(Class<?> klass,
  155. String name,
  156. String scope) {
  157. return newHistogram(klass, name, scope, false);
  158. }
  159. /**
  160. * Creates a new {@link Histogram} and registers it under the given metric name.
  161. *
  162. * @param metricName the name of the metric
  163. * @param biased whether or not the histogram should be biased
  164. * @return a new {@link Histogram}
  165. */
  166. public Histogram newHistogram(MetricName metricName,
  167. boolean biased) {
  168. return getOrAdd(metricName,
  169. new Histogram(biased ? SampleType.BIASED : SampleType.UNIFORM));
  170. }
  171. /**
  172. * Creates a new {@link Meter} and registers it under the given class and name.
  173. *
  174. * @param klass the class which owns the metric
  175. * @param name the name of the metric
  176. * @param eventType the plural name of the type of events the meter is measuring (e.g., {@code
  177. * "requests"})
  178. * @param unit the rate unit of the new meter
  179. * @return a new {@link Meter}
  180. */
  181. public Meter newMeter(Class<?> klass,
  182. String name,
  183. String eventType,
  184. TimeUnit unit) {
  185. return newMeter(klass, name, null, eventType, unit);
  186. }
  187. /**
  188. * Creates a new {@link Meter} and registers it under the given class, name, and scope.
  189. *
  190. * @param klass the class which owns the metric
  191. * @param name the name of the metric
  192. * @param scope the scope of the metric
  193. * @param eventType the plural name of the type of events the meter is measuring (e.g., {@code
  194. * "requests"})
  195. * @param unit the rate unit of the new meter
  196. * @return a new {@link Meter}
  197. */
  198. public Meter newMeter(Class<?> klass,
  199. String name,
  200. String scope,
  201. String eventType,
  202. TimeUnit unit) {
  203. return newMeter(createName(klass, name, scope), eventType, unit);
  204. }
  205. /**
  206. * Creates a new {@link Meter} and registers it under the given metric name.
  207. *
  208. * @param metricName the name of the metric
  209. * @param eventType the plural name of the type of events the meter is measuring (e.g., {@code
  210. * "requests"})
  211. * @param unit the rate unit of the new meter
  212. * @return a new {@link Meter}
  213. */
  214. public Meter newMeter(MetricName metricName,
  215. String eventType,
  216. TimeUnit unit) {
  217. final Metric existingMetric = metrics.get(metricName);
  218. if (existingMetric != null) {
  219. return (Meter) existingMetric;
  220. }
  221. return getOrAdd(metricName, new Meter(newMeterTickThreadPool(), eventType, unit, clock));
  222. }
  223. /**
  224. * Creates a new {@link Timer} and registers it under the given class and name, measuring
  225. * elapsed time in milliseconds and invocations per second.
  226. *
  227. * @param klass the class which owns the metric
  228. * @param name the name of the metric
  229. * @return a new {@link Timer}
  230. */
  231. public Timer newTimer(Class<?> klass,
  232. String name) {
  233. return newTimer(klass, name, null, TimeUnit.MILLISECONDS, TimeUnit.SECONDS);
  234. }
  235. /**
  236. * Creates a new {@link Timer} and registers it under the given class and name.
  237. *
  238. * @param klass the class which owns the metric
  239. * @param name the name of the metric
  240. * @param durationUnit the duration scale unit of the new timer
  241. * @param rateUnit the rate scale unit of the new timer
  242. * @return a new {@link Timer}
  243. */
  244. public Timer newTimer(Class<?> klass,
  245. String name,
  246. TimeUnit durationUnit,
  247. TimeUnit rateUnit) {
  248. return newTimer(klass, name, null, durationUnit, rateUnit);
  249. }
  250. /**
  251. * Creates a new {@link Timer} and registers it under the given class, name, and scope,
  252. * measuring elapsed time in milliseconds and invocations per second.
  253. *
  254. * @param klass the class which owns the metric
  255. * @param name the name of the metric
  256. * @param scope the scope of the metric
  257. * @return a new {@link Timer}
  258. */
  259. public Timer newTimer(Class<?> klass,
  260. String name,
  261. String scope) {
  262. return newTimer(klass, name, scope, TimeUnit.MILLISECONDS, TimeUnit.SECONDS);
  263. }
  264. /**
  265. * Creates a new {@link Timer} and registers it under the given class, name, and scope.
  266. *
  267. * @param klass the class which owns the metric
  268. * @param name the name of the metric
  269. * @param scope the scope of the metric
  270. * @param durationUnit the duration scale unit of the new timer
  271. * @param rateUnit the rate scale unit of the new timer
  272. * @return a new {@link Timer}
  273. */
  274. public Timer newTimer(Class<?> klass,
  275. String name,
  276. String scope,
  277. TimeUnit durationUnit,
  278. TimeUnit rateUnit) {
  279. return newTimer(createName(klass, name, scope), durationUnit, rateUnit);
  280. }
  281. /**
  282. * Creates a new {@link Timer} and registers it under the given metric name.
  283. *
  284. * @param metricName the name of the metric
  285. * @param durationUnit the duration scale unit of the new timer
  286. * @param rateUnit the rate scale unit of the new timer
  287. * @return a new {@link Timer}
  288. */
  289. public Timer newTimer(MetricName metricName,
  290. TimeUnit durationUnit,
  291. TimeUnit rateUnit) {
  292. final Metric existingMetric = metrics.get(metricName);
  293. if (existingMetric != null) {
  294. return (Timer) existingMetric;
  295. }
  296. return getOrAdd(metricName,
  297. new Timer(newMeterTickThreadPool(), durationUnit, rateUnit, clock));
  298. }
  299. /**
  300. * Returns an unmodifiable map of all metrics and their names.
  301. *
  302. * @return an unmodifiable map of all metrics and their names
  303. */
  304. public Map<MetricName, Metric> getAllMetrics() {
  305. return Collections.unmodifiableMap(metrics);
  306. }
  307. /**
  308. * Returns a grouped and sorted map of all registered metrics.
  309. *
  310. * @return all registered metrics, grouped by name and sorted
  311. */
  312. public SortedMap<String, SortedMap<MetricName, Metric>> getGroupedMetrics() {
  313. return getGroupedMetrics(MetricPredicate.ALL);
  314. }
  315. /**
  316. * Returns a grouped and sorted map of all registered metrics which match then given {@link
  317. * MetricPredicate}.
  318. *
  319. * @param predicate a predicate which metrics have to match to be in the results
  320. * @return all registered metrics which match {@code predicate}, sorted by name
  321. */
  322. public SortedMap<String, SortedMap<MetricName, Metric>> getGroupedMetrics(MetricPredicate predicate) {
  323. final SortedMap<String, SortedMap<MetricName, Metric>> groups =
  324. new TreeMap<String, SortedMap<MetricName, Metric>>();
  325. for (Map.Entry<MetricName, Metric> entry : metrics.entrySet()) {
  326. final String qualifiedTypeName = entry.getKey().getGroup() + "." + entry.getKey()
  327. .getType();
  328. if (predicate.matches(entry.getKey(), entry.getValue())) {
  329. final String scopedName;
  330. if (entry.getKey().hasScope()) {
  331. scopedName = qualifiedTypeName + "." + entry.getKey().getScope();
  332. } else {
  333. scopedName = qualifiedTypeName;
  334. }
  335. SortedMap<MetricName, Metric> group = groups.get(scopedName);
  336. if (group == null) {
  337. group = new TreeMap<MetricName, Metric>();
  338. groups.put(scopedName, group);
  339. }
  340. group.put(entry.getKey(), entry.getValue());
  341. }
  342. }
  343. return Collections.unmodifiableSortedMap(groups);
  344. }
  345. /**
  346. * Shut down this registry's thread pools.
  347. */
  348. public void shutdown() {
  349. threadPools.shutdown();
  350. }
  351. /**
  352. * Creates a new scheduled thread pool of a given size with the given name, or returns an
  353. * existing thread pool if one was already created with the same name.
  354. *
  355. * @param poolSize the number of threads to create
  356. * @param name the name of the pool
  357. * @return a new {@link ScheduledExecutorService}
  358. */
  359. public ScheduledExecutorService newScheduledThreadPool(int poolSize, String name) {
  360. return threadPools.newScheduledThreadPool(poolSize, name);
  361. }
  362. /**
  363. * Removes the metric for the given class with the given name.
  364. *
  365. * @param klass the klass the metric is associated with
  366. * @param name the name of the metric
  367. */
  368. public void removeMetric(Class<?> klass,
  369. String name) {
  370. removeMetric(klass, name, null);
  371. }
  372. /**
  373. * Removes the metric for the given class with the given name and scope.
  374. *
  375. * @param klass the klass the metric is associated with
  376. * @param name the name of the metric
  377. * @param scope the scope of the metric
  378. */
  379. public void removeMetric(Class<?> klass,
  380. String name,
  381. String scope) {
  382. removeMetric(createName(klass, name, scope));
  383. }
  384. /**
  385. * Removes the metric with the given name.
  386. *
  387. * @param name the name of the metric
  388. */
  389. public void removeMetric(MetricName name) {
  390. final Metric metric = metrics.remove(name);
  391. if (metric != null) {
  392. if (metric instanceof Stoppable) {
  393. ((Stoppable) metric).stop();
  394. }
  395. notifyMetricRemoved(name);
  396. }
  397. }
  398. /**
  399. * Adds a {@link MetricsRegistryListener} to a collection of listeners that will be notified on
  400. * metric creation. Listeners will be notified in the order in which they are added.
  401. * <p/>
  402. * <b>N.B.:</b> The listener will be notified of all existing metrics when it first registers.
  403. *
  404. * @param listener the listener that will be notified
  405. */
  406. public void addListener(MetricsRegistryListener listener) {
  407. listeners.add(listener);
  408. for (Map.Entry<MetricName, Metric> entry : metrics.entrySet()) {
  409. listener.onMetricAdded(entry.getKey(), entry.getValue());
  410. }
  411. }
  412. /**
  413. * Removes a {@link MetricsRegistryListener} from this registry's collection of listeners.
  414. *
  415. * @param listener the listener that will be removed
  416. */
  417. public void removeListener(MetricsRegistryListener listener) {
  418. listeners.remove(listener);
  419. }
  420. /**
  421. * Override to customize how {@link MetricName}s are created.
  422. *
  423. * @param klass the class which owns the metric
  424. * @param name the name of the metric
  425. * @param scope the metric's scope
  426. * @return the metric's full name
  427. */
  428. protected MetricName createName(Class<?> klass, String name, String scope) {
  429. return new MetricName(klass, name, scope);
  430. }
  431. /**
  432. * Returns a new {@link ConcurrentMap} implementation. Subclass this to do weird things with
  433. * your own {@link MetricsRegistry} implementation.
  434. *
  435. * @return a new {@link ConcurrentMap}
  436. */
  437. protected ConcurrentMap<MetricName, Metric> newMetricsMap() {
  438. return new ConcurrentHashMap<MetricName, Metric>(EXPECTED_METRIC_COUNT);
  439. }
  440. /**
  441. * Gets any existing metric with the given name or, if none exists, adds the given metric.
  442. *
  443. * @param name the metric's name
  444. * @param metric the new metric
  445. * @param <T> the type of the metric
  446. * @return either the existing metric or {@code metric}
  447. */
  448. @SuppressWarnings("unchecked")
  449. protected final <T extends Metric> T getOrAdd(MetricName name, T metric) {
  450. final Metric existingMetric = metrics.get(name);
  451. if (existingMetric == null) {
  452. final Metric justAddedMetric = metrics.putIfAbsent(name, metric);
  453. if (justAddedMetric == null) {
  454. notifyMetricAdded(name, metric);
  455. return metric;
  456. }
  457. if (metric instanceof Stoppable) {
  458. ((Stoppable) metric).stop();
  459. }
  460. return (T) justAddedMetric;
  461. }
  462. return (T) existingMetric;
  463. }
  464. private ScheduledExecutorService newMeterTickThreadPool() {
  465. return threadPools.newScheduledThreadPool(2, "meter-tick");
  466. }
  467. private void notifyMetricRemoved(MetricName name) {
  468. for (MetricsRegistryListener listener : listeners) {
  469. listener.onMetricRemoved(name);
  470. }
  471. }
  472. private void notifyMetricAdded(MetricName name, Metric metric) {
  473. for (MetricsRegistryListener listener : listeners) {
  474. listener.onMetricAdded(name, metric);
  475. }
  476. }
  477. }