mlbiscoc commented on code in PR #3482:
URL: https://github.com/apache/solr/pull/3482#discussion_r2379847012


##########
solr/core/src/java/org/apache/solr/search/SolrIndexSearcher.java:
##########
@@ -2605,95 +2636,152 @@ public SolrMetricsContext getSolrMetricsContext() {
     return solrMetricsContext;
   }
 
-  // TODO SOLR-17458: Migrate to Otel
   @Override
   public void initializeMetrics(
-      SolrMetricsContext parentContext, Attributes attributes, String scope) {
-    parentContext.gauge(() -> name, true, "searcherName", 
Category.SEARCHER.toString(), scope);
-    parentContext.gauge(() -> cachingEnabled, true, "caching", 
Category.SEARCHER.toString(), scope);
-    parentContext.gauge(() -> openTime, true, "openedAt", 
Category.SEARCHER.toString(), scope);
-    parentContext.gauge(() -> warmupTime, true, "warmupTime", 
Category.SEARCHER.toString(), scope);
-    parentContext.gauge(
-        () -> registerTime, true, "registeredAt", 
Category.SEARCHER.toString(), scope);
-    parentContext.gauge(
-        fullSortCount::sum, true, "fullSortCount", 
Category.SEARCHER.toString(), scope);
-    parentContext.gauge(
-        skipSortCount::sum, true, "skipSortCount", 
Category.SEARCHER.toString(), scope);
-    final MetricsMap liveDocsCacheMetrics =
-        new MetricsMap(
-            (map) -> {
-              map.put("inserts", liveDocsInsertsCount.sum());
-              map.put("hits", liveDocsHitCount.sum());
-              map.put("naiveHits", liveDocsNaiveCacheHitCount.sum());
-            });
-    parentContext.gauge(
-        liveDocsCacheMetrics, true, "liveDocsCache", 
Category.SEARCHER.toString(), scope);
-    // reader stats
-    parentContext.gauge(
-        rgauge(parentContext.nullNumber(), () -> reader.numDocs()),
-        true,
-        "numDocs",
-        Category.SEARCHER.toString(),
-        scope);
-    parentContext.gauge(
-        rgauge(parentContext.nullNumber(), () -> reader.maxDoc()),
-        true,
-        "maxDoc",
-        Category.SEARCHER.toString(),
-        scope);
-    parentContext.gauge(
-        rgauge(parentContext.nullNumber(), () -> reader.maxDoc() - 
reader.numDocs()),
-        true,
-        "deletedDocs",
-        Category.SEARCHER.toString(),
-        scope);
-    parentContext.gauge(
-        rgauge(parentContext.nullString(), () -> reader.toString()),
-        true,
-        "reader",
-        Category.SEARCHER.toString(),
-        scope);
-    parentContext.gauge(
-        rgauge(parentContext.nullString(), () -> 
reader.directory().toString()),
-        true,
-        "readerDir",
-        Category.SEARCHER.toString(),
-        scope);
-    parentContext.gauge(
-        rgauge(parentContext.nullNumber(), () -> reader.getVersion()),
-        true,
-        "indexVersion",
-        Category.SEARCHER.toString(),
-        scope);
+      SolrMetricsContext solrMetricsContext, Attributes attributes, String 
scope) {
+    var baseAttributes =
+        attributes.toBuilder().put(CATEGORY_ATTR, 
Category.SEARCHER.toString()).build();
+    // caching (boolean -> 0/1) - synchronous gauge
+    cachingEnabledGauge =
+        new AttributedLongGauge(
+            solrMetricsContext.longGauge("solr_searcher_caching_enabled", "1 
if caching enabled"),
+            baseAttributes);
+    cachingEnabledGauge.set(cachingEnabled ? 1L : 0L);
+
+    // warmupTime (ms) - timer for histogram tracking
+    warmupTimer =
+        new AttributedLongTimer(
+            solrMetricsContext.longHistogram(
+                "solr_searcher_warmup_time", "Searcher warmup time (ms)", 
OtelUnit.MILLISECONDS),
+            baseAttributes);
+
+    // fullSortCount
+    toClose.add(
+        solrMetricsContext.observableLongCounter(
+            "solr_searcher_full_sort",
+            "Number of queries that required building a full DocList with 
sorting",
+            obs -> obs.record(fullSortCount.sum(), baseAttributes)));
+
+    // skipSortCount
+    toClose.add(
+        solrMetricsContext.observableLongCounter(
+            "solr_searcher_skip_sort",
+            "Number of queries where Solr skipped building a full sorted 
DocList and instead returned results directly from the DocSet",
+            obs -> obs.record(skipSortCount.sum(), baseAttributes)));
+    toClose.add(
+        solrMetricsContext.observableLongCounter(
+            "solr_searcher_live_docs_cache",
+            "LiveDocs cache metrics",
+            obs -> {
+              obs.record(
+                  liveDocsInsertsCount.sum(),
+                  baseAttributes.toBuilder().put(TYPE_ATTR, 
"inserts").build());
+              obs.record(
+                  liveDocsHitCount.sum(),
+                  baseAttributes.toBuilder().put(TYPE_ATTR, "hits").build());
+              obs.record(
+                  liveDocsNaiveCacheHitCount.sum(),
+                  baseAttributes.toBuilder().put(TYPE_ATTR, 
"naive_hits").build());
+            }));
+    // reader stats (numeric)
+    toClose.add(
+        solrMetricsContext.observableLongGauge(
+            "solr_searcher_num_docs",
+            "Number of live docs",
+            obs -> {
+              try {
+                obs.record(reader.numDocs(), baseAttributes);
+              } catch (Exception ignore) {
+                // replacement for nullNumber
+              }
+            }));
+
+    toClose.add(
+        solrMetricsContext.observableLongGauge(
+            "solr_searcher_max_doc",
+            "Max doc (including deletions)",
+            obs -> {
+              try {
+                obs.record(reader.maxDoc(), baseAttributes);
+              } catch (Exception ignore) {
+              }
+            }));
+
+    toClose.add(
+        solrMetricsContext.observableLongGauge(
+            "solr_searcher_deleted_docs",

Review Comment:
   Dropped deleted



##########
solr/core/src/java/org/apache/solr/search/SolrIndexSearcher.java:
##########
@@ -2605,95 +2636,152 @@ public SolrMetricsContext getSolrMetricsContext() {
     return solrMetricsContext;
   }
 
-  // TODO SOLR-17458: Migrate to Otel
   @Override
   public void initializeMetrics(
-      SolrMetricsContext parentContext, Attributes attributes, String scope) {
-    parentContext.gauge(() -> name, true, "searcherName", 
Category.SEARCHER.toString(), scope);
-    parentContext.gauge(() -> cachingEnabled, true, "caching", 
Category.SEARCHER.toString(), scope);
-    parentContext.gauge(() -> openTime, true, "openedAt", 
Category.SEARCHER.toString(), scope);
-    parentContext.gauge(() -> warmupTime, true, "warmupTime", 
Category.SEARCHER.toString(), scope);
-    parentContext.gauge(
-        () -> registerTime, true, "registeredAt", 
Category.SEARCHER.toString(), scope);
-    parentContext.gauge(
-        fullSortCount::sum, true, "fullSortCount", 
Category.SEARCHER.toString(), scope);
-    parentContext.gauge(
-        skipSortCount::sum, true, "skipSortCount", 
Category.SEARCHER.toString(), scope);
-    final MetricsMap liveDocsCacheMetrics =
-        new MetricsMap(
-            (map) -> {
-              map.put("inserts", liveDocsInsertsCount.sum());
-              map.put("hits", liveDocsHitCount.sum());
-              map.put("naiveHits", liveDocsNaiveCacheHitCount.sum());
-            });
-    parentContext.gauge(
-        liveDocsCacheMetrics, true, "liveDocsCache", 
Category.SEARCHER.toString(), scope);
-    // reader stats
-    parentContext.gauge(
-        rgauge(parentContext.nullNumber(), () -> reader.numDocs()),
-        true,
-        "numDocs",
-        Category.SEARCHER.toString(),
-        scope);
-    parentContext.gauge(
-        rgauge(parentContext.nullNumber(), () -> reader.maxDoc()),
-        true,
-        "maxDoc",
-        Category.SEARCHER.toString(),
-        scope);
-    parentContext.gauge(
-        rgauge(parentContext.nullNumber(), () -> reader.maxDoc() - 
reader.numDocs()),
-        true,
-        "deletedDocs",
-        Category.SEARCHER.toString(),
-        scope);
-    parentContext.gauge(
-        rgauge(parentContext.nullString(), () -> reader.toString()),
-        true,
-        "reader",
-        Category.SEARCHER.toString(),
-        scope);
-    parentContext.gauge(
-        rgauge(parentContext.nullString(), () -> 
reader.directory().toString()),
-        true,
-        "readerDir",
-        Category.SEARCHER.toString(),
-        scope);
-    parentContext.gauge(
-        rgauge(parentContext.nullNumber(), () -> reader.getVersion()),
-        true,
-        "indexVersion",
-        Category.SEARCHER.toString(),
-        scope);
+      SolrMetricsContext solrMetricsContext, Attributes attributes, String 
scope) {
+    var baseAttributes =
+        attributes.toBuilder().put(CATEGORY_ATTR, 
Category.SEARCHER.toString()).build();
+    // caching (boolean -> 0/1) - synchronous gauge
+    cachingEnabledGauge =
+        new AttributedLongGauge(
+            solrMetricsContext.longGauge("solr_searcher_caching_enabled", "1 
if caching enabled"),
+            baseAttributes);
+    cachingEnabledGauge.set(cachingEnabled ? 1L : 0L);
+
+    // warmupTime (ms) - timer for histogram tracking
+    warmupTimer =
+        new AttributedLongTimer(
+            solrMetricsContext.longHistogram(
+                "solr_searcher_warmup_time", "Searcher warmup time (ms)", 
OtelUnit.MILLISECONDS),
+            baseAttributes);
+
+    // fullSortCount
+    toClose.add(
+        solrMetricsContext.observableLongCounter(
+            "solr_searcher_full_sort",
+            "Number of queries that required building a full DocList with 
sorting",
+            obs -> obs.record(fullSortCount.sum(), baseAttributes)));
+
+    // skipSortCount
+    toClose.add(
+        solrMetricsContext.observableLongCounter(
+            "solr_searcher_skip_sort",
+            "Number of queries where Solr skipped building a full sorted 
DocList and instead returned results directly from the DocSet",
+            obs -> obs.record(skipSortCount.sum(), baseAttributes)));
+    toClose.add(
+        solrMetricsContext.observableLongCounter(
+            "solr_searcher_live_docs_cache",
+            "LiveDocs cache metrics",
+            obs -> {
+              obs.record(
+                  liveDocsInsertsCount.sum(),
+                  baseAttributes.toBuilder().put(TYPE_ATTR, 
"inserts").build());
+              obs.record(
+                  liveDocsHitCount.sum(),
+                  baseAttributes.toBuilder().put(TYPE_ATTR, "hits").build());
+              obs.record(
+                  liveDocsNaiveCacheHitCount.sum(),
+                  baseAttributes.toBuilder().put(TYPE_ATTR, 
"naive_hits").build());
+            }));
+    // reader stats (numeric)
+    toClose.add(
+        solrMetricsContext.observableLongGauge(
+            "solr_searcher_num_docs",

Review Comment:
   Added index



##########
solr/core/src/java/org/apache/solr/search/SolrIndexSearcher.java:
##########
@@ -2605,95 +2636,152 @@ public SolrMetricsContext getSolrMetricsContext() {
     return solrMetricsContext;
   }
 
-  // TODO SOLR-17458: Migrate to Otel
   @Override
   public void initializeMetrics(
-      SolrMetricsContext parentContext, Attributes attributes, String scope) {
-    parentContext.gauge(() -> name, true, "searcherName", 
Category.SEARCHER.toString(), scope);
-    parentContext.gauge(() -> cachingEnabled, true, "caching", 
Category.SEARCHER.toString(), scope);
-    parentContext.gauge(() -> openTime, true, "openedAt", 
Category.SEARCHER.toString(), scope);
-    parentContext.gauge(() -> warmupTime, true, "warmupTime", 
Category.SEARCHER.toString(), scope);
-    parentContext.gauge(
-        () -> registerTime, true, "registeredAt", 
Category.SEARCHER.toString(), scope);
-    parentContext.gauge(
-        fullSortCount::sum, true, "fullSortCount", 
Category.SEARCHER.toString(), scope);
-    parentContext.gauge(
-        skipSortCount::sum, true, "skipSortCount", 
Category.SEARCHER.toString(), scope);
-    final MetricsMap liveDocsCacheMetrics =
-        new MetricsMap(
-            (map) -> {
-              map.put("inserts", liveDocsInsertsCount.sum());
-              map.put("hits", liveDocsHitCount.sum());
-              map.put("naiveHits", liveDocsNaiveCacheHitCount.sum());
-            });
-    parentContext.gauge(
-        liveDocsCacheMetrics, true, "liveDocsCache", 
Category.SEARCHER.toString(), scope);
-    // reader stats
-    parentContext.gauge(
-        rgauge(parentContext.nullNumber(), () -> reader.numDocs()),
-        true,
-        "numDocs",
-        Category.SEARCHER.toString(),
-        scope);
-    parentContext.gauge(
-        rgauge(parentContext.nullNumber(), () -> reader.maxDoc()),
-        true,
-        "maxDoc",
-        Category.SEARCHER.toString(),
-        scope);
-    parentContext.gauge(
-        rgauge(parentContext.nullNumber(), () -> reader.maxDoc() - 
reader.numDocs()),
-        true,
-        "deletedDocs",
-        Category.SEARCHER.toString(),
-        scope);
-    parentContext.gauge(
-        rgauge(parentContext.nullString(), () -> reader.toString()),
-        true,
-        "reader",
-        Category.SEARCHER.toString(),
-        scope);
-    parentContext.gauge(
-        rgauge(parentContext.nullString(), () -> 
reader.directory().toString()),
-        true,
-        "readerDir",
-        Category.SEARCHER.toString(),
-        scope);
-    parentContext.gauge(
-        rgauge(parentContext.nullNumber(), () -> reader.getVersion()),
-        true,
-        "indexVersion",
-        Category.SEARCHER.toString(),
-        scope);
+      SolrMetricsContext solrMetricsContext, Attributes attributes, String 
scope) {
+    var baseAttributes =
+        attributes.toBuilder().put(CATEGORY_ATTR, 
Category.SEARCHER.toString()).build();
+    // caching (boolean -> 0/1) - synchronous gauge
+    cachingEnabledGauge =
+        new AttributedLongGauge(
+            solrMetricsContext.longGauge("solr_searcher_caching_enabled", "1 
if caching enabled"),
+            baseAttributes);
+    cachingEnabledGauge.set(cachingEnabled ? 1L : 0L);
+
+    // warmupTime (ms) - timer for histogram tracking
+    warmupTimer =
+        new AttributedLongTimer(
+            solrMetricsContext.longHistogram(
+                "solr_searcher_warmup_time", "Searcher warmup time (ms)", 
OtelUnit.MILLISECONDS),
+            baseAttributes);
+
+    // fullSortCount
+    toClose.add(
+        solrMetricsContext.observableLongCounter(
+            "solr_searcher_full_sort",
+            "Number of queries that required building a full DocList with 
sorting",
+            obs -> obs.record(fullSortCount.sum(), baseAttributes)));
+
+    // skipSortCount
+    toClose.add(
+        solrMetricsContext.observableLongCounter(
+            "solr_searcher_skip_sort",
+            "Number of queries where Solr skipped building a full sorted 
DocList and instead returned results directly from the DocSet",
+            obs -> obs.record(skipSortCount.sum(), baseAttributes)));
+    toClose.add(
+        solrMetricsContext.observableLongCounter(
+            "solr_searcher_live_docs_cache",
+            "LiveDocs cache metrics",
+            obs -> {
+              obs.record(
+                  liveDocsInsertsCount.sum(),
+                  baseAttributes.toBuilder().put(TYPE_ATTR, 
"inserts").build());
+              obs.record(
+                  liveDocsHitCount.sum(),
+                  baseAttributes.toBuilder().put(TYPE_ATTR, "hits").build());
+              obs.record(
+                  liveDocsNaiveCacheHitCount.sum(),
+                  baseAttributes.toBuilder().put(TYPE_ATTR, 
"naive_hits").build());
+            }));
+    // reader stats (numeric)
+    toClose.add(
+        solrMetricsContext.observableLongGauge(
+            "solr_searcher_num_docs",
+            "Number of live docs",
+            obs -> {
+              try {
+                obs.record(reader.numDocs(), baseAttributes);
+              } catch (Exception ignore) {
+                // replacement for nullNumber
+              }
+            }));
+
+    toClose.add(
+        solrMetricsContext.observableLongGauge(
+            "solr_searcher_max_doc",
+            "Max doc (including deletions)",
+            obs -> {
+              try {
+                obs.record(reader.maxDoc(), baseAttributes);
+              } catch (Exception ignore) {
+              }
+            }));
+
+    toClose.add(
+        solrMetricsContext.observableLongGauge(
+            "solr_searcher_deleted_docs",
+            "Number of deleted docs",
+            obs -> {
+              try {
+                obs.record(reader.maxDoc() - reader.numDocs(), baseAttributes);
+              } catch (Exception ignore) {
+              }
+            }));
+    // indexVersion (numeric)
+    toClose.add(
+        solrMetricsContext.observableLongGauge(
+            "solr_searcher_index_version",
+            "Lucene index version",
+            obs -> {
+              try {
+                obs.record(reader.getVersion(), baseAttributes);
+              } catch (Exception ignore) {
+              }
+            }));
     // size of the currently opened commit
-    parentContext.gauge(
-        () -> {
-          try {
-            Collection<String> files = reader.getIndexCommit().getFileNames();
-            long total = 0;
-            for (String file : files) {
-              total += DirectoryFactory.sizeOf(reader.directory(), file);
-            }
-            return total;
-          } catch (Exception e) {
-            return parentContext.nullNumber();
-          }
-        },
-        true,
-        "indexCommitSize",
-        Category.SEARCHER.toString(),
-        scope);
+    toClose.add(
+        solrMetricsContext.observableLongGauge(
+            "solr_searcher_index_commit_size_bytes",
+            "Size of the current index commit (bytes)",
+            obs -> {
+              try {
+                long total = 0L;
+                for (String file : reader.getIndexCommit().getFileNames()) {
+                  total += DirectoryFactory.sizeOf(reader.directory(), file);
+                }
+                obs.record(total, baseAttributes);
+              } catch (Exception e) {
+                // skip recording if unavailable (no nullNumber in OTel)
+              }
+            }));
+
+    var cacheBaseAttribute =
+        attributes.toBuilder().put(CATEGORY_ATTR, 
Category.CACHE.toString()).build();
     // statsCache metrics
-    parentContext.gauge(
-        new MetricsMap(
-            map -> {
-              statsCache.getCacheMetrics().getSnapshot(map::putNoEx);
-              map.put("statsCacheImpl", statsCache.getClass().getSimpleName());
-            }),
-        true,
-        "statsCache",
-        Category.CACHE.toString(),
-        scope);
+    toClose.add(
+        solrMetricsContext.observableLongGauge(
+            "solr_searcher_stats_cache",

Review Comment:
   > Also WDYT of moving this initialization to StatsCache (perhaps a static 
method) so that SolrIndexSearcher can be less distracted by details of that 
cache. We do similarly for CaffeineCache. SolrIndexSearcher is a huge class.
   
   I like this idea and makes much more sense. Went ahead and moved it out.



##########
solr/core/src/java/org/apache/solr/search/SolrIndexSearcher.java:
##########
@@ -2605,95 +2636,152 @@ public SolrMetricsContext getSolrMetricsContext() {
     return solrMetricsContext;
   }
 
-  // TODO SOLR-17458: Migrate to Otel
   @Override
   public void initializeMetrics(
-      SolrMetricsContext parentContext, Attributes attributes, String scope) {
-    parentContext.gauge(() -> name, true, "searcherName", 
Category.SEARCHER.toString(), scope);
-    parentContext.gauge(() -> cachingEnabled, true, "caching", 
Category.SEARCHER.toString(), scope);
-    parentContext.gauge(() -> openTime, true, "openedAt", 
Category.SEARCHER.toString(), scope);
-    parentContext.gauge(() -> warmupTime, true, "warmupTime", 
Category.SEARCHER.toString(), scope);
-    parentContext.gauge(
-        () -> registerTime, true, "registeredAt", 
Category.SEARCHER.toString(), scope);
-    parentContext.gauge(
-        fullSortCount::sum, true, "fullSortCount", 
Category.SEARCHER.toString(), scope);
-    parentContext.gauge(
-        skipSortCount::sum, true, "skipSortCount", 
Category.SEARCHER.toString(), scope);
-    final MetricsMap liveDocsCacheMetrics =
-        new MetricsMap(
-            (map) -> {
-              map.put("inserts", liveDocsInsertsCount.sum());
-              map.put("hits", liveDocsHitCount.sum());
-              map.put("naiveHits", liveDocsNaiveCacheHitCount.sum());
-            });
-    parentContext.gauge(
-        liveDocsCacheMetrics, true, "liveDocsCache", 
Category.SEARCHER.toString(), scope);
-    // reader stats
-    parentContext.gauge(
-        rgauge(parentContext.nullNumber(), () -> reader.numDocs()),
-        true,
-        "numDocs",
-        Category.SEARCHER.toString(),
-        scope);
-    parentContext.gauge(
-        rgauge(parentContext.nullNumber(), () -> reader.maxDoc()),
-        true,
-        "maxDoc",
-        Category.SEARCHER.toString(),
-        scope);
-    parentContext.gauge(
-        rgauge(parentContext.nullNumber(), () -> reader.maxDoc() - 
reader.numDocs()),
-        true,
-        "deletedDocs",
-        Category.SEARCHER.toString(),
-        scope);
-    parentContext.gauge(
-        rgauge(parentContext.nullString(), () -> reader.toString()),
-        true,
-        "reader",
-        Category.SEARCHER.toString(),
-        scope);
-    parentContext.gauge(
-        rgauge(parentContext.nullString(), () -> 
reader.directory().toString()),
-        true,
-        "readerDir",
-        Category.SEARCHER.toString(),
-        scope);
-    parentContext.gauge(
-        rgauge(parentContext.nullNumber(), () -> reader.getVersion()),
-        true,
-        "indexVersion",
-        Category.SEARCHER.toString(),
-        scope);
+      SolrMetricsContext solrMetricsContext, Attributes attributes, String 
scope) {
+    var baseAttributes =
+        attributes.toBuilder().put(CATEGORY_ATTR, 
Category.SEARCHER.toString()).build();
+    // caching (boolean -> 0/1) - synchronous gauge
+    cachingEnabledGauge =
+        new AttributedLongGauge(
+            solrMetricsContext.longGauge("solr_searcher_caching_enabled", "1 
if caching enabled"),
+            baseAttributes);
+    cachingEnabledGauge.set(cachingEnabled ? 1L : 0L);
+
+    // warmupTime (ms) - timer for histogram tracking
+    warmupTimer =
+        new AttributedLongTimer(
+            solrMetricsContext.longHistogram(
+                "solr_searcher_warmup_time", "Searcher warmup time (ms)", 
OtelUnit.MILLISECONDS),
+            baseAttributes);
+
+    // fullSortCount
+    toClose.add(
+        solrMetricsContext.observableLongCounter(
+            "solr_searcher_full_sort",
+            "Number of queries that required building a full DocList with 
sorting",
+            obs -> obs.record(fullSortCount.sum(), baseAttributes)));
+
+    // skipSortCount
+    toClose.add(
+        solrMetricsContext.observableLongCounter(
+            "solr_searcher_skip_sort",
+            "Number of queries where Solr skipped building a full sorted 
DocList and instead returned results directly from the DocSet",
+            obs -> obs.record(skipSortCount.sum(), baseAttributes)));
+    toClose.add(
+        solrMetricsContext.observableLongCounter(
+            "solr_searcher_live_docs_cache",
+            "LiveDocs cache metrics",
+            obs -> {
+              obs.record(
+                  liveDocsInsertsCount.sum(),
+                  baseAttributes.toBuilder().put(TYPE_ATTR, 
"inserts").build());
+              obs.record(
+                  liveDocsHitCount.sum(),
+                  baseAttributes.toBuilder().put(TYPE_ATTR, "hits").build());
+              obs.record(
+                  liveDocsNaiveCacheHitCount.sum(),
+                  baseAttributes.toBuilder().put(TYPE_ATTR, 
"naive_hits").build());
+            }));
+    // reader stats (numeric)
+    toClose.add(
+        solrMetricsContext.observableLongGauge(
+            "solr_searcher_num_docs",
+            "Number of live docs",
+            obs -> {
+              try {
+                obs.record(reader.numDocs(), baseAttributes);
+              } catch (Exception ignore) {
+                // replacement for nullNumber
+              }
+            }));
+
+    toClose.add(
+        solrMetricsContext.observableLongGauge(
+            "solr_searcher_max_doc",
+            "Max doc (including deletions)",
+            obs -> {
+              try {
+                obs.record(reader.maxDoc(), baseAttributes);
+              } catch (Exception ignore) {
+              }
+            }));
+
+    toClose.add(
+        solrMetricsContext.observableLongGauge(
+            "solr_searcher_deleted_docs",
+            "Number of deleted docs",
+            obs -> {
+              try {
+                obs.record(reader.maxDoc() - reader.numDocs(), baseAttributes);
+              } catch (Exception ignore) {
+              }
+            }));
+    // indexVersion (numeric)
+    toClose.add(
+        solrMetricsContext.observableLongGauge(
+            "solr_searcher_index_version",
+            "Lucene index version",
+            obs -> {
+              try {
+                obs.record(reader.getVersion(), baseAttributes);
+              } catch (Exception ignore) {
+              }
+            }));
     // size of the currently opened commit
-    parentContext.gauge(
-        () -> {
-          try {
-            Collection<String> files = reader.getIndexCommit().getFileNames();
-            long total = 0;
-            for (String file : files) {
-              total += DirectoryFactory.sizeOf(reader.directory(), file);
-            }
-            return total;
-          } catch (Exception e) {
-            return parentContext.nullNumber();
-          }
-        },
-        true,
-        "indexCommitSize",
-        Category.SEARCHER.toString(),
-        scope);
+    toClose.add(
+        solrMetricsContext.observableLongGauge(
+            "solr_searcher_index_commit_size_bytes",
+            "Size of the current index commit (bytes)",
+            obs -> {
+              try {
+                long total = 0L;
+                for (String file : reader.getIndexCommit().getFileNames()) {
+                  total += DirectoryFactory.sizeOf(reader.directory(), file);
+                }
+                obs.record(total, baseAttributes);
+              } catch (Exception e) {
+                // skip recording if unavailable (no nullNumber in OTel)
+              }
+            }));
+
+    var cacheBaseAttribute =
+        attributes.toBuilder().put(CATEGORY_ATTR, 
Category.CACHE.toString()).build();
     // statsCache metrics
-    parentContext.gauge(
-        new MetricsMap(
-            map -> {
-              statsCache.getCacheMetrics().getSnapshot(map::putNoEx);
-              map.put("statsCacheImpl", statsCache.getClass().getSimpleName());
-            }),
-        true,
-        "statsCache",
-        Category.CACHE.toString(),
-        scope);
+    toClose.add(
+        solrMetricsContext.observableLongGauge(
+            "solr_searcher_stats_cache",

Review Comment:
   I like this idea so I moved it out and implemented StatsCache as a 
SolrInfoBean



##########
solr/core/src/java/org/apache/solr/search/SolrIndexSearcher.java:
##########
@@ -2605,95 +2636,152 @@ public SolrMetricsContext getSolrMetricsContext() {
     return solrMetricsContext;
   }
 
-  // TODO SOLR-17458: Migrate to Otel
   @Override
   public void initializeMetrics(
-      SolrMetricsContext parentContext, Attributes attributes, String scope) {
-    parentContext.gauge(() -> name, true, "searcherName", 
Category.SEARCHER.toString(), scope);
-    parentContext.gauge(() -> cachingEnabled, true, "caching", 
Category.SEARCHER.toString(), scope);
-    parentContext.gauge(() -> openTime, true, "openedAt", 
Category.SEARCHER.toString(), scope);
-    parentContext.gauge(() -> warmupTime, true, "warmupTime", 
Category.SEARCHER.toString(), scope);
-    parentContext.gauge(
-        () -> registerTime, true, "registeredAt", 
Category.SEARCHER.toString(), scope);
-    parentContext.gauge(
-        fullSortCount::sum, true, "fullSortCount", 
Category.SEARCHER.toString(), scope);
-    parentContext.gauge(
-        skipSortCount::sum, true, "skipSortCount", 
Category.SEARCHER.toString(), scope);
-    final MetricsMap liveDocsCacheMetrics =
-        new MetricsMap(
-            (map) -> {
-              map.put("inserts", liveDocsInsertsCount.sum());
-              map.put("hits", liveDocsHitCount.sum());
-              map.put("naiveHits", liveDocsNaiveCacheHitCount.sum());
-            });
-    parentContext.gauge(
-        liveDocsCacheMetrics, true, "liveDocsCache", 
Category.SEARCHER.toString(), scope);
-    // reader stats
-    parentContext.gauge(
-        rgauge(parentContext.nullNumber(), () -> reader.numDocs()),
-        true,
-        "numDocs",
-        Category.SEARCHER.toString(),
-        scope);
-    parentContext.gauge(
-        rgauge(parentContext.nullNumber(), () -> reader.maxDoc()),
-        true,
-        "maxDoc",
-        Category.SEARCHER.toString(),
-        scope);
-    parentContext.gauge(
-        rgauge(parentContext.nullNumber(), () -> reader.maxDoc() - 
reader.numDocs()),
-        true,
-        "deletedDocs",
-        Category.SEARCHER.toString(),
-        scope);
-    parentContext.gauge(
-        rgauge(parentContext.nullString(), () -> reader.toString()),
-        true,
-        "reader",
-        Category.SEARCHER.toString(),
-        scope);
-    parentContext.gauge(
-        rgauge(parentContext.nullString(), () -> 
reader.directory().toString()),
-        true,
-        "readerDir",
-        Category.SEARCHER.toString(),
-        scope);
-    parentContext.gauge(
-        rgauge(parentContext.nullNumber(), () -> reader.getVersion()),
-        true,
-        "indexVersion",
-        Category.SEARCHER.toString(),
-        scope);
+      SolrMetricsContext solrMetricsContext, Attributes attributes, String 
scope) {
+    var baseAttributes =
+        attributes.toBuilder().put(CATEGORY_ATTR, 
Category.SEARCHER.toString()).build();
+    // caching (boolean -> 0/1) - synchronous gauge
+    cachingEnabledGauge =
+        new AttributedLongGauge(
+            solrMetricsContext.longGauge("solr_searcher_caching_enabled", "1 
if caching enabled"),
+            baseAttributes);
+    cachingEnabledGauge.set(cachingEnabled ? 1L : 0L);

Review Comment:
   ```
   A "realtime" searcher will currently never become "registered" (since it 
currently lacks caching)
   ```
   
   I saw this comment so it looks like it won't publish metrics on realtime 
searchers since metrics only initialize when it registers.



##########
solr/core/src/java/org/apache/solr/search/SolrIndexSearcher.java:
##########
@@ -2605,95 +2636,152 @@ public SolrMetricsContext getSolrMetricsContext() {
     return solrMetricsContext;
   }
 
-  // TODO SOLR-17458: Migrate to Otel
   @Override
   public void initializeMetrics(
-      SolrMetricsContext parentContext, Attributes attributes, String scope) {
-    parentContext.gauge(() -> name, true, "searcherName", 
Category.SEARCHER.toString(), scope);
-    parentContext.gauge(() -> cachingEnabled, true, "caching", 
Category.SEARCHER.toString(), scope);
-    parentContext.gauge(() -> openTime, true, "openedAt", 
Category.SEARCHER.toString(), scope);
-    parentContext.gauge(() -> warmupTime, true, "warmupTime", 
Category.SEARCHER.toString(), scope);
-    parentContext.gauge(
-        () -> registerTime, true, "registeredAt", 
Category.SEARCHER.toString(), scope);
-    parentContext.gauge(
-        fullSortCount::sum, true, "fullSortCount", 
Category.SEARCHER.toString(), scope);
-    parentContext.gauge(
-        skipSortCount::sum, true, "skipSortCount", 
Category.SEARCHER.toString(), scope);
-    final MetricsMap liveDocsCacheMetrics =
-        new MetricsMap(
-            (map) -> {
-              map.put("inserts", liveDocsInsertsCount.sum());
-              map.put("hits", liveDocsHitCount.sum());
-              map.put("naiveHits", liveDocsNaiveCacheHitCount.sum());
-            });
-    parentContext.gauge(
-        liveDocsCacheMetrics, true, "liveDocsCache", 
Category.SEARCHER.toString(), scope);
-    // reader stats
-    parentContext.gauge(
-        rgauge(parentContext.nullNumber(), () -> reader.numDocs()),
-        true,
-        "numDocs",
-        Category.SEARCHER.toString(),
-        scope);
-    parentContext.gauge(
-        rgauge(parentContext.nullNumber(), () -> reader.maxDoc()),
-        true,
-        "maxDoc",
-        Category.SEARCHER.toString(),
-        scope);
-    parentContext.gauge(
-        rgauge(parentContext.nullNumber(), () -> reader.maxDoc() - 
reader.numDocs()),
-        true,
-        "deletedDocs",
-        Category.SEARCHER.toString(),
-        scope);
-    parentContext.gauge(
-        rgauge(parentContext.nullString(), () -> reader.toString()),
-        true,
-        "reader",
-        Category.SEARCHER.toString(),
-        scope);
-    parentContext.gauge(
-        rgauge(parentContext.nullString(), () -> 
reader.directory().toString()),
-        true,
-        "readerDir",
-        Category.SEARCHER.toString(),
-        scope);
-    parentContext.gauge(
-        rgauge(parentContext.nullNumber(), () -> reader.getVersion()),
-        true,
-        "indexVersion",
-        Category.SEARCHER.toString(),
-        scope);
+      SolrMetricsContext solrMetricsContext, Attributes attributes, String 
scope) {
+    var baseAttributes =
+        attributes.toBuilder().put(CATEGORY_ATTR, 
Category.SEARCHER.toString()).build();
+    // caching (boolean -> 0/1) - synchronous gauge
+    cachingEnabledGauge =
+        new AttributedLongGauge(
+            solrMetricsContext.longGauge("solr_searcher_caching_enabled", "1 
if caching enabled"),
+            baseAttributes);
+    cachingEnabledGauge.set(cachingEnabled ? 1L : 0L);
+
+    // warmupTime (ms) - timer for histogram tracking
+    warmupTimer =
+        new AttributedLongTimer(
+            solrMetricsContext.longHistogram(
+                "solr_searcher_warmup_time", "Searcher warmup time (ms)", 
OtelUnit.MILLISECONDS),
+            baseAttributes);
+
+    // fullSortCount
+    toClose.add(
+        solrMetricsContext.observableLongCounter(
+            "solr_searcher_full_sort",

Review Comment:
   Added queries to the name but also split out full and sort as an attribute 
as `type` if it was a full sort or skipped.



##########
solr/core/src/java/org/apache/solr/search/SolrIndexSearcher.java:
##########
@@ -2605,95 +2636,152 @@ public SolrMetricsContext getSolrMetricsContext() {
     return solrMetricsContext;
   }
 
-  // TODO SOLR-17458: Migrate to Otel
   @Override
   public void initializeMetrics(
-      SolrMetricsContext parentContext, Attributes attributes, String scope) {
-    parentContext.gauge(() -> name, true, "searcherName", 
Category.SEARCHER.toString(), scope);
-    parentContext.gauge(() -> cachingEnabled, true, "caching", 
Category.SEARCHER.toString(), scope);
-    parentContext.gauge(() -> openTime, true, "openedAt", 
Category.SEARCHER.toString(), scope);
-    parentContext.gauge(() -> warmupTime, true, "warmupTime", 
Category.SEARCHER.toString(), scope);
-    parentContext.gauge(
-        () -> registerTime, true, "registeredAt", 
Category.SEARCHER.toString(), scope);
-    parentContext.gauge(
-        fullSortCount::sum, true, "fullSortCount", 
Category.SEARCHER.toString(), scope);
-    parentContext.gauge(
-        skipSortCount::sum, true, "skipSortCount", 
Category.SEARCHER.toString(), scope);
-    final MetricsMap liveDocsCacheMetrics =
-        new MetricsMap(
-            (map) -> {
-              map.put("inserts", liveDocsInsertsCount.sum());
-              map.put("hits", liveDocsHitCount.sum());
-              map.put("naiveHits", liveDocsNaiveCacheHitCount.sum());
-            });
-    parentContext.gauge(
-        liveDocsCacheMetrics, true, "liveDocsCache", 
Category.SEARCHER.toString(), scope);
-    // reader stats
-    parentContext.gauge(
-        rgauge(parentContext.nullNumber(), () -> reader.numDocs()),
-        true,
-        "numDocs",
-        Category.SEARCHER.toString(),
-        scope);
-    parentContext.gauge(
-        rgauge(parentContext.nullNumber(), () -> reader.maxDoc()),
-        true,
-        "maxDoc",
-        Category.SEARCHER.toString(),
-        scope);
-    parentContext.gauge(
-        rgauge(parentContext.nullNumber(), () -> reader.maxDoc() - 
reader.numDocs()),
-        true,
-        "deletedDocs",
-        Category.SEARCHER.toString(),
-        scope);
-    parentContext.gauge(
-        rgauge(parentContext.nullString(), () -> reader.toString()),
-        true,
-        "reader",
-        Category.SEARCHER.toString(),
-        scope);
-    parentContext.gauge(
-        rgauge(parentContext.nullString(), () -> 
reader.directory().toString()),
-        true,
-        "readerDir",
-        Category.SEARCHER.toString(),
-        scope);
-    parentContext.gauge(
-        rgauge(parentContext.nullNumber(), () -> reader.getVersion()),
-        true,
-        "indexVersion",
-        Category.SEARCHER.toString(),
-        scope);
+      SolrMetricsContext solrMetricsContext, Attributes attributes, String 
scope) {
+    var baseAttributes =
+        attributes.toBuilder().put(CATEGORY_ATTR, 
Category.SEARCHER.toString()).build();
+    // caching (boolean -> 0/1) - synchronous gauge
+    cachingEnabledGauge =
+        new AttributedLongGauge(
+            solrMetricsContext.longGauge("solr_searcher_caching_enabled", "1 
if caching enabled"),
+            baseAttributes);
+    cachingEnabledGauge.set(cachingEnabled ? 1L : 0L);

Review Comment:
   > A "realtime" searcher will currently never become "registered" (since it 
currently lacks caching).
   
   I don't think realTimeSearcher will publish metrics because its never 
registered according to this comment and metrics only initialize on registering 
of a searcher.
   



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]


Reply via email to