1 /***********************************************************************
2  * Copyright (c) 2013-2024 Commonwealth Computer Research, Inc.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Apache License, Version 2.0
5  * which accompanies this distribution and is available at
6  * http://www.opensource.org/licenses/apache2.0.php.
7  ***********************************************************************/
8 
9 package org.locationtech.geomesa.metrics.core
10 
11 import com.codahale.metrics._
12 import com.typesafe.config.Config
13 import org.locationtech.geomesa.utils.io.CloseWithLogging
14 
15 import java.io.Closeable
16 
17 /**
18   * Provides namespaced access to reporting metrics
19   *
20   * @param registry metric registry
21   * @param prefix namespace prefix
22   * @param reporters metric reporters
23   */
24 class GeoMesaMetrics(val registry: MetricRegistry, prefix: String, reporters: Seq[ScheduledReporter])
25     extends Closeable {
26 
27   private val pre = GeoMesaMetrics.safePrefix(prefix)
28 
29   protected def id(typeName: String, id: String): String = s"$pre${GeoMesaMetrics.safePrefix(typeName)}$id"
30 
31   /**
32    * Creates a prefixed counter
33    *
34    * @param typeName simple feature type name
35    * @param id short identifier for the metric being counted
36    * @return
37    */
38   def counter(typeName: String, id: String): Counter = registry.counter(this.id(typeName, id))
39 
40   /**
41    * Gets a gauge. Note that it is possible (although unlikely) that the gauge will not be the
42    * one from the supplier, if the id has already been registered
43    *
44    * @param typeName simple feature type name
45    * @param id short identifier for the metric being gauged
46    * @param supplier metric supplier
47    * @return
48    */
49   def gauge(typeName: String, id: String, metric: => Gauge[_]): Gauge[_] = {
50     val ident = this.id(typeName, id)
51     // note: don't use MetricRegistry#gauge(String, MetricSupplier<Gauge>) to support older
52     // metric jars that ship with hbase
53     def getOrCreate(): Gauge[_] = {
54       registry.getMetrics.get(ident) match {
55         case g: Gauge[_] => g
56         case null => registry.register(ident, metric)
57         case m =>
58           throw new IllegalArgumentException(s"${m.getClass.getSimpleName} already registered under the name '$ident'")
59       }
60     }
61 
62     // re-try once to avoid concurrency issues with checking then adding a metric (which should be rare)
63     try { getOrCreate() } catch { case _: IllegalArgumentException => getOrCreate() }
64   }
65 
66   /**
67    * Creates a prefixed histogram
68    *
69    * @param typeName simple feature type name
70    * @param id short identifier for the metric being histogramed
71    * @return
72    */
73   def histogram(typeName: String, id: String): Histogram = registry.histogram(this.id(typeName, id))
74 
75   /**
76    * Creates a prefixed meter
77    *
78    * @param typeName simple feature type name
79    * @param id short identifier for the metric being metered
80    * @return
81    */
82   def meter(typeName: String, id: String): Meter = registry.meter(this.id(typeName, id))
83 
84   /**
85    * Creates a prefixed timer
86    *
87    * @param typeName simple feature type name
88    * @param id short identifier for the metric being timed
89    * @return
90    */
91   def timer(typeName: String, id: String): Timer = registry.timer(this.id(typeName, id))
92 
93   /**
94    * Register a metric. Note that in comparison to most methods in this class, a given identifier
95    * can only be registered once
96    *
97    * @param typeName simple feature type name
98    * @param id short identifier for the metric
99    * @param metric metric to register
100    * @tparam T metric type
101    * @return
102    */
103   def register[T <: Metric](typeName: String, id: String, metric: T): T =
104     registry.register(this.id(typeName, id), metric)
105 
106   override def close(): Unit = CloseWithLogging(reporters)
107 }
108 
109 object GeoMesaMetrics {
110 
111   /**
112     * Create a registry
113     *
114     * @param prefix metric name prefix
115     * @param reporters configs for metric reporters
116     * @return
117     */
118   def apply(prefix: String, reporters: Seq[Config]): GeoMesaMetrics = {
119     val registry = new MetricRegistry()
120     val reps = reporters.map(ReporterFactory.apply(_, registry)).toList
121     new GeoMesaMetrics(registry, prefix, reps)
122   }
123 
124   private def safePrefix(name: String): String = {
125     val replaced = name.replaceAll("[^A-Za-z0-9]", ".")
126     if (replaced.isEmpty || replaced.endsWith(".")) { replaced } else { s"$replaced." }
127   }
128 }
Line Stmt Id Pos Tree Symbol Tests Code
27 53111 1008 - 1014 Select org.locationtech.geomesa.metrics.core.GeoMesaMetrics.prefix GeoMesaMetrics.this.prefix
27 53112 982 - 1015 Apply org.locationtech.geomesa.metrics.core.GeoMesaMetrics.safePrefix GeoMesaMetrics.safePrefix(GeoMesaMetrics.this.prefix)
29 53113 1078 - 1079 Literal <nosymbol> ""
29 53114 1082 - 1083 Literal <nosymbol> ""
29 53115 1120 - 1121 Literal <nosymbol> ""
29 53116 1123 - 1124 Literal <nosymbol> ""
29 53117 1079 - 1082 Select org.locationtech.geomesa.metrics.core.GeoMesaMetrics.pre GeoMesaMetrics.this.pre
29 53118 1084 - 1119 Apply org.locationtech.geomesa.metrics.core.GeoMesaMetrics.safePrefix GeoMesaMetrics.safePrefix(typeName)
29 53119 1076 - 1124 Apply scala.StringContext.s scala.StringContext.apply("", "", "", "").s(GeoMesaMetrics.this.pre, GeoMesaMetrics.safePrefix(typeName), id)
38 53120 1367 - 1388 Apply org.locationtech.geomesa.metrics.core.GeoMesaMetrics.id this.id(typeName, id)
38 53121 1350 - 1389 Apply com.codahale.metrics.MetricRegistry.counter GeoMesaMetrics.this.registry.counter(this.id(typeName, id))
50 53122 1818 - 1839 Apply org.locationtech.geomesa.metrics.core.GeoMesaMetrics.id this.id(typeName, id)
54 53123 2014 - 2044 Apply java.util.Map.get GeoMesaMetrics.this.registry.getMetrics().get(ident)
55 53124 2081 - 2082 Ident org.locationtech.geomesa.metrics.core.GeoMesaMetrics.g g
56 53125 2104 - 2136 Apply com.codahale.metrics.MetricRegistry.register GeoMesaMetrics.this.registry.register[com.codahale.metrics.Gauge[_$1]](ident, metric)
56 53126 2104 - 2136 Block com.codahale.metrics.MetricRegistry.register GeoMesaMetrics.this.registry.register[com.codahale.metrics.Gauge[_$1]](ident, metric)
58 53127 2165 - 2274 Throw <nosymbol> throw new scala.`package`.IllegalArgumentException(scala.StringContext.apply("", " already registered under the name \'", "\'").s(m.getClass().getSimpleName(), ident))
58 53128 2165 - 2274 Block <nosymbol> throw new scala.`package`.IllegalArgumentException(scala.StringContext.apply("", " already registered under the name \'", "\'").s(m.getClass().getSimpleName(), ident))
63 53129 2405 - 2418 Apply org.locationtech.geomesa.metrics.core.GeoMesaMetrics.getOrCreate getOrCreate()
63 53130 2405 - 2418 Block org.locationtech.geomesa.metrics.core.GeoMesaMetrics.getOrCreate getOrCreate()
63 53131 2465 - 2478 Apply org.locationtech.geomesa.metrics.core.GeoMesaMetrics.getOrCreate getOrCreate()
63 53132 2465 - 2478 Block org.locationtech.geomesa.metrics.core.GeoMesaMetrics.getOrCreate getOrCreate()
73 53133 2739 - 2760 Apply org.locationtech.geomesa.metrics.core.GeoMesaMetrics.id this.id(typeName, id)
73 53134 2720 - 2761 Apply com.codahale.metrics.MetricRegistry.histogram GeoMesaMetrics.this.registry.histogram(this.id(typeName, id))
82 53135 2996 - 3017 Apply org.locationtech.geomesa.metrics.core.GeoMesaMetrics.id this.id(typeName, id)
82 53136 2981 - 3018 Apply com.codahale.metrics.MetricRegistry.meter GeoMesaMetrics.this.registry.meter(this.id(typeName, id))
91 53137 3251 - 3272 Apply org.locationtech.geomesa.metrics.core.GeoMesaMetrics.id this.id(typeName, id)
91 53138 3236 - 3273 Apply com.codahale.metrics.MetricRegistry.timer GeoMesaMetrics.this.registry.timer(this.id(typeName, id))
104 53139 3690 - 3711 Apply org.locationtech.geomesa.metrics.core.GeoMesaMetrics.id this.id(typeName, id)
104 53140 3672 - 3720 Apply com.codahale.metrics.MetricRegistry.register GeoMesaMetrics.this.registry.register[T](this.id(typeName, id), metric)
106 53141 3770 - 3779 Select org.locationtech.geomesa.metrics.core.GeoMesaMetrics.reporters GeoMesaMetrics.this.reporters
106 53142 3769 - 3769 Select org.locationtech.geomesa.utils.io.IsCloseableImplicits.iterableIsCloseable io.this.IsCloseable.iterableIsCloseable
106 53143 3753 - 3780 ApplyToImplicitArgs org.locationtech.geomesa.utils.io.CloseWithLogging.apply org.locationtech.geomesa.utils.io.`package`.CloseWithLogging.apply[Seq[com.codahale.metrics.ScheduledReporter]](GeoMesaMetrics.this.reporters)(io.this.IsCloseable.iterableIsCloseable)
106 53144 3769 - 3769 Literal <nosymbol> ()
119 53145 4048 - 4068 Apply com.codahale.metrics.MetricRegistry.<init> new com.codahale.metrics.MetricRegistry()
120 53146 4098 - 4132 Apply org.locationtech.geomesa.metrics.core.ReporterFactory.apply ReporterFactory.apply(x$1, registry)
120 53147 4097 - 4097 TypeApply scala.collection.Seq.canBuildFrom collection.this.Seq.canBuildFrom[com.codahale.metrics.ScheduledReporter]
120 53148 4084 - 4140 Select scala.collection.TraversableOnce.toList reporters.map[com.codahale.metrics.ScheduledReporter, Seq[com.codahale.metrics.ScheduledReporter]](((x$1: com.typesafe.config.Config) => ReporterFactory.apply(x$1, registry)))(collection.this.Seq.canBuildFrom[com.codahale.metrics.ScheduledReporter]).toList
121 53149 4145 - 4187 Apply org.locationtech.geomesa.metrics.core.GeoMesaMetrics.<init> new GeoMesaMetrics(registry, prefix, reps)
125 53150 4263 - 4299 Apply java.lang.String.replaceAll name.replaceAll("[^A-Za-z0-9]", ".")
126 53151 4328 - 4350 Apply java.lang.String.endsWith replaced.endsWith(".")
126 53152 4308 - 4350 Apply scala.Boolean.|| replaced.isEmpty().||(replaced.endsWith("."))
126 53153 4354 - 4362 Ident org.locationtech.geomesa.metrics.core.GeoMesaMetrics.replaced replaced
126 53154 4372 - 4385 Apply scala.StringContext.s scala.StringContext.apply("", ".").s(replaced)
126 53155 4372 - 4385 Block scala.StringContext.s scala.StringContext.apply("", ".").s(replaced)