diff --git a/solr/core/src/java/org/apache/solr/servlet/PrometheusMetricsServlet.java b/solr/core/src/java/org/apache/solr/servlet/PrometheusMetricsServlet.java index 1240822e568..98f1dbf95c2 100644 --- a/solr/core/src/java/org/apache/solr/servlet/PrometheusMetricsServlet.java +++ b/solr/core/src/java/org/apache/solr/servlet/PrometheusMetricsServlet.java @@ -25,12 +25,14 @@ import java.lang.invoke.MethodHandles; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; +import java.util.AbstractMap.SimpleImmutableEntry; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; +import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; @@ -72,7 +74,8 @@ public final class PrometheusMetricsServlet extends BaseSolrServlet { new OsMetricsApiCaller(), new ThreadMetricsApiCaller(), new StatusCodeMetricsApiCaller(), - new CoresMetricsApiCaller())); + new CoresMetricsApiCaller(), + new NodeCacheMetricsApiCaller())); private final Map cacheMetricTypes = Map.of( @@ -82,6 +85,19 @@ public final class PrometheusMetricsServlet extends BaseSolrServlet { "puts", PrometheusMetricType.COUNTER, "evictions", PrometheusMetricType.COUNTER); + /** + * node-level caches use slightly different terminology for some fields, so we map to old + * terminology for consistency. + */ + private static final Map> + nodeLevelCacheMetricTypes = + Map.of( + "ramBytesUsed", new SimpleImmutableEntry<>("bytesUsed", PrometheusMetricType.GAUGE), + "lookups", new SimpleImmutableEntry<>("lookups", PrometheusMetricType.COUNTER), + "hits", new SimpleImmutableEntry<>("hits", PrometheusMetricType.COUNTER), + "inserts", new SimpleImmutableEntry<>("puts", PrometheusMetricType.COUNTER), + "evictions", new SimpleImmutableEntry<>("evictions", PrometheusMetricType.COUNTER)); + @Override public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, UnavailableException { @@ -143,6 +159,45 @@ static void getSharedCacheMetrics( } } + static class NodeCacheMetricsApiCaller extends MetricsApiCaller { + private static final String PREFIX = "CACHE.nodeLevelCache/"; + + NodeCacheMetricsApiCaller() { + super("node", PREFIX, ""); + } + + @Override + protected void handle(List results, JsonNode metrics) throws IOException { + JsonNode parent = metrics.path("solr.node"); + Iterator> caches = parent.fields(); + while (caches.hasNext()) { + Map.Entry cacheEntry = caches.next(); + String cacheName = cacheEntry.getKey().substring(PREFIX.length()).toLowerCase(Locale.ROOT); + Iterator> fields = cacheEntry.getValue().fields(); + while (fields.hasNext()) { + Map.Entry next = fields.next(); + Map.Entry typeEntry = + nodeLevelCacheMetricTypes.get(next.getKey()); + if (typeEntry != null) { + String fieldName = typeEntry.getKey(); + PrometheusMetricType type = typeEntry.getValue(); + results.add( + new PrometheusMetric( + String.format(Locale.ROOT, "cache_%s_%s", cacheName, fieldName), + type, + String.format( + Locale.ROOT, + "%s %s for cache %s", + fieldName, + type.getDisplayName(), + cacheName), + next.getValue().numberValue())); + } + } + } + } + } + static class GarbageCollectorMetricsApiCaller extends MetricsApiCaller { GarbageCollectorMetricsApiCaller() { diff --git a/solr/core/src/test/org/apache/solr/servlet/PrometheusMetricsServletTest.java b/solr/core/src/test/org/apache/solr/servlet/PrometheusMetricsServletTest.java index 57a029bb976..0bf4ffe520a 100644 --- a/solr/core/src/test/org/apache/solr/servlet/PrometheusMetricsServletTest.java +++ b/solr/core/src/test/org/apache/solr/servlet/PrometheusMetricsServletTest.java @@ -478,4 +478,48 @@ public void testCoresMetricsApiCallerMissingIndex() throws Exception { + "deletes_by_query 66\n"; assertMetricsApiCaller(new PrometheusMetricsServlet.CoresMetricsApiCaller(), json, 25, output); } + + @Test + public void testNodeCacheMetricsApiCaller() throws Exception { + String json = + "{\n" + + " \"responseHeader\":{\n" + + " \"status\":0,\n" + + " \"QTime\":25},\n" + + " \"metrics\":{\n" + + " \"solr.node\":{\n" + + " \"CACHE.nodeLevelCache/myCache\":{\n" + + " \"lookups\":2,\n" + + " \"hits\":1,\n" + + " \"hitratio\":0.5,\n" + + " \"inserts\":1,\n" + + " \"evictions\":0,\n" + + " \"size\":1,\n" + + " \"warmupTime\":0,\n" + + " \"ramBytesUsed\":623,\n" + + " \"maxRamMB\":-1,\n" + + " \"cumulative_lookups\":2,\n" + + " \"cumulative_hits\":1,\n" + + " \"cumulative_hitratio\":0.5,\n" + + " \"cumulative_inserts\":1,\n" + + " \"cumulative_evictions\":0}}}}"; + String output = + "# HELP cache_mycache_lookups lookups counter for cache mycache\n" + + "# TYPE cache_mycache_lookups counter\n" + + "cache_mycache_lookups 2\n" + + "# HELP cache_mycache_hits hits counter for cache mycache\n" + + "# TYPE cache_mycache_hits counter\n" + + "cache_mycache_hits 1\n" + + "# HELP cache_mycache_puts puts counter for cache mycache\n" + + "# TYPE cache_mycache_puts counter\n" + + "cache_mycache_puts 1\n" + + "# HELP cache_mycache_evictions evictions counter for cache mycache\n" + + "# TYPE cache_mycache_evictions counter\n" + + "cache_mycache_evictions 0\n" + + "# HELP cache_mycache_bytes_used bytesUsed gauge for cache mycache\n" + + "# TYPE cache_mycache_bytes_used gauge\n" + + "cache_mycache_bytes_used 623\n"; + assertMetricsApiCaller( + new PrometheusMetricsServlet.NodeCacheMetricsApiCaller(), json, 25, output); + } }