diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt index dea7a6167d7..e054083eb20 100644 --- a/solr/CHANGES.txt +++ b/solr/CHANGES.txt @@ -115,6 +115,8 @@ New Features * SOLR-17335: New "vectorSimilarity" QParser for matching documents mased on a minimum vector similarity threshold. (hossman) +* SOLR-10654: Introduce output of Prometheus metrics directly from Solr. (Matthew Biscocho via David Smiley) + Improvements --------------------- * SOLR-17137: Enable Prometheus exporter to communicate with SSL protected Solr. (Eivind Bergstøl via Eric Pugh) diff --git a/solr/core/build.gradle b/solr/core/build.gradle index b91f2db168f..b36843fa568 100644 --- a/solr/core/build.gradle +++ b/solr/core/build.gradle @@ -153,6 +153,13 @@ dependencies { implementation 'org.apache.logging.log4j:log4j-core' runtimeOnly 'org.apache.logging.log4j:log4j-slf4j2-impl' + // For the PrometheusResponseWriter + implementation 'io.prometheus:prometheus-metrics-model:1.1.0' + implementation('io.prometheus:prometheus-metrics-exposition-formats:1.1.0', { + exclude group: "io.prometheus", module: "prometheus-metrics-shaded-protobuf" + exclude group: "io.prometheus", module: "prometheus-metrics-config" + }) + // For faster XML processing than the JDK implementation 'org.codehaus.woodstox:stax2-api' implementation 'com.fasterxml.woodstox:woodstox-core' diff --git a/solr/core/src/java/org/apache/solr/core/SolrCore.java b/solr/core/src/java/org/apache/solr/core/SolrCore.java index e4bee2e8502..2b040f14f37 100644 --- a/solr/core/src/java/org/apache/solr/core/SolrCore.java +++ b/solr/core/src/java/org/apache/solr/core/SolrCore.java @@ -17,6 +17,7 @@ package org.apache.solr.core; import static org.apache.solr.common.params.CommonParams.PATH; +import static org.apache.solr.handler.admin.MetricsHandler.PROMETHEUS_METRICS_WT; import com.codahale.metrics.Counter; import com.codahale.metrics.Timer; @@ -129,6 +130,7 @@ import org.apache.solr.response.JacksonJsonWriter; import org.apache.solr.response.PHPResponseWriter; import org.apache.solr.response.PHPSerializedResponseWriter; +import org.apache.solr.response.PrometheusResponseWriter; import org.apache.solr.response.PythonResponseWriter; import org.apache.solr.response.QueryResponseWriter; import org.apache.solr.response.RawResponseWriter; @@ -3018,6 +3020,7 @@ public PluginBag getResponseWriters() { m.put("csv", new CSVResponseWriter()); m.put("schema.xml", new SchemaXmlResponseWriter()); m.put("smile", new SmileResponseWriter()); + m.put(PROMETHEUS_METRICS_WT, new PrometheusResponseWriter()); m.put(ReplicationHandler.FILE_STREAM, getFileStreamWriter()); DEFAULT_RESPONSE_WRITERS = Collections.unmodifiableMap(m); try { diff --git a/solr/core/src/java/org/apache/solr/handler/admin/MetricsHandler.java b/solr/core/src/java/org/apache/solr/handler/admin/MetricsHandler.java index 7490278ffc8..73da8a818e4 100644 --- a/solr/core/src/java/org/apache/solr/handler/admin/MetricsHandler.java +++ b/solr/core/src/java/org/apache/solr/handler/admin/MetricsHandler.java @@ -40,6 +40,7 @@ import java.util.stream.Collectors; import org.apache.solr.common.MapWriter; import org.apache.solr.common.SolrException; +import org.apache.solr.common.params.CommonParams; import org.apache.solr.common.params.SolrParams; import org.apache.solr.common.util.CommonTestInjection; import org.apache.solr.common.util.NamedList; @@ -50,6 +51,7 @@ import org.apache.solr.metrics.SolrMetricManager; import org.apache.solr.request.SolrQueryRequest; import org.apache.solr.request.SolrRequestInfo; +import org.apache.solr.response.PrometheusResponseWriter; import org.apache.solr.response.SolrQueryResponse; import org.apache.solr.security.AuthorizationContext; import org.apache.solr.security.PermissionNameProvider; @@ -68,6 +70,7 @@ public class MetricsHandler extends RequestHandlerBase implements PermissionName public static final String KEY_PARAM = "key"; public static final String EXPR_PARAM = "expr"; public static final String TYPE_PARAM = "type"; + public static final String PROMETHEUS_METRICS_WT = "prometheus"; public static final String ALL = "all"; @@ -116,13 +119,19 @@ public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) throw } } - private void handleRequest(SolrParams params, BiConsumer consumer) - throws Exception { + private void handleRequest(SolrParams params, BiConsumer consumer) { + NamedList response; + if (!enabled) { consumer.accept("error", "metrics collection is disabled"); return; } - boolean compact = params.getBool(COMPACT_PARAM, true); + + if (PROMETHEUS_METRICS_WT.equals(params.get(CommonParams.WT))) { + response = handlePrometheusExport(params); + consumer.accept("metrics", response); + return; + } String[] keys = params.getParams(KEY_PARAM); if (keys != null && keys.length > 0) { handleKeyRequest(keys, consumer); @@ -133,6 +142,14 @@ private void handleRequest(SolrParams params, BiConsumer consume handleExprRequest(exprs, consumer); return; } + + response = handleDropwizardRegistry(params); + + consumer.accept("metrics", response); + } + + private NamedList handleDropwizardRegistry(SolrParams params) { + boolean compact = params.getBool(COMPACT_PARAM, true); MetricFilter mustMatchFilter = parseMustMatchFilter(params); Predicate propertyFilter = parsePropertyFilter(params); List metricTypes = parseMetricTypes(params); @@ -144,6 +161,7 @@ private void handleRequest(SolrParams params, BiConsumer consume for (String registryName : requestedRegistries) { MetricRegistry registry = metricManager.registry(registryName); SimpleOrderedMap result = new SimpleOrderedMap<>(); + MetricUtils.toMaps( registry, metricFilters, @@ -158,7 +176,34 @@ private void handleRequest(SolrParams params, BiConsumer consume response.add(registryName, result); } } - consumer.accept("metrics", response); + return response; + } + + private NamedList handlePrometheusExport(SolrParams params) { + NamedList response = new SimpleOrderedMap<>(); + MetricFilter mustMatchFilter = parseMustMatchFilter(params); + Predicate propertyFilter = parsePropertyFilter(params); + List metricTypes = parseMetricTypes(params); + List metricFilters = + metricTypes.stream().map(MetricType::asMetricFilter).collect(Collectors.toList()); + Set requestedRegistries = parseRegistries(params); + + for (String registryName : requestedRegistries) { + MetricRegistry dropwizardRegistry = metricManager.registry(registryName); + PrometheusResponseWriter.toPrometheus( + dropwizardRegistry, + registryName, + metricFilters, + mustMatchFilter, + propertyFilter, + false, + false, + true, + (registry) -> { + response.add(registryName, registry); + }); + } + return response; } private static class MetricsExpr { diff --git a/solr/core/src/java/org/apache/solr/metrics/prometheus/SolrMetric.java b/solr/core/src/java/org/apache/solr/metrics/prometheus/SolrMetric.java new file mode 100644 index 00000000000..4668e615c85 --- /dev/null +++ b/solr/core/src/java/org/apache/solr/metrics/prometheus/SolrMetric.java @@ -0,0 +1,57 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.solr.metrics.prometheus; + +import com.codahale.metrics.Metric; +import io.prometheus.metrics.model.snapshots.Labels; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; + +/** + * Base class is a wrapper to categorize and export {@link com.codahale.metrics.Metric} to {@link + * io.prometheus.metrics.model.snapshots.DataPointSnapshot} and register to a {@link + * SolrPrometheusExporter}. {@link com.codahale.metrics.MetricRegistry} does not support tags unlike + * prometheus. Metrics registered to the registry need to be parsed out from the metric name to be + * exported to {@link io.prometheus.metrics.model.snapshots.DataPointSnapshot} + */ +public abstract class SolrMetric { + public Metric dropwizardMetric; + public String metricName; + public Map labels = new HashMap<>(); + + public SolrMetric() {} + + public SolrMetric(Metric dropwizardMetric, String metricName) { + this.dropwizardMetric = dropwizardMetric; + this.metricName = metricName; + } + + /* + * Parse labels from the Dropwizard Metric name to be exported + */ + public abstract SolrMetric parseLabels(); + + /* + * Export metric to Prometheus with labels + */ + public abstract void toPrometheus(SolrPrometheusExporter exporter); + + public Labels getLabels() { + return Labels.of(new ArrayList<>(labels.keySet()), new ArrayList<>(labels.values())); + } +} diff --git a/solr/core/src/java/org/apache/solr/metrics/prometheus/SolrNoOpMetric.java b/solr/core/src/java/org/apache/solr/metrics/prometheus/SolrNoOpMetric.java new file mode 100644 index 00000000000..0be1761dc51 --- /dev/null +++ b/solr/core/src/java/org/apache/solr/metrics/prometheus/SolrNoOpMetric.java @@ -0,0 +1,29 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.solr.metrics.prometheus; + +public class SolrNoOpMetric extends SolrMetric { + public SolrNoOpMetric() {} + + @Override + public SolrMetric parseLabels() { + return this; + } + + @Override + public void toPrometheus(SolrPrometheusExporter exporter) {} +} diff --git a/solr/core/src/java/org/apache/solr/metrics/prometheus/SolrPrometheusExporter.java b/solr/core/src/java/org/apache/solr/metrics/prometheus/SolrPrometheusExporter.java new file mode 100644 index 00000000000..6fbb4c0d8ed --- /dev/null +++ b/solr/core/src/java/org/apache/solr/metrics/prometheus/SolrPrometheusExporter.java @@ -0,0 +1,226 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.solr.metrics.prometheus; + +import com.codahale.metrics.Meter; +import com.codahale.metrics.Metric; +import com.codahale.metrics.Timer; +import io.prometheus.metrics.model.snapshots.CounterSnapshot; +import io.prometheus.metrics.model.snapshots.GaugeSnapshot; +import io.prometheus.metrics.model.snapshots.Labels; +import io.prometheus.metrics.model.snapshots.MetricMetadata; +import io.prometheus.metrics.model.snapshots.MetricSnapshot; +import io.prometheus.metrics.model.snapshots.MetricSnapshots; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Base class for all {@link SolrPrometheusExporter} holding {@link MetricSnapshot}s. Can export + * {@link com.codahale.metrics.Metric} to {@link MetricSnapshot} to be outputted for {@link + * org.apache.solr.response.PrometheusResponseWriter} + */ +public abstract class SolrPrometheusExporter { + protected final Map> metricCounters; + protected final Map> metricGauges; + + public SolrPrometheusExporter() { + this.metricCounters = new HashMap<>(); + this.metricGauges = new HashMap<>(); + } + + /** + * Export {@link Metric} to {@link io.prometheus.metrics.model.snapshots.MetricSnapshot} and + * register the Snapshot + * + * @param dropwizardMetric the {@link Metric} to be exported + * @param metricName Dropwizard metric name + */ + public abstract void exportDropwizardMetric(Metric dropwizardMetric, String metricName); + + /** + * Categorize {@link Metric} based on the metric name + * + * @param dropwizardMetric the {@link Metric} to be exported + * @param metricName Dropwizard metric name + */ + public abstract SolrMetric categorizeMetric(Metric dropwizardMetric, String metricName); + + /** + * Export {@link Meter} to {@link + * io.prometheus.metrics.model.snapshots.CounterSnapshot.CounterDataPointSnapshot} and collect + * datapoint + * + * @param metricName name of metric after export + * @param dropwizardMetric the {@link Meter} to be exported + * @param labels label names and values to record + */ + public void exportMeter(String metricName, Meter dropwizardMetric, Labels labels) { + CounterSnapshot.CounterDataPointSnapshot dataPoint = + createCounterDatapoint((double) dropwizardMetric.getCount(), labels); + collectCounterDatapoint(metricName, dataPoint); + } + + /** + * Export {@link com.codahale.metrics.Counter} to {@link + * io.prometheus.metrics.model.snapshots.CounterSnapshot.CounterDataPointSnapshot} and collect + * datapoint + * + * @param metricName name of prometheus metric + * @param dropwizardMetric the {@link com.codahale.metrics.Counter} to be exported + * @param labels label names and values to record + */ + public void exportCounter( + String metricName, com.codahale.metrics.Counter dropwizardMetric, Labels labels) { + CounterSnapshot.CounterDataPointSnapshot dataPoint = + createCounterDatapoint((double) dropwizardMetric.getCount(), labels); + collectCounterDatapoint(metricName, dataPoint); + } + + /** + * Export {@link Timer} ands its mean rate to {@link + * io.prometheus.metrics.model.snapshots.GaugeSnapshot.GaugeDataPointSnapshot} and collect + * datapoint + * + * @param metricName name of prometheus metric + * @param dropwizardMetric the {@link Timer} to be exported + * @param labels label names and values to record + */ + public void exportTimer(String metricName, Timer dropwizardMetric, Labels labels) { + GaugeSnapshot.GaugeDataPointSnapshot dataPoint = + createGaugeDatapoint(dropwizardMetric.getSnapshot().getMean(), labels); + collectGaugeDatapoint(metricName, dataPoint); + } + + /** + * Export {@link Timer} ands its Count to {@link + * io.prometheus.metrics.model.snapshots.CounterSnapshot.CounterDataPointSnapshot} and collect + * datapoint + * + * @param metricName name of prometheus metric + * @param dropwizardMetric the {@link Timer} to be exported + * @param labels label names and values to record + */ + public void exportTimerCount(String metricName, Timer dropwizardMetric, Labels labels) { + CounterSnapshot.CounterDataPointSnapshot dataPoint = + createCounterDatapoint((double) dropwizardMetric.getCount(), labels); + collectCounterDatapoint(metricName, dataPoint); + } + + /** + * Export {@link com.codahale.metrics.Gauge} to {@link + * io.prometheus.metrics.model.snapshots.GaugeSnapshot.GaugeDataPointSnapshot} and collect to + * datapoint. Unlike other Dropwizard metric types, Gauges can have more complex types. In the + * case of a hashmap, collect each as an individual metric and have its key appended as a label to + * the metric called "item" + * + * @param metricName name of prometheus metric + * @param dropwizardMetricRaw the {@link com.codahale.metrics.Gauge} to be exported + * @param labels label names and values to record + */ + public void exportGauge( + String metricName, com.codahale.metrics.Gauge dropwizardMetricRaw, Labels labels) { + Object dropwizardMetric = (dropwizardMetricRaw).getValue(); + if (dropwizardMetric instanceof Number) { + GaugeSnapshot.GaugeDataPointSnapshot dataPoint = + createGaugeDatapoint(((Number) dropwizardMetric).doubleValue(), labels); + collectGaugeDatapoint(metricName, dataPoint); + } else if (dropwizardMetric instanceof HashMap) { + HashMap itemsMap = (HashMap) dropwizardMetric; + for (Object item : itemsMap.keySet()) { + if (itemsMap.get(item) instanceof Number) { + GaugeSnapshot.GaugeDataPointSnapshot dataPoint = + createGaugeDatapoint( + ((Number) itemsMap.get(item)).doubleValue(), + labels.merge(Labels.of("item", (String) item))); + collectGaugeDatapoint(metricName, dataPoint); + } + } + } + } + + /** + * Create a {@link io.prometheus.metrics.model.snapshots.CounterSnapshot.CounterDataPointSnapshot} + * with labels + * + * @param value metric value + * @param labels set of name/values labels + */ + public CounterSnapshot.CounterDataPointSnapshot createCounterDatapoint( + double value, Labels labels) { + return CounterSnapshot.CounterDataPointSnapshot.builder().value(value).labels(labels).build(); + } + + /** + * Create a {@link io.prometheus.metrics.model.snapshots.GaugeSnapshot.GaugeDataPointSnapshot} + * with labels + * + * @param value metric value + * @param labels set of name/values labels + */ + public GaugeSnapshot.GaugeDataPointSnapshot createGaugeDatapoint(double value, Labels labels) { + return GaugeSnapshot.GaugeDataPointSnapshot.builder().value(value).labels(labels).build(); + } + + /** + * Collects {@link io.prometheus.metrics.model.snapshots.CounterSnapshot.CounterDataPointSnapshot} + * and appends to existing metric or create new metric if name does not exist + * + * @param metricName Name of metric + * @param dataPoint Counter datapoint to be collected + */ + public void collectCounterDatapoint( + String metricName, CounterSnapshot.CounterDataPointSnapshot dataPoint) { + if (!metricCounters.containsKey(metricName)) { + metricCounters.put(metricName, new ArrayList<>()); + } + metricCounters.get(metricName).add(dataPoint); + } + + /** + * Collects {@link io.prometheus.metrics.model.snapshots.GaugeSnapshot.GaugeDataPointSnapshot} and + * appends to existing metric or create new metric if name does not exist + * + * @param metricName Name of metric + * @param dataPoint Gauge datapoint to be collected + */ + public void collectGaugeDatapoint( + String metricName, GaugeSnapshot.GaugeDataPointSnapshot dataPoint) { + if (!metricGauges.containsKey(metricName)) { + metricGauges.put(metricName, new ArrayList<>()); + } + metricGauges.get(metricName).add(dataPoint); + } + + /** + * Returns an immutable {@link MetricSnapshots} from the {@link + * io.prometheus.metrics.model.snapshots.DataPointSnapshot}s collected from the registry + */ + public MetricSnapshots collect() { + ArrayList snapshots = new ArrayList<>(); + for (String metricName : metricCounters.keySet()) { + snapshots.add( + new CounterSnapshot(new MetricMetadata(metricName), metricCounters.get(metricName))); + } + for (String metricName : metricGauges.keySet()) { + snapshots.add( + new GaugeSnapshot(new MetricMetadata(metricName), metricGauges.get(metricName))); + } + return new MetricSnapshots(snapshots); + } +} diff --git a/solr/core/src/java/org/apache/solr/metrics/prometheus/core/PrometheusCoreExporterInfo.java b/solr/core/src/java/org/apache/solr/metrics/prometheus/core/PrometheusCoreExporterInfo.java new file mode 100644 index 00000000000..32ed8104276 --- /dev/null +++ b/solr/core/src/java/org/apache/solr/metrics/prometheus/core/PrometheusCoreExporterInfo.java @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.solr.metrics.prometheus.core; + +import java.util.regex.Pattern; + +public interface PrometheusCoreExporterInfo { + /** Category of prefix Solr Core dropwizard handler metric names */ + enum CoreCategory { + ADMIN, + QUERY, + UPDATE, + REPLICATION, + TLOG, + CACHE, + SEARCHER, + HIGHLIGHTER, + INDEX, + CORE + } + + Pattern CLOUD_CORE_PATTERN = Pattern.compile("^core_(.*)_(shard[0-9]+)_(replica_n[0-9]+)$"); +} diff --git a/solr/core/src/java/org/apache/solr/metrics/prometheus/core/SolrCoreCacheMetric.java b/solr/core/src/java/org/apache/solr/metrics/prometheus/core/SolrCoreCacheMetric.java new file mode 100644 index 00000000000..6c71664102e --- /dev/null +++ b/solr/core/src/java/org/apache/solr/metrics/prometheus/core/SolrCoreCacheMetric.java @@ -0,0 +1,52 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.solr.metrics.prometheus.core; + +import com.codahale.metrics.Gauge; +import com.codahale.metrics.Metric; +import org.apache.solr.metrics.prometheus.SolrPrometheusExporter; + +/** Dropwizard metrics of name CACHE.* */ +public class SolrCoreCacheMetric extends SolrCoreMetric { + public static final String CORE_CACHE_SEARCHER_METRICS = "solr_metrics_core_cache"; + + public SolrCoreCacheMetric( + Metric dropwizardMetric, String coreName, String metricName, boolean cloudMode) { + super(dropwizardMetric, coreName, metricName, cloudMode); + } + + /* + * Metric examples being exported + * CACHE.searcher.documentCache + */ + + @Override + public SolrCoreMetric parseLabels() { + String[] parsedMetric = metricName.split("\\."); + if (dropwizardMetric instanceof Gauge) { + labels.put("cacheType", parsedMetric[2]); + } + return this; + } + + @Override + public void toPrometheus(SolrPrometheusExporter exporter) { + if (dropwizardMetric instanceof Gauge) { + exporter.exportGauge(CORE_CACHE_SEARCHER_METRICS, (Gauge) dropwizardMetric, getLabels()); + } + } +} diff --git a/solr/core/src/java/org/apache/solr/metrics/prometheus/core/SolrCoreHandlerMetric.java b/solr/core/src/java/org/apache/solr/metrics/prometheus/core/SolrCoreHandlerMetric.java new file mode 100644 index 00000000000..3a05fbeb5b0 --- /dev/null +++ b/solr/core/src/java/org/apache/solr/metrics/prometheus/core/SolrCoreHandlerMetric.java @@ -0,0 +1,78 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.solr.metrics.prometheus.core; + +import com.codahale.metrics.Counter; +import com.codahale.metrics.Gauge; +import com.codahale.metrics.Meter; +import com.codahale.metrics.Metric; +import com.codahale.metrics.Timer; +import org.apache.solr.metrics.prometheus.SolrPrometheusExporter; + +/** Dropwizard metrics of name ADMIN/QUERY/UPDATE/REPLICATION.* */ +public class SolrCoreHandlerMetric extends SolrCoreMetric { + public static final String CORE_REQUESTS_TOTAL = "solr_metrics_core_requests"; + public static final String CORE_REQUESTS_UPDATE_HANDLER = "solr_metrics_core_update_handler"; + public static final String CORE_REQUESTS_TOTAL_TIME = "solr_metrics_core_requests_time"; + public static final String CORE_REQUEST_TIMES = "solr_metrics_core_average_request_time"; + + public SolrCoreHandlerMetric( + Metric dropwizardMetric, String coreName, String metricName, boolean cloudMode) { + super(dropwizardMetric, coreName, metricName, cloudMode); + } + + /* + * Metric examples being exported + * + * QUERY./select.totalTime + * UPDATE./update.requests + * UPDATE./update.totalTime + */ + + @Override + public SolrCoreMetric parseLabels() { + String[] parsedMetric = metricName.split("\\."); + labels.put("category", parsedMetric[0]); + labels.put("handler", parsedMetric[1]); + labels.put("type", parsedMetric[2]); + return this; + } + + @Override + public void toPrometheus(SolrPrometheusExporter exporter) { + if (dropwizardMetric instanceof Meter) { + exporter.exportMeter(CORE_REQUESTS_TOTAL, (Meter) dropwizardMetric, getLabels()); + } else if (dropwizardMetric instanceof Counter) { + if (metricName.endsWith("requests")) { + exporter.exportCounter(CORE_REQUESTS_TOTAL, (Counter) dropwizardMetric, getLabels()); + } else if (metricName.endsWith("totalTime")) { + // Do not need type label for total time + labels.remove("type"); + exporter.exportCounter(CORE_REQUESTS_TOTAL_TIME, (Counter) dropwizardMetric, getLabels()); + } + } else if (dropwizardMetric instanceof Gauge) { + if (!metricName.endsWith("handlerStart")) { + exporter.exportGauge( + CORE_REQUESTS_UPDATE_HANDLER, (Gauge) dropwizardMetric, getLabels()); + } + } else if (dropwizardMetric instanceof Timer) { + // Do not need type label for request times + labels.remove("type"); + exporter.exportTimer(CORE_REQUEST_TIMES, (Timer) dropwizardMetric, getLabels()); + } + } +} diff --git a/solr/core/src/java/org/apache/solr/metrics/prometheus/core/SolrCoreHighlighterMetric.java b/solr/core/src/java/org/apache/solr/metrics/prometheus/core/SolrCoreHighlighterMetric.java new file mode 100644 index 00000000000..3d0c57d6043 --- /dev/null +++ b/solr/core/src/java/org/apache/solr/metrics/prometheus/core/SolrCoreHighlighterMetric.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.solr.metrics.prometheus.core; + +import com.codahale.metrics.Counter; +import com.codahale.metrics.Metric; +import org.apache.solr.metrics.prometheus.SolrPrometheusExporter; + +/** Dropwizard metrics of name HIGHLIGHTER.* */ +public class SolrCoreHighlighterMetric extends SolrCoreMetric { + public static final String CORE_HIGHLIGHER_METRICS = "solr_metrics_core_highlighter_requests"; + + public SolrCoreHighlighterMetric( + Metric dropwizardMetric, String coreName, String metricName, boolean cloudMode) { + super(dropwizardMetric, coreName, metricName, cloudMode); + } + + /* + * Metric examples being exported + * HIGHLIGHTER.SolrBoundaryScanner.default.requests + * HIGHLIGHTER.SolrFragmentsBuilder.colored.requests + */ + + @Override + public SolrCoreMetric parseLabels() { + String[] parsedMetric = metricName.split("\\."); + labels.put("type", parsedMetric[1]); + labels.put("item", parsedMetric[2]); + return this; + } + + @Override + public void toPrometheus(SolrPrometheusExporter exporter) { + exporter.exportCounter(CORE_HIGHLIGHER_METRICS, (Counter) dropwizardMetric, getLabels()); + } +} diff --git a/solr/core/src/java/org/apache/solr/metrics/prometheus/core/SolrCoreIndexMetric.java b/solr/core/src/java/org/apache/solr/metrics/prometheus/core/SolrCoreIndexMetric.java new file mode 100644 index 00000000000..75a2457668a --- /dev/null +++ b/solr/core/src/java/org/apache/solr/metrics/prometheus/core/SolrCoreIndexMetric.java @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.solr.metrics.prometheus.core; + +import com.codahale.metrics.Gauge; +import com.codahale.metrics.Metric; +import org.apache.solr.metrics.prometheus.SolrPrometheusExporter; + +/** Dropwizard metrics of name INDEX.* */ +public class SolrCoreIndexMetric extends SolrCoreMetric { + public static final String CORE_INDEX_METRICS = "solr_metrics_core_index_size_bytes"; + + public SolrCoreIndexMetric( + Metric dropwizardMetric, String coreName, String metricName, boolean cloudMode) { + super(dropwizardMetric, coreName, metricName, cloudMode); + } + + /* + * Metric examples being exported + * INDEX.sizeInBytes + */ + + @Override + public SolrCoreMetric parseLabels() { + return this; + } + + @Override + public void toPrometheus(SolrPrometheusExporter exporter) { + if (metricName.endsWith("sizeInBytes")) { + exporter.exportGauge(CORE_INDEX_METRICS, (Gauge) dropwizardMetric, getLabels()); + } + } +} diff --git a/solr/core/src/java/org/apache/solr/metrics/prometheus/core/SolrCoreMetric.java b/solr/core/src/java/org/apache/solr/metrics/prometheus/core/SolrCoreMetric.java new file mode 100644 index 00000000000..d977106d241 --- /dev/null +++ b/solr/core/src/java/org/apache/solr/metrics/prometheus/core/SolrCoreMetric.java @@ -0,0 +1,52 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.solr.metrics.prometheus.core; + +import static org.apache.solr.metrics.prometheus.core.PrometheusCoreExporterInfo.CLOUD_CORE_PATTERN; + +import com.codahale.metrics.Metric; +import java.util.regex.Matcher; +import org.apache.solr.common.SolrException; +import org.apache.solr.metrics.prometheus.SolrMetric; + +/** Base class is a wrapper to export a solr.core {@link com.codahale.metrics.Metric} */ +public abstract class SolrCoreMetric extends SolrMetric { + public String coreName; + + public SolrCoreMetric( + Metric dropwizardMetric, String coreName, String metricName, boolean cloudMode) { + super(dropwizardMetric, metricName); + this.coreName = coreName; + labels.put("core", coreName); + if (cloudMode) { + appendCloudModeLabels(); + } + } + + private void appendCloudModeLabels() { + Matcher m = CLOUD_CORE_PATTERN.matcher(coreName); + if (m.find()) { + labels.put("collection", m.group(1)); + labels.put("shard", m.group(2)); + labels.put("replica", m.group(3)); + } else { + throw new SolrException( + SolrException.ErrorCode.SERVER_ERROR, + "Core name does not match pattern for parsing " + coreName); + } + } +} diff --git a/solr/core/src/java/org/apache/solr/metrics/prometheus/core/SolrCoreSearcherMetric.java b/solr/core/src/java/org/apache/solr/metrics/prometheus/core/SolrCoreSearcherMetric.java new file mode 100644 index 00000000000..13d53f38db5 --- /dev/null +++ b/solr/core/src/java/org/apache/solr/metrics/prometheus/core/SolrCoreSearcherMetric.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.solr.metrics.prometheus.core; + +import static org.apache.solr.metrics.prometheus.core.SolrCoreCacheMetric.CORE_CACHE_SEARCHER_METRICS; + +import com.codahale.metrics.Counter; +import com.codahale.metrics.Gauge; +import com.codahale.metrics.Metric; +import com.codahale.metrics.Timer; +import org.apache.solr.metrics.prometheus.SolrPrometheusExporter; + +/** Dropwizard metrics of name SEARCHER.* */ +public class SolrCoreSearcherMetric extends SolrCoreMetric { + public static final String CORE_SEARCHER_METRICS = "solr_metrics_core_searcher_documents"; + public static final String CORE_SEARCHER_TIMES = "solr_metrics_core_average_searcher_warmup_time"; + + public SolrCoreSearcherMetric( + Metric dropwizardMetric, String coreName, String metricName, boolean cloudMode) { + super(dropwizardMetric, coreName, metricName, cloudMode); + } + + /* + * Metric examples being exported + * SEARCHER.searcher.deletedDocs + * SEARCHER.new.time + */ + + @Override + public SolrCoreMetric parseLabels() { + String[] parsedMetric = metricName.split("\\."); + if (!(dropwizardMetric instanceof Counter)) { + labels.put("type", parsedMetric[2]); + } + return this; + } + + @Override + public void toPrometheus(SolrPrometheusExporter exporter) { + if (dropwizardMetric instanceof Gauge) { + if (metricName.endsWith("liveDocsCache")) { + exporter.exportGauge(CORE_CACHE_SEARCHER_METRICS, (Gauge) dropwizardMetric, getLabels()); + } else { + exporter.exportGauge(CORE_SEARCHER_METRICS, (Gauge) dropwizardMetric, getLabels()); + } + } else if (dropwizardMetric instanceof Timer) { + exporter.exportTimer(CORE_SEARCHER_TIMES, (Timer) dropwizardMetric, getLabels()); + } + } +} diff --git a/solr/core/src/java/org/apache/solr/metrics/prometheus/core/SolrCoreTlogMetric.java b/solr/core/src/java/org/apache/solr/metrics/prometheus/core/SolrCoreTlogMetric.java new file mode 100644 index 00000000000..8aba9cfd7ab --- /dev/null +++ b/solr/core/src/java/org/apache/solr/metrics/prometheus/core/SolrCoreTlogMetric.java @@ -0,0 +1,52 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.solr.metrics.prometheus.core; + +import com.codahale.metrics.Meter; +import com.codahale.metrics.Metric; +import org.apache.solr.metrics.prometheus.SolrPrometheusExporter; + +/** Dropwizard metrics of name TLOG.* */ +public class SolrCoreTlogMetric extends SolrCoreMetric { + public static final String CORE_TLOG_METRICS = "solr_metrics_core_tlog"; + + public SolrCoreTlogMetric( + Metric dropwizardMetric, String coreName, String metricName, boolean cloudMode) { + super(dropwizardMetric, coreName, metricName, cloudMode); + } + + /* + * Metric examples being exported + * TLOG.buffered.ops + */ + + @Override + public SolrCoreMetric parseLabels() { + String[] parsedMetric = metricName.split("\\."); + if (dropwizardMetric instanceof Meter) { + labels.put("item", parsedMetric[1]); + } + return this; + } + + @Override + public void toPrometheus(SolrPrometheusExporter exporter) { + if (dropwizardMetric instanceof Meter) { + exporter.exportMeter(CORE_TLOG_METRICS, (Meter) dropwizardMetric, getLabels()); + } + } +} diff --git a/solr/core/src/java/org/apache/solr/metrics/prometheus/core/SolrPrometheusCoreExporter.java b/solr/core/src/java/org/apache/solr/metrics/prometheus/core/SolrPrometheusCoreExporter.java new file mode 100644 index 00000000000..99066acea32 --- /dev/null +++ b/solr/core/src/java/org/apache/solr/metrics/prometheus/core/SolrPrometheusCoreExporter.java @@ -0,0 +1,74 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.solr.metrics.prometheus.core; + +import com.codahale.metrics.Metric; +import com.google.common.base.Enums; +import org.apache.solr.metrics.prometheus.SolrMetric; +import org.apache.solr.metrics.prometheus.SolrNoOpMetric; +import org.apache.solr.metrics.prometheus.SolrPrometheusExporter; + +/** + * This class maintains a {@link io.prometheus.metrics.model.snapshots.MetricSnapshot}s exported + * from solr.core {@link com.codahale.metrics.MetricRegistry} + */ +public class SolrPrometheusCoreExporter extends SolrPrometheusExporter + implements PrometheusCoreExporterInfo { + public final String coreName; + public final boolean cloudMode; + + public SolrPrometheusCoreExporter(String coreName, boolean cloudMode) { + super(); + this.coreName = coreName; + this.cloudMode = cloudMode; + } + + @Override + public void exportDropwizardMetric(Metric dropwizardMetric, String metricName) { + SolrMetric solrCoreMetric = categorizeMetric(dropwizardMetric, metricName); + solrCoreMetric.parseLabels().toPrometheus(this); + } + + @Override + public SolrMetric categorizeMetric(Metric dropwizardMetric, String metricName) { + String metricCategory = metricName.split("\\.", 2)[0]; + if (!Enums.getIfPresent(PrometheusCoreExporterInfo.CoreCategory.class, metricCategory) + .isPresent()) { + return new SolrNoOpMetric(); + } + switch (CoreCategory.valueOf(metricCategory)) { + case ADMIN: + case QUERY: + case UPDATE: + case REPLICATION: + return new SolrCoreHandlerMetric(dropwizardMetric, coreName, metricName, cloudMode); + case TLOG: + return new SolrCoreTlogMetric(dropwizardMetric, coreName, metricName, cloudMode); + case CACHE: + return new SolrCoreCacheMetric(dropwizardMetric, coreName, metricName, cloudMode); + case SEARCHER: + return new SolrCoreSearcherMetric(dropwizardMetric, coreName, metricName, cloudMode); + case HIGHLIGHTER: + return new SolrCoreHighlighterMetric(dropwizardMetric, coreName, metricName, cloudMode); + case INDEX: + return new SolrCoreIndexMetric(dropwizardMetric, coreName, metricName, cloudMode); + case CORE: + default: + return new SolrNoOpMetric(); + } + } +} diff --git a/solr/core/src/java/org/apache/solr/metrics/prometheus/core/package-info.java b/solr/core/src/java/org/apache/solr/metrics/prometheus/core/package-info.java new file mode 100644 index 00000000000..701dcf9e3bf --- /dev/null +++ b/solr/core/src/java/org/apache/solr/metrics/prometheus/core/package-info.java @@ -0,0 +1,22 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * The {@link org.apache.solr.metrics.prometheus.core.SolrPrometheusCoreExporter} is responsible for + * exporting solr.core registry metrics to Prometheus. + */ +package org.apache.solr.metrics.prometheus.core; diff --git a/solr/core/src/java/org/apache/solr/metrics/prometheus/jetty/SolrJettyDispatchesMetric.java b/solr/core/src/java/org/apache/solr/metrics/prometheus/jetty/SolrJettyDispatchesMetric.java new file mode 100644 index 00000000000..dbded5d36ac --- /dev/null +++ b/solr/core/src/java/org/apache/solr/metrics/prometheus/jetty/SolrJettyDispatchesMetric.java @@ -0,0 +1,46 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.solr.metrics.prometheus.jetty; + +import com.codahale.metrics.Metric; +import com.codahale.metrics.Timer; +import org.apache.solr.metrics.prometheus.SolrMetric; +import org.apache.solr.metrics.prometheus.SolrPrometheusExporter; + +/* Dropwizard metrics of name *.dispatches */ +public class SolrJettyDispatchesMetric extends SolrJettyMetric { + public static final String JETTY_DISPATCHES_TOTAL = "solr_metrics_jetty_dispatches"; + + public SolrJettyDispatchesMetric(Metric dropwizardMetric, String metricName) { + super(dropwizardMetric, metricName); + } + + /* + * Metric examples being exported + * org.eclipse.jetty.server.handler.DefaultHandler.dispatches + */ + + @Override + public SolrMetric parseLabels() { + return this; + } + + @Override + public void toPrometheus(SolrPrometheusExporter exporter) { + exporter.exportTimerCount(JETTY_DISPATCHES_TOTAL, (Timer) dropwizardMetric, getLabels()); + } +} diff --git a/solr/core/src/java/org/apache/solr/metrics/prometheus/jetty/SolrJettyMetric.java b/solr/core/src/java/org/apache/solr/metrics/prometheus/jetty/SolrJettyMetric.java new file mode 100644 index 00000000000..95211f6d9eb --- /dev/null +++ b/solr/core/src/java/org/apache/solr/metrics/prometheus/jetty/SolrJettyMetric.java @@ -0,0 +1,27 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.solr.metrics.prometheus.jetty; + +import com.codahale.metrics.Metric; +import org.apache.solr.metrics.prometheus.SolrMetric; + +/** Base class is a wrapper to export a solr.jetty {@link com.codahale.metrics.Metric} */ +public abstract class SolrJettyMetric extends SolrMetric { + public SolrJettyMetric(Metric dropwizardMetric, String metricName) { + super(dropwizardMetric, metricName); + } +} diff --git a/solr/core/src/java/org/apache/solr/metrics/prometheus/jetty/SolrJettyReqRespMetric.java b/solr/core/src/java/org/apache/solr/metrics/prometheus/jetty/SolrJettyReqRespMetric.java new file mode 100644 index 00000000000..51c1fd80b3a --- /dev/null +++ b/solr/core/src/java/org/apache/solr/metrics/prometheus/jetty/SolrJettyReqRespMetric.java @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.solr.metrics.prometheus.jetty; + +import com.codahale.metrics.Counter; +import com.codahale.metrics.Meter; +import com.codahale.metrics.Metric; +import com.codahale.metrics.Timer; +import org.apache.solr.metrics.prometheus.SolrMetric; +import org.apache.solr.metrics.prometheus.SolrPrometheusExporter; + +/* Dropwizard metrics of name *.xx-responses and *-requests */ +public class SolrJettyReqRespMetric extends SolrJettyMetric { + public static final String JETTY_RESPONSES_TOTAL = "solr_metrics_jetty_response"; + public static final String JETTY_REQUESTS_TOTAL = "solr_metrics_jetty_requests"; + + public SolrJettyReqRespMetric(Metric dropwizardMetric, String metricName) { + super(dropwizardMetric, metricName); + } + + /* + * Metric examples being exported + * org.eclipse.jetty.server.handler.DefaultHandler.2xx-responses + * org.eclipse.jetty.server.handler.DefaultHandler.get-requests + */ + + @Override + public SolrMetric parseLabels() { + String[] parsedMetric = metricName.split("\\."); + String label = parsedMetric[parsedMetric.length - 1].split("-")[0]; + if (metricName.endsWith("xx-responses")) { + labels.put("status", label); + } else { + labels.put("method", label); + } + return this; + } + + @Override + public void toPrometheus(SolrPrometheusExporter exporter) { + if (metricName.endsWith("xx-responses")) { + exporter.exportMeter(JETTY_RESPONSES_TOTAL, (Meter) dropwizardMetric, getLabels()); + } else if (metricName.endsWith("-requests")) { + if (dropwizardMetric instanceof Counter) { + exporter.exportCounter(JETTY_REQUESTS_TOTAL, (Counter) dropwizardMetric, getLabels()); + } else if (dropwizardMetric instanceof Timer) { + exporter.exportTimerCount(JETTY_REQUESTS_TOTAL, (Timer) dropwizardMetric, getLabels()); + } + } + } +} diff --git a/solr/core/src/java/org/apache/solr/metrics/prometheus/jetty/SolrPrometheusJettyExporter.java b/solr/core/src/java/org/apache/solr/metrics/prometheus/jetty/SolrPrometheusJettyExporter.java new file mode 100644 index 00000000000..0cfafb7f4cd --- /dev/null +++ b/solr/core/src/java/org/apache/solr/metrics/prometheus/jetty/SolrPrometheusJettyExporter.java @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.solr.metrics.prometheus.jetty; + +import com.codahale.metrics.Metric; +import org.apache.solr.metrics.prometheus.SolrMetric; +import org.apache.solr.metrics.prometheus.SolrNoOpMetric; +import org.apache.solr.metrics.prometheus.SolrPrometheusExporter; + +/** + * This class maintains a {@link io.prometheus.metrics.model.snapshots.MetricSnapshot}s exported + * from solr.jetty {@link com.codahale.metrics.MetricRegistry} + */ +public class SolrPrometheusJettyExporter extends SolrPrometheusExporter { + public SolrPrometheusJettyExporter() { + super(); + } + + @Override + public void exportDropwizardMetric(Metric dropwizardMetric, String metricName) { + SolrMetric solrJettyMetric = categorizeMetric(dropwizardMetric, metricName); + solrJettyMetric.parseLabels().toPrometheus(this); + } + + @Override + public SolrMetric categorizeMetric(Metric dropwizardMetric, String metricName) { + if (metricName.endsWith("xx-responses") || metricName.endsWith("-requests")) { + return new SolrJettyReqRespMetric(dropwizardMetric, metricName); + } else if (metricName.endsWith(".dispatches")) { + return new SolrJettyDispatchesMetric(dropwizardMetric, metricName); + } else { + return new SolrNoOpMetric(); + } + } +} diff --git a/solr/core/src/java/org/apache/solr/metrics/prometheus/jetty/package-info.java b/solr/core/src/java/org/apache/solr/metrics/prometheus/jetty/package-info.java new file mode 100644 index 00000000000..9f4123c13f1 --- /dev/null +++ b/solr/core/src/java/org/apache/solr/metrics/prometheus/jetty/package-info.java @@ -0,0 +1,22 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * The {@link org.apache.solr.metrics.prometheus.jetty.SolrPrometheusJettyExporter} is responsible + * for exporting solr.jetty registry metrics to Prometheus. + */ +package org.apache.solr.metrics.prometheus.jetty; diff --git a/solr/core/src/java/org/apache/solr/metrics/prometheus/jvm/PrometheusJvmExporterInfo.java b/solr/core/src/java/org/apache/solr/metrics/prometheus/jvm/PrometheusJvmExporterInfo.java new file mode 100644 index 00000000000..dfe9075eb17 --- /dev/null +++ b/solr/core/src/java/org/apache/solr/metrics/prometheus/jvm/PrometheusJvmExporterInfo.java @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.solr.metrics.prometheus.jvm; + +public interface PrometheusJvmExporterInfo { + /** Category of prefix Solr JVM dropwizard handler metric names */ + enum JvmCategory { + buffers, + gc, + memory, + os, + threads, + classes, + system + } +} diff --git a/solr/core/src/java/org/apache/solr/metrics/prometheus/jvm/SolrJvmBuffersMetric.java b/solr/core/src/java/org/apache/solr/metrics/prometheus/jvm/SolrJvmBuffersMetric.java new file mode 100644 index 00000000000..2cd09ac64e8 --- /dev/null +++ b/solr/core/src/java/org/apache/solr/metrics/prometheus/jvm/SolrJvmBuffersMetric.java @@ -0,0 +1,56 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.solr.metrics.prometheus.jvm; + +import com.codahale.metrics.Gauge; +import com.codahale.metrics.Metric; +import org.apache.solr.metrics.prometheus.SolrPrometheusExporter; + +/* Dropwizard metrics of name buffers.* */ +public class SolrJvmBuffersMetric extends SolrJvmMetric { + public static final String JVM_BUFFERS = "solr_metrics_jvm_buffers"; + public static final String JVM_BUFFERS_BYTES = "solr_metrics_jvm_buffers_bytes"; + + public SolrJvmBuffersMetric(Metric dropwizardMetric, String metricName) { + super(dropwizardMetric, metricName); + } + + /* + * Metric examples being exported + * buffers.direct.MemoryUsed + * buffers.mapped.Count + */ + + @Override + public SolrJvmMetric parseLabels() { + String[] parsedMetric = metricName.split("\\."); + labels.put("pool", parsedMetric[1]); + labels.put("item", parsedMetric[2]); + return this; + } + + @Override + public void toPrometheus(SolrPrometheusExporter exporter) { + String[] parsedMetric = metricName.split("\\."); + String metricType = parsedMetric[parsedMetric.length - 1]; + if (metricType.equals("Count")) { + exporter.exportGauge(JVM_BUFFERS, (Gauge) dropwizardMetric, getLabels()); + } else if (metricType.equals(("MemoryUsed")) || metricType.equals(("TotalCapacity"))) { + exporter.exportGauge(JVM_BUFFERS_BYTES, (Gauge) dropwizardMetric, getLabels()); + } + } +} diff --git a/solr/core/src/java/org/apache/solr/metrics/prometheus/jvm/SolrJvmGcMetrics.java b/solr/core/src/java/org/apache/solr/metrics/prometheus/jvm/SolrJvmGcMetrics.java new file mode 100644 index 00000000000..50aca3bff27 --- /dev/null +++ b/solr/core/src/java/org/apache/solr/metrics/prometheus/jvm/SolrJvmGcMetrics.java @@ -0,0 +1,54 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.solr.metrics.prometheus.jvm; + +import com.codahale.metrics.Gauge; +import com.codahale.metrics.Metric; +import org.apache.solr.metrics.prometheus.SolrMetric; +import org.apache.solr.metrics.prometheus.SolrPrometheusExporter; + +/* Dropwizard metrics of name gc.* */ +public class SolrJvmGcMetrics extends SolrJvmMetric { + public static final String JVM_GC = "solr_metrics_jvm_gc"; + public static final String JVM_GC_SECONDS = "solr_metrics_jvm_gc_seconds"; + + public SolrJvmGcMetrics(Metric dropwizardMetric, String metricName) { + super(dropwizardMetric, metricName); + } + + /* + * Metric examples being exported + * gc.G1-Old-Generation.time + * gc.G1-Young-Generation.count + */ + + @Override + public SolrMetric parseLabels() { + String[] parsedMetric = metricName.split("\\."); + labels.put("item", parsedMetric[1]); + return this; + } + + @Override + public void toPrometheus(SolrPrometheusExporter exporter) { + if (metricName.endsWith(".count")) { + exporter.exportGauge(JVM_GC, (Gauge) dropwizardMetric, getLabels()); + } else if (metricName.endsWith(".time")) { + exporter.exportGauge(JVM_GC_SECONDS, (Gauge) dropwizardMetric, getLabels()); + } + } +} diff --git a/solr/core/src/java/org/apache/solr/metrics/prometheus/jvm/SolrJvmMemoryMetric.java b/solr/core/src/java/org/apache/solr/metrics/prometheus/jvm/SolrJvmMemoryMetric.java new file mode 100644 index 00000000000..180edae2100 --- /dev/null +++ b/solr/core/src/java/org/apache/solr/metrics/prometheus/jvm/SolrJvmMemoryMetric.java @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.solr.metrics.prometheus.jvm; + +import com.codahale.metrics.Gauge; +import com.codahale.metrics.Metric; +import org.apache.solr.metrics.prometheus.SolrMetric; +import org.apache.solr.metrics.prometheus.SolrPrometheusExporter; + +/* Dropwizard metrics of name memory.* */ +public class SolrJvmMemoryMetric extends SolrJvmMetric { + public static final String JVM_MEMORY_POOL_BYTES = "solr_metrics_jvm_memory_pools_bytes"; + public static final String JVM_MEMORY = "solr_metrics_jvm_heap"; + + public SolrJvmMemoryMetric(Metric dropwizardMetric, String metricName) { + super(dropwizardMetric, metricName); + } + + /* + * Metric examples being exported + * memory.heap.usage + * memory.non-heap.committed + * memory.total.committed + * memory.pools.CodeHeap-'non-nmethods'.committed + */ + + @Override + public SolrMetric parseLabels() { + String[] parsedMetric = metricName.split("\\."); + labels.put("item", parsedMetric[parsedMetric.length - 1]); + return this; + } + + @Override + public void toPrometheus(SolrPrometheusExporter exporter) { + String[] parsedMetric = metricName.split("\\."); + String metricType = parsedMetric[1]; + switch (metricType) { + case "heap": + case "non-heap": + case "total": + labels.put("memory", parsedMetric[1]); + exporter.exportGauge(JVM_MEMORY, (Gauge) dropwizardMetric, getLabels()); + break; + case "pools": + labels.put("space", parsedMetric[2]); + exporter.exportGauge(JVM_MEMORY_POOL_BYTES, (Gauge) dropwizardMetric, getLabels()); + break; + } + } +} diff --git a/solr/core/src/java/org/apache/solr/metrics/prometheus/jvm/SolrJvmMetric.java b/solr/core/src/java/org/apache/solr/metrics/prometheus/jvm/SolrJvmMetric.java new file mode 100644 index 00000000000..75541640488 --- /dev/null +++ b/solr/core/src/java/org/apache/solr/metrics/prometheus/jvm/SolrJvmMetric.java @@ -0,0 +1,27 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.solr.metrics.prometheus.jvm; + +import com.codahale.metrics.Metric; +import org.apache.solr.metrics.prometheus.SolrMetric; + +/** Base class is a wrapper to export a solr.jvm {@link com.codahale.metrics.Metric} */ +public abstract class SolrJvmMetric extends SolrMetric { + public SolrJvmMetric(Metric dropwizardMetric, String metricName) { + super(dropwizardMetric, metricName); + } +} diff --git a/solr/core/src/java/org/apache/solr/metrics/prometheus/jvm/SolrJvmOsMetric.java b/solr/core/src/java/org/apache/solr/metrics/prometheus/jvm/SolrJvmOsMetric.java new file mode 100644 index 00000000000..fc42c6ac8ef --- /dev/null +++ b/solr/core/src/java/org/apache/solr/metrics/prometheus/jvm/SolrJvmOsMetric.java @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.solr.metrics.prometheus.jvm; + +import com.codahale.metrics.Gauge; +import com.codahale.metrics.Metric; +import org.apache.solr.metrics.prometheus.SolrMetric; +import org.apache.solr.metrics.prometheus.SolrPrometheusExporter; + +/* Dropwizard metrics of name os.* and threads.* */ +public class SolrJvmOsMetric extends SolrJvmMetric { + public static final String JVM_OS_THREADS = "solr_metrics_jvm_threads"; + public static final String JVM_OS = "solr_metrics_os"; + + public SolrJvmOsMetric(Metric dropwizardMetric, String metricName) { + super(dropwizardMetric, metricName); + } + + /* + * Metric examples being exported + * os.availableProcessors + * threads.peak.count + */ + + @Override + public SolrMetric parseLabels() { + String[] parsedMetric = metricName.split("\\."); + if (parsedMetric[0].equals("threads")) { + labels.put("item", parsedMetric[1]); + } else { + labels.put("item", parsedMetric[parsedMetric.length - 1]); + } + return this; + } + + @Override + public void toPrometheus(SolrPrometheusExporter exporter) { + if (metricName.startsWith("threads.")) { + exporter.exportGauge(JVM_OS_THREADS, (Gauge) dropwizardMetric, getLabels()); + } else { + exporter.exportGauge(JVM_OS, (Gauge) dropwizardMetric, getLabels()); + } + } +} diff --git a/solr/core/src/java/org/apache/solr/metrics/prometheus/jvm/SolrPrometheusJvmExporter.java b/solr/core/src/java/org/apache/solr/metrics/prometheus/jvm/SolrPrometheusJvmExporter.java new file mode 100644 index 00000000000..91a5ef07229 --- /dev/null +++ b/solr/core/src/java/org/apache/solr/metrics/prometheus/jvm/SolrPrometheusJvmExporter.java @@ -0,0 +1,61 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.solr.metrics.prometheus.jvm; + +import com.codahale.metrics.Metric; +import com.google.common.base.Enums; +import org.apache.solr.metrics.prometheus.SolrMetric; +import org.apache.solr.metrics.prometheus.SolrNoOpMetric; +import org.apache.solr.metrics.prometheus.SolrPrometheusExporter; + +/** + * This class maintains a {@link io.prometheus.metrics.model.snapshots.MetricSnapshot}s exported + * from solr.jvm {@link com.codahale.metrics.MetricRegistry} + */ +public class SolrPrometheusJvmExporter extends SolrPrometheusExporter + implements PrometheusJvmExporterInfo { + public SolrPrometheusJvmExporter() { + super(); + } + + @Override + public void exportDropwizardMetric(Metric dropwizardMetric, String metricName) { + SolrMetric solrJvmMetric = categorizeMetric(dropwizardMetric, metricName); + solrJvmMetric.parseLabels().toPrometheus(this); + } + + @Override + public SolrMetric categorizeMetric(Metric dropwizardMetric, String metricName) { + String metricCategory = metricName.split("\\.", 2)[0]; + if (!Enums.getIfPresent(JvmCategory.class, metricCategory).isPresent()) { + return new SolrNoOpMetric(); + } + switch (JvmCategory.valueOf(metricCategory)) { + case gc: + return new SolrJvmGcMetrics(dropwizardMetric, metricName); + case memory: + return new SolrJvmMemoryMetric(dropwizardMetric, metricName); + case os: + case threads: + return new SolrJvmOsMetric(dropwizardMetric, metricName); + case buffers: + return new SolrJvmBuffersMetric(dropwizardMetric, metricName); + default: + return new SolrNoOpMetric(); + } + } +} diff --git a/solr/core/src/java/org/apache/solr/metrics/prometheus/jvm/package-info.java b/solr/core/src/java/org/apache/solr/metrics/prometheus/jvm/package-info.java new file mode 100644 index 00000000000..230f9576576 --- /dev/null +++ b/solr/core/src/java/org/apache/solr/metrics/prometheus/jvm/package-info.java @@ -0,0 +1,22 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * The {@link org.apache.solr.metrics.prometheus.jvm.SolrPrometheusJvmExporter} is responsible for + * exporting solr.jvm registry metrics to Prometheus. + */ +package org.apache.solr.metrics.prometheus.jvm; diff --git a/solr/core/src/java/org/apache/solr/metrics/prometheus/node/PrometheusNodeExporterInfo.java b/solr/core/src/java/org/apache/solr/metrics/prometheus/node/PrometheusNodeExporterInfo.java new file mode 100644 index 00000000000..67caa63ef24 --- /dev/null +++ b/solr/core/src/java/org/apache/solr/metrics/prometheus/node/PrometheusNodeExporterInfo.java @@ -0,0 +1,27 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.solr.metrics.prometheus.node; + +public interface PrometheusNodeExporterInfo { + /** Category of prefix Solr Node dropwizard handler metric names */ + enum NodeCategory { + ADMIN, + UPDATE, + CONTAINER, + CACHE + } +} diff --git a/solr/core/src/java/org/apache/solr/metrics/prometheus/node/SolrNodeContainerMetric.java b/solr/core/src/java/org/apache/solr/metrics/prometheus/node/SolrNodeContainerMetric.java new file mode 100644 index 00000000000..bb48eea9790 --- /dev/null +++ b/solr/core/src/java/org/apache/solr/metrics/prometheus/node/SolrNodeContainerMetric.java @@ -0,0 +1,57 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.solr.metrics.prometheus.node; + +import com.codahale.metrics.Gauge; +import com.codahale.metrics.Metric; +import org.apache.solr.metrics.prometheus.SolrMetric; +import org.apache.solr.metrics.prometheus.SolrPrometheusExporter; + +/* Dropwizard metrics of name CONTAINER.* */ +public class SolrNodeContainerMetric extends SolrNodeMetric { + public static final String NODE_CORES = "solr_metrics_node_cores"; + public static final String NODE_CORE_FS_BYTES = "solr_metrics_node_core_root_fs_bytes"; + + public SolrNodeContainerMetric(Metric dropwizardMetric, String metricName) { + super(dropwizardMetric, metricName); + } + + /* + * Metric examples being exported + * CONTAINER.fs.coreRoot.totalSpace + * CONTAINER.cores.loaded + */ + + @Override + public SolrMetric parseLabels() { + String[] parsedMetric = metricName.split("\\."); + labels.put("category", parsedMetric[0]); + return this; + } + + @Override + public void toPrometheus(SolrPrometheusExporter exporter) { + String[] parsedMetric = metricName.split("\\."); + if (metricName.startsWith("CONTAINER.cores.")) { + labels.put("item", parsedMetric[2]); + exporter.exportGauge(NODE_CORES, (Gauge) dropwizardMetric, getLabels()); + } else if (metricName.startsWith("CONTAINER.fs.coreRoot.")) { + labels.put("item", parsedMetric[3]); + exporter.exportGauge(NODE_CORE_FS_BYTES, (Gauge) dropwizardMetric, getLabels()); + } + } +} diff --git a/solr/core/src/java/org/apache/solr/metrics/prometheus/node/SolrNodeHandlerMetric.java b/solr/core/src/java/org/apache/solr/metrics/prometheus/node/SolrNodeHandlerMetric.java new file mode 100644 index 00000000000..2d28ce90703 --- /dev/null +++ b/solr/core/src/java/org/apache/solr/metrics/prometheus/node/SolrNodeHandlerMetric.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.solr.metrics.prometheus.node; + +import com.codahale.metrics.Counter; +import com.codahale.metrics.Gauge; +import com.codahale.metrics.Meter; +import com.codahale.metrics.Metric; +import org.apache.solr.metrics.prometheus.SolrMetric; +import org.apache.solr.metrics.prometheus.SolrPrometheusExporter; + +/* Dropwizard metrics of name ADMIN.* and UPDATE.* */ +public class SolrNodeHandlerMetric extends SolrNodeMetric { + public static final String NODE_REQUESTS = "solr_metrics_node_requests"; + public static final String NODE_SECONDS_TOTAL = "solr_metrics_node_requests_time"; + public static final String NODE_CONNECTIONS = "solr_metrics_node_connections"; + + public SolrNodeHandlerMetric(Metric dropwizardMetric, String metricName) { + super(dropwizardMetric, metricName); + } + + /* + * Metric examples being exported + * ADMIN./admin/collections.requests + * UPDATE.updateShardHandler.maxConnections + */ + + @Override + public SolrMetric parseLabels() { + String[] parsedMetric = metricName.split("\\."); + labels.put("category", parsedMetric[0]); + labels.put("handler", parsedMetric[1]); + labels.put("type", parsedMetric[2]); + return this; + } + + @Override + public void toPrometheus(SolrPrometheusExporter exporter) { + if (metricName.endsWith(".totalTime")) { + labels.remove("type"); + exporter.exportCounter(NODE_SECONDS_TOTAL, (Counter) dropwizardMetric, getLabels()); + } else if (metricName.endsWith("Connections")) { + exporter.exportGauge(NODE_CONNECTIONS, (Gauge) dropwizardMetric, getLabels()); + } else if (dropwizardMetric instanceof Meter) { + exporter.exportMeter(NODE_REQUESTS, (Meter) dropwizardMetric, getLabels()); + } else if (dropwizardMetric instanceof Counter) { + exporter.exportCounter(NODE_REQUESTS, (Counter) dropwizardMetric, getLabels()); + } + } +} diff --git a/solr/core/src/java/org/apache/solr/metrics/prometheus/node/SolrNodeMetric.java b/solr/core/src/java/org/apache/solr/metrics/prometheus/node/SolrNodeMetric.java new file mode 100644 index 00000000000..34795e240de --- /dev/null +++ b/solr/core/src/java/org/apache/solr/metrics/prometheus/node/SolrNodeMetric.java @@ -0,0 +1,29 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.solr.metrics.prometheus.node; + +import com.codahale.metrics.Metric; +import org.apache.solr.metrics.prometheus.SolrMetric; + +/** Base class is a wrapper to export a solr.node {@link com.codahale.metrics.Metric} */ +public abstract class SolrNodeMetric extends SolrMetric { + public static final String NODE_THREAD_POOL = "solr_metrics_node_thread_pool"; + + public SolrNodeMetric(Metric dropwizardMetric, String metricName) { + super(dropwizardMetric, metricName); + } +} diff --git a/solr/core/src/java/org/apache/solr/metrics/prometheus/node/SolrPrometheusNodeExporter.java b/solr/core/src/java/org/apache/solr/metrics/prometheus/node/SolrPrometheusNodeExporter.java new file mode 100644 index 00000000000..40a76bfd85c --- /dev/null +++ b/solr/core/src/java/org/apache/solr/metrics/prometheus/node/SolrPrometheusNodeExporter.java @@ -0,0 +1,104 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.solr.metrics.prometheus.node; + +import static org.apache.solr.metrics.prometheus.node.SolrNodeMetric.NODE_THREAD_POOL; + +import com.codahale.metrics.Counter; +import com.codahale.metrics.Meter; +import com.codahale.metrics.Metric; +import com.google.common.base.Enums; +import io.prometheus.metrics.model.snapshots.Labels; +import org.apache.solr.metrics.prometheus.SolrMetric; +import org.apache.solr.metrics.prometheus.SolrNoOpMetric; +import org.apache.solr.metrics.prometheus.SolrPrometheusExporter; + +/** + * This class maintains a {@link io.prometheus.metrics.model.snapshots.MetricSnapshot}s exported + * from solr.node {@link com.codahale.metrics.MetricRegistry} + */ +public class SolrPrometheusNodeExporter extends SolrPrometheusExporter + implements PrometheusNodeExporterInfo { + public SolrPrometheusNodeExporter() { + super(); + } + + @Override + public void exportDropwizardMetric(Metric dropwizardMetric, String metricName) { + if (metricName.contains(".threadPool.")) { + exportThreadPoolMetric(dropwizardMetric, metricName); + return; + } + + SolrMetric solrNodeMetric = categorizeMetric(dropwizardMetric, metricName); + solrNodeMetric.parseLabels().toPrometheus(this); + } + + @Override + public SolrMetric categorizeMetric(Metric dropwizardMetric, String metricName) { + String metricCategory = metricName.split("\\.", 2)[0]; + if (!Enums.getIfPresent(PrometheusNodeExporterInfo.NodeCategory.class, metricCategory) + .isPresent()) { + return new SolrNoOpMetric(); + } + switch (NodeCategory.valueOf(metricCategory)) { + case ADMIN: + case UPDATE: + return new SolrNodeHandlerMetric(dropwizardMetric, metricName); + case CONTAINER: + return new SolrNodeContainerMetric(dropwizardMetric, metricName); + default: + return new SolrNoOpMetric(); + } + } + + /* + * Metric examples being exported + * ADMIN./admin/cores.threadPool.parallelCoreAdminExecutor.completed + * CONTAINER.threadPool.coreContainerWorkExecutor.completed + */ + private void exportThreadPoolMetric(Metric dropwizardMetric, String metricName) { + Labels labels; + String[] parsedMetric = metricName.split("\\."); + if (parsedMetric.length >= 5) { + labels = + Labels.of( + "category", + parsedMetric[0], + "handler", + parsedMetric[1], + "executer", + parsedMetric[3], + "task", + parsedMetric[parsedMetric.length - 1]); + } else { + labels = + Labels.of( + "category", + parsedMetric[0], + "executer", + parsedMetric[2], + "task", + parsedMetric[parsedMetric.length - 1]); + } + if (dropwizardMetric instanceof Counter) { + exportCounter(NODE_THREAD_POOL, (Counter) dropwizardMetric, labels); + } else if (dropwizardMetric instanceof Meter) { + exportMeter(NODE_THREAD_POOL, (Meter) dropwizardMetric, labels); + } + } +} diff --git a/solr/core/src/java/org/apache/solr/metrics/prometheus/node/package-info.java b/solr/core/src/java/org/apache/solr/metrics/prometheus/node/package-info.java new file mode 100644 index 00000000000..31ca46347d0 --- /dev/null +++ b/solr/core/src/java/org/apache/solr/metrics/prometheus/node/package-info.java @@ -0,0 +1,22 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * The {@link org.apache.solr.metrics.prometheus.node.SolrPrometheusNodeExporter} is responsible for + * exporting solr.node registry metrics to Prometheus. + */ +package org.apache.solr.metrics.prometheus.node; diff --git a/solr/core/src/java/org/apache/solr/metrics/prometheus/package-info.java b/solr/core/src/java/org/apache/solr/metrics/prometheus/package-info.java new file mode 100644 index 00000000000..33da9290400 --- /dev/null +++ b/solr/core/src/java/org/apache/solr/metrics/prometheus/package-info.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * The {@link org.apache.solr.metrics.prometheus.SolrPrometheusExporter} is responsible for + * collecting Prometheus metrics from exporting {@link com.codahale.metrics.Metric}'s from {@link + * com.codahale.metrics.MetricRegistry} {@link org.apache.solr.metrics.prometheus.SolrMetric} is a + * wrapper to export {@link com.codahale.metrics.Metric} to {@link + * io.prometheus.metrics.model.snapshots.DataPointSnapshot} + */ +package org.apache.solr.metrics.prometheus; diff --git a/solr/core/src/java/org/apache/solr/response/PrometheusResponseWriter.java b/solr/core/src/java/org/apache/solr/response/PrometheusResponseWriter.java new file mode 100644 index 00000000000..e67b20a0007 --- /dev/null +++ b/solr/core/src/java/org/apache/solr/response/PrometheusResponseWriter.java @@ -0,0 +1,146 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.solr.response; + +import com.codahale.metrics.Histogram; +import com.codahale.metrics.Metric; +import com.codahale.metrics.MetricFilter; +import com.codahale.metrics.MetricRegistry; +import com.codahale.metrics.Timer; +import io.prometheus.metrics.expositionformats.PrometheusTextFormatWriter; +import java.io.IOException; +import java.io.OutputStream; +import java.lang.invoke.MethodHandles; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import org.apache.solr.common.util.NamedList; +import org.apache.solr.metrics.AggregateMetric; +import org.apache.solr.metrics.prometheus.SolrPrometheusExporter; +import org.apache.solr.metrics.prometheus.core.SolrPrometheusCoreExporter; +import org.apache.solr.metrics.prometheus.jetty.SolrPrometheusJettyExporter; +import org.apache.solr.metrics.prometheus.jvm.SolrPrometheusJvmExporter; +import org.apache.solr.metrics.prometheus.node.SolrPrometheusNodeExporter; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.util.stats.MetricUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Response writer for Prometheus metrics. This is used only by the {@link + * org.apache.solr.handler.admin.MetricsHandler} + */ +@SuppressWarnings(value = "unchecked") +public class PrometheusResponseWriter extends RawResponseWriter { + private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + + @Override + public void write(OutputStream out, SolrQueryRequest request, SolrQueryResponse response) + throws IOException { + NamedList prometheusRegistries = + (NamedList) response.getValues().get("metrics"); + var prometheusTextFormatWriter = new PrometheusTextFormatWriter(false); + for (Map.Entry prometheusRegistry : prometheusRegistries) { + var prometheusExporter = (SolrPrometheusExporter) prometheusRegistry.getValue(); + prometheusTextFormatWriter.write(out, prometheusExporter.collect()); + } + } + + /** + * Provides a representation of the given Dropwizard metric registry as {@link + * SolrPrometheusCoreExporter}-s. Only those metrics are converted which match at least one of the + * given MetricFilter instances. + * + * @param registry the {@link MetricRegistry} to be converted + * @param shouldMatchFilters a list of {@link MetricFilter} instances. A metric must match any + * one of the filters from this list to be included in the output + * @param mustMatchFilter a {@link MetricFilter}. A metric must match this filter to be + * included in the output. + * @param propertyFilter limit what properties of a metric are returned + * @param skipHistograms discard any {@link Histogram}-s and histogram parts of {@link Timer}-s. + * @param skipAggregateValues discard internal values of {@link AggregateMetric}-s. + * @param compact use compact representation for counters and gauges. + * @param consumer consumer that accepts produced {@link SolrPrometheusCoreExporter}-s + */ + public static void toPrometheus( + MetricRegistry registry, + String registryName, + List shouldMatchFilters, + MetricFilter mustMatchFilter, + Predicate propertyFilter, + boolean skipHistograms, + boolean skipAggregateValues, + boolean compact, + Consumer consumer) { + Map dropwizardMetrics = registry.getMetrics(); + var exporter = getExporterType(registryName); + if (exporter == null) { + return; + } + + MetricUtils.toMaps( + registry, + shouldMatchFilters, + mustMatchFilter, + propertyFilter, + skipHistograms, + skipAggregateValues, + compact, + false, + (metricName, metric) -> { + try { + Metric dropwizardMetric = dropwizardMetrics.get(metricName); + exporter.exportDropwizardMetric(dropwizardMetric, metricName); + } catch (Exception e) { + // Do not fail entirely for metrics exporting. Log and try to export next metric + log.warn("Error occurred exporting Dropwizard Metric to Prometheus", e); + } + }); + + consumer.accept(exporter); + } + + public static SolrPrometheusExporter getExporterType(String registryName) { + String coreName; + boolean cloudMode = false; + String[] parsedRegistry = registryName.split("\\."); + + switch (parsedRegistry[1]) { + case "core": + if (parsedRegistry.length == 3) { + coreName = parsedRegistry[2]; + } else if (parsedRegistry.length == 5) { + coreName = Arrays.stream(parsedRegistry).skip(1).collect(Collectors.joining("_")); + cloudMode = true; + } else { + coreName = registryName; + } + return new SolrPrometheusCoreExporter(coreName, cloudMode); + case "jvm": + return new SolrPrometheusJvmExporter(); + case "jetty": + return new SolrPrometheusJettyExporter(); + case "node": + return new SolrPrometheusNodeExporter(); + default: + return null; + } + } +} diff --git a/solr/core/src/test-files/prometheus/solr-prometheus-output.txt b/solr/core/src/test-files/prometheus/solr-prometheus-output.txt new file mode 100644 index 00000000000..825381fe4e8 --- /dev/null +++ b/solr/core/src/test-files/prometheus/solr-prometheus-output.txt @@ -0,0 +1,535 @@ +# TYPE solr_metrics_jvm_buffers gauge +solr_metrics_jvm_buffers{item="Count",pool="direct"} +solr_metrics_jvm_buffers{item="Count",pool="mapped"} +solr_metrics_jvm_buffers{item="Count",pool="mapped - 'non-volatile memory'"} +# TYPE solr_metrics_jvm_buffers_bytes gauge +solr_metrics_jvm_buffers_bytes{item="MemoryUsed",pool="direct"} +solr_metrics_jvm_buffers_bytes{item="MemoryUsed",pool="mapped"} +solr_metrics_jvm_buffers_bytes{item="MemoryUsed",pool="mapped - 'non-volatile memory'"} +solr_metrics_jvm_buffers_bytes{item="TotalCapacity",pool="direct"} +solr_metrics_jvm_buffers_bytes{item="TotalCapacity",pool="mapped"} +solr_metrics_jvm_buffers_bytes{item="TotalCapacity",pool="mapped - 'non-volatile memory'"} +# TYPE solr_metrics_jvm_gc gauge +solr_metrics_jvm_gc{} +solr_metrics_jvm_gc{} +# TYPE solr_metrics_jvm_gc_seconds gauge +solr_metrics_jvm_gc_seconds{} +solr_metrics_jvm_gc_seconds{} +# TYPE solr_metrics_jvm_heap gauge +solr_metrics_jvm_heap{item="committed",memory="heap"} +solr_metrics_jvm_heap{item="committed",memory="non-heap"} +solr_metrics_jvm_heap{item="committed",memory="total"} +solr_metrics_jvm_heap{item="init",memory="heap"} +solr_metrics_jvm_heap{item="init",memory="non-heap"} +solr_metrics_jvm_heap{item="init",memory="total"} +solr_metrics_jvm_heap{item="max",memory="heap"} +solr_metrics_jvm_heap{item="max",memory="non-heap"} +solr_metrics_jvm_heap{item="max",memory="total"} +solr_metrics_jvm_heap{item="usage",memory="heap"} +solr_metrics_jvm_heap{item="usage",memory="non-heap"} +solr_metrics_jvm_heap{item="used",memory="heap"} +solr_metrics_jvm_heap{item="used",memory="non-heap"} +solr_metrics_jvm_heap{item="used",memory="total"} +# TYPE solr_metrics_jvm_memory_pools_bytes gauge +solr_metrics_jvm_memory_pools_bytes{item="committed",space="CodeCache"} +solr_metrics_jvm_memory_pools_bytes{item="committed",space="Compressed-Class-Space"} +solr_metrics_jvm_memory_pools_bytes{item="committed",space="Metaspace"} +solr_metrics_jvm_memory_pools_bytes{item="committed",space="PS-Eden-Space"} +solr_metrics_jvm_memory_pools_bytes{item="committed",space="PS-Old-Gen"} +solr_metrics_jvm_memory_pools_bytes{item="committed",space="PS-Survivor-Space"} +solr_metrics_jvm_memory_pools_bytes{item="init",space="CodeCache"} +solr_metrics_jvm_memory_pools_bytes{item="init",space="Compressed-Class-Space"} +solr_metrics_jvm_memory_pools_bytes{item="init",space="Metaspace"} +solr_metrics_jvm_memory_pools_bytes{item="init",space="PS-Eden-Space"} +solr_metrics_jvm_memory_pools_bytes{item="init",space="PS-Old-Gen"} +solr_metrics_jvm_memory_pools_bytes{item="init",space="PS-Survivor-Space"} +solr_metrics_jvm_memory_pools_bytes{item="max",space="CodeCache"} +solr_metrics_jvm_memory_pools_bytes{item="max",space="Compressed-Class-Space"} +solr_metrics_jvm_memory_pools_bytes{item="max",space="Metaspace"} +solr_metrics_jvm_memory_pools_bytes{item="max",space="PS-Eden-Space"} +solr_metrics_jvm_memory_pools_bytes{item="max",space="PS-Old-Gen"} +solr_metrics_jvm_memory_pools_bytes{item="max",space="PS-Survivor-Space"} +solr_metrics_jvm_memory_pools_bytes{item="usage",space="CodeCache"} +solr_metrics_jvm_memory_pools_bytes{item="usage",space="Compressed-Class-Space"} +solr_metrics_jvm_memory_pools_bytes{item="usage",space="Metaspace"} +solr_metrics_jvm_memory_pools_bytes{item="usage",space="PS-Eden-Space"} +solr_metrics_jvm_memory_pools_bytes{item="usage",space="PS-Old-Gen"} +solr_metrics_jvm_memory_pools_bytes{item="usage",space="PS-Survivor-Space"} +solr_metrics_jvm_memory_pools_bytes{item="used",space="CodeCache"} +solr_metrics_jvm_memory_pools_bytes{item="used",space="Compressed-Class-Space"} +solr_metrics_jvm_memory_pools_bytes{item="used",space="Metaspace"} +solr_metrics_jvm_memory_pools_bytes{item="used",space="PS-Eden-Space"} +solr_metrics_jvm_memory_pools_bytes{item="used",space="PS-Old-Gen"} +solr_metrics_jvm_memory_pools_bytes{item="used",space="PS-Survivor-Space"} +solr_metrics_jvm_memory_pools_bytes{item="used-after-gc",space="PS-Eden-Space"} +solr_metrics_jvm_memory_pools_bytes{item="used-after-gc",space="PS-Old-Gen"} +solr_metrics_jvm_memory_pools_bytes{item="used-after-gc",space="PS-Survivor-Space"} +# TYPE solr_metrics_jvm_threads gauge +solr_metrics_jvm_threads{item="blocked"} +solr_metrics_jvm_threads{item="count"} +solr_metrics_jvm_threads{item="daemon"} +solr_metrics_jvm_threads{item="deadlock"} +solr_metrics_jvm_threads{item="new"} +solr_metrics_jvm_threads{item="peak"} +solr_metrics_jvm_threads{item="runnable"} +solr_metrics_jvm_threads{item="terminated"} +solr_metrics_jvm_threads{item="timed_waiting"} +solr_metrics_jvm_threads{item="total_started"} +solr_metrics_jvm_threads{item="waiting"} +# TYPE solr_metrics_os gauge +solr_metrics_os{item="availableProcessors"} +solr_metrics_os{item="committedVirtualMemorySize"} +solr_metrics_os{item="cpuLoad"} +solr_metrics_os{item="freeMemorySize"} +solr_metrics_os{item="freePhysicalMemorySize"} +solr_metrics_os{item="freeSwapSpaceSize"} +solr_metrics_os{item="maxFileDescriptorCount"} +solr_metrics_os{item="openFileDescriptorCount"} +solr_metrics_os{item="processCpuLoad"} +solr_metrics_os{item="processCpuTime"} +solr_metrics_os{item="systemCpuLoad"} +solr_metrics_os{item="systemLoadAverage"} +solr_metrics_os{item="totalMemorySize"} +solr_metrics_os{item="totalPhysicalMemorySize"} +solr_metrics_os{item="totalSwapSpaceSize"} +# TYPE solr_metrics_node_connections gauge +solr_metrics_node_connections{category="UPDATE",handler="updateShardHandler",type="availableConnections"} +solr_metrics_node_connections{category="UPDATE",handler="updateShardHandler",type="leasedConnections"} +solr_metrics_node_connections{category="UPDATE",handler="updateShardHandler",type="maxConnections"} +solr_metrics_node_connections{category="UPDATE",handler="updateShardHandler",type="pendingConnections"} +# TYPE solr_metrics_node_core_root_fs_bytes gauge +solr_metrics_node_core_root_fs_bytes{category="CONTAINER",item="totalSpace"} +solr_metrics_node_core_root_fs_bytes{category="CONTAINER",item="usableSpace"} +# TYPE solr_metrics_node_cores gauge +solr_metrics_node_cores{category="CONTAINER",item="lazy"} +solr_metrics_node_cores{category="CONTAINER",item="loaded"} +solr_metrics_node_cores{category="CONTAINER",item="unloaded"} +# TYPE solr_metrics_node_requests_total counter +solr_metrics_node_requests_total{category="ADMIN",handler="/admin/authorization",type="clientErrors"} +solr_metrics_node_requests_total{category="ADMIN",handler="/admin/authorization",type="errors"} +solr_metrics_node_requests_total{category="ADMIN",handler="/admin/authorization",type="requests"} +solr_metrics_node_requests_total{category="ADMIN",handler="/admin/authorization",type="serverErrors"} +solr_metrics_node_requests_total{category="ADMIN",handler="/admin/authorization",type="timeouts"} +solr_metrics_node_requests_total{category="ADMIN",handler="/admin/collections",type="clientErrors"} +solr_metrics_node_requests_total{category="ADMIN",handler="/admin/collections",type="errors"} +solr_metrics_node_requests_total{category="ADMIN",handler="/admin/collections",type="requests"} +solr_metrics_node_requests_total{category="ADMIN",handler="/admin/collections",type="serverErrors"} +solr_metrics_node_requests_total{category="ADMIN",handler="/admin/collections",type="timeouts"} +solr_metrics_node_requests_total{category="ADMIN",handler="/admin/configs",type="clientErrors"} +solr_metrics_node_requests_total{category="ADMIN",handler="/admin/configs",type="errors"} +solr_metrics_node_requests_total{category="ADMIN",handler="/admin/configs",type="requests"} +solr_metrics_node_requests_total{category="ADMIN",handler="/admin/configs",type="serverErrors"} +solr_metrics_node_requests_total{category="ADMIN",handler="/admin/configs",type="timeouts"} +solr_metrics_node_requests_total{category="ADMIN",handler="/admin/cores",type="clientErrors"} +solr_metrics_node_requests_total{category="ADMIN",handler="/admin/cores",type="errors"} +solr_metrics_node_requests_total{category="ADMIN",handler="/admin/cores",type="requests"} +solr_metrics_node_requests_total{category="ADMIN",handler="/admin/cores",type="serverErrors"} +solr_metrics_node_requests_total{category="ADMIN",handler="/admin/cores",type="timeouts"} +solr_metrics_node_requests_total{category="ADMIN",handler="/admin/info",type="clientErrors"} +solr_metrics_node_requests_total{category="ADMIN",handler="/admin/info",type="errors"} +solr_metrics_node_requests_total{category="ADMIN",handler="/admin/info",type="requests"} +solr_metrics_node_requests_total{category="ADMIN",handler="/admin/info",type="serverErrors"} +solr_metrics_node_requests_total{category="ADMIN",handler="/admin/info",type="timeouts"} +solr_metrics_node_requests_total{category="ADMIN",handler="/admin/metrics",type="clientErrors"} +solr_metrics_node_requests_total{category="ADMIN",handler="/admin/metrics",type="errors"} +solr_metrics_node_requests_total{category="ADMIN",handler="/admin/metrics",type="requests"} +solr_metrics_node_requests_total{category="ADMIN",handler="/admin/metrics",type="serverErrors"} +solr_metrics_node_requests_total{category="ADMIN",handler="/admin/metrics",type="timeouts"} +solr_metrics_node_requests_total{category="ADMIN",handler="/admin/zookeeper",type="clientErrors"} +solr_metrics_node_requests_total{category="ADMIN",handler="/admin/zookeeper",type="errors"} +solr_metrics_node_requests_total{category="ADMIN",handler="/admin/zookeeper",type="requests"} +solr_metrics_node_requests_total{category="ADMIN",handler="/admin/zookeeper",type="serverErrors"} +solr_metrics_node_requests_total{category="ADMIN",handler="/admin/zookeeper",type="timeouts"} +solr_metrics_node_requests_total{category="ADMIN",handler="/admin/zookeeper/status",type="clientErrors"} +solr_metrics_node_requests_total{category="ADMIN",handler="/admin/zookeeper/status",type="errors"} +solr_metrics_node_requests_total{category="ADMIN",handler="/admin/zookeeper/status",type="requests"} +solr_metrics_node_requests_total{category="ADMIN",handler="/admin/zookeeper/status",type="serverErrors"} +solr_metrics_node_requests_total{category="ADMIN",handler="/admin/zookeeper/status",type="timeouts"} +# TYPE solr_metrics_node_requests_time_total counter +solr_metrics_node_requests_time_total{category="ADMIN",handler="/admin/authorization"} +solr_metrics_node_requests_time_total{category="ADMIN",handler="/admin/collections"} +solr_metrics_node_requests_time_total{category="ADMIN",handler="/admin/configs"} +solr_metrics_node_requests_time_total{category="ADMIN",handler="/admin/cores"} +solr_metrics_node_requests_time_total{category="ADMIN",handler="/admin/info"} +solr_metrics_node_requests_time_total{category="ADMIN",handler="/admin/metrics"} +solr_metrics_node_requests_time_total{category="ADMIN",handler="/admin/zookeeper"} +solr_metrics_node_requests_time_total{category="ADMIN",handler="/admin/zookeeper/status"} +# TYPE solr_metrics_node_thread_pool_total counter +solr_metrics_node_thread_pool_total{category="ADMIN",executer="parallelCoreAdminExecutor",handler="/admin/cores",task="completed"} +solr_metrics_node_thread_pool_total{category="ADMIN",executer="parallelCoreAdminExecutor",handler="/admin/cores",task="running"} +solr_metrics_node_thread_pool_total{category="ADMIN",executer="parallelCoreAdminExecutor",handler="/admin/cores",task="submitted"} +solr_metrics_node_thread_pool_total{category="ADMIN",executer="parallelCoreExpensiveAdminExecutor",handler="/admin/cores",task="completed"} +solr_metrics_node_thread_pool_total{category="ADMIN",executer="parallelCoreExpensiveAdminExecutor",handler="/admin/cores",task="running"} +solr_metrics_node_thread_pool_total{category="ADMIN",executer="parallelCoreExpensiveAdminExecutor",handler="/admin/cores",task="submitted"} +solr_metrics_node_thread_pool_total{category="QUERY",executer="httpShardExecutor",handler="httpShardHandler",task="completed"} +solr_metrics_node_thread_pool_total{category="QUERY",executer="httpShardExecutor",handler="httpShardHandler",task="running"} +solr_metrics_node_thread_pool_total{category="QUERY",executer="httpShardExecutor",handler="httpShardHandler",task="submitted"} +solr_metrics_node_thread_pool_total{category="UPDATE",executer="recoveryExecutor",handler="updateShardHandler",task="completed"} +solr_metrics_node_thread_pool_total{category="UPDATE",executer="recoveryExecutor",handler="updateShardHandler",task="running"} +solr_metrics_node_thread_pool_total{category="UPDATE",executer="recoveryExecutor",handler="updateShardHandler",task="submitted"} +solr_metrics_node_thread_pool_total{category="UPDATE",executer="updateOnlyExecutor",handler="updateShardHandler",task="completed"} +solr_metrics_node_thread_pool_total{category="UPDATE",executer="updateOnlyExecutor",handler="updateShardHandler",task="running"} +solr_metrics_node_thread_pool_total{category="UPDATE",executer="updateOnlyExecutor",handler="updateShardHandler",task="submitted"} +solr_metrics_node_thread_pool_total{category="CONTAINER",executer="coreContainerWorkExecutor",task="completed"} +solr_metrics_node_thread_pool_total{category="CONTAINER",executer="coreContainerWorkExecutor",task="running"} +solr_metrics_node_thread_pool_total{category="CONTAINER",executer="coreContainerWorkExecutor",task="submitted"} +solr_metrics_node_thread_pool_total{category="CONTAINER",executer="coreLoadExecutor",task="completed"} +solr_metrics_node_thread_pool_total{category="CONTAINER",executer="coreLoadExecutor",task="running"} +solr_metrics_node_thread_pool_total{category="CONTAINER",executer="coreLoadExecutor",task="submitted"} +# TYPE solr_metrics_core_average_request_time gauge +solr_metrics_core_average_request_time{category="ADMIN",core="collection1",handler="/admin/file"} +solr_metrics_core_average_request_time{category="ADMIN",core="collection1",handler="/admin/luke"} +solr_metrics_core_average_request_time{category="ADMIN",core="collection1",handler="/admin/mbeans"} +solr_metrics_core_average_request_time{category="ADMIN",core="collection1",handler="/admin/ping"} +solr_metrics_core_average_request_time{category="ADMIN",core="collection1",handler="/admin/plugins"} +solr_metrics_core_average_request_time{category="ADMIN",core="collection1",handler="/admin/segments"} +solr_metrics_core_average_request_time{category="ADMIN",core="collection1",handler="/admin/system"} +solr_metrics_core_average_request_time{category="ADMIN",core="collection1",handler="/config"} +solr_metrics_core_average_request_time{category="ADMIN",core="collection1",handler="/schema"} +solr_metrics_core_average_request_time{category="ADMIN",core="collection1",handler="/tasks/cancel"} +solr_metrics_core_average_request_time{category="ADMIN",core="collection1",handler="/tasks/list"} +solr_metrics_core_average_request_time{category="QUERY",core="collection1",handler="/debug/dump"} +solr_metrics_core_average_request_time{category="QUERY",core="collection1",handler="/export"} +solr_metrics_core_average_request_time{category="QUERY",core="collection1",handler="/export[shard]"} +solr_metrics_core_average_request_time{category="QUERY",core="collection1",handler="/get"} +solr_metrics_core_average_request_time{category="QUERY",core="collection1",handler="/get[shard]"} +solr_metrics_core_average_request_time{category="QUERY",core="collection1",handler="/graph"} +solr_metrics_core_average_request_time{category="QUERY",core="collection1",handler="/query"} +solr_metrics_core_average_request_time{category="QUERY",core="collection1",handler="/query[shard]"} +solr_metrics_core_average_request_time{category="QUERY",core="collection1",handler="/select"} +solr_metrics_core_average_request_time{category="QUERY",core="collection1",handler="/select[shard]"} +solr_metrics_core_average_request_time{category="QUERY",core="collection1",handler="/stream"} +solr_metrics_core_average_request_time{category="QUERY",core="collection1",handler="/terms"} +solr_metrics_core_average_request_time{category="QUERY",core="collection1",handler="/terms[shard]"} +solr_metrics_core_average_request_time{category="REPLICATION",core="collection1",handler="/replication"} +solr_metrics_core_average_request_time{category="UPDATE",core="collection1",handler="/update"} +solr_metrics_core_average_request_time{category="UPDATE",core="collection1",handler="/update/cbor"} +solr_metrics_core_average_request_time{category="UPDATE",core="collection1",handler="/update/csv"} +solr_metrics_core_average_request_time{category="UPDATE",core="collection1",handler="/update/json"} +solr_metrics_core_average_request_time{category="UPDATE",core="collection1",handler="/update/json/docs"} +solr_metrics_core_average_request_time{category="UPDATE",core="collection1",handler="update"} +# TYPE solr_metrics_core_average_searcher_warmup_time gauge +solr_metrics_core_average_searcher_warmup_time{core="collection1",type="time"} +solr_metrics_core_average_searcher_warmup_time{core="collection1",type="warmup"} +# TYPE solr_metrics_core_cache gauge +solr_metrics_core_cache{cacheType="documentCache",core="collection1",item="cumulative_evictions"} +solr_metrics_core_cache{cacheType="documentCache",core="collection1",item="cumulative_hitratio"} +solr_metrics_core_cache{cacheType="documentCache",core="collection1",item="cumulative_hits"} +solr_metrics_core_cache{cacheType="documentCache",core="collection1",item="cumulative_inserts"} +solr_metrics_core_cache{cacheType="documentCache",core="collection1",item="cumulative_lookups"} +solr_metrics_core_cache{cacheType="documentCache",core="collection1",item="evictions"} +solr_metrics_core_cache{cacheType="documentCache",core="collection1",item="hitratio"} +solr_metrics_core_cache{cacheType="documentCache",core="collection1",item="hits"} +solr_metrics_core_cache{cacheType="documentCache",core="collection1",item="inserts"} +solr_metrics_core_cache{cacheType="documentCache",core="collection1",item="lookups"} +solr_metrics_core_cache{cacheType="documentCache",core="collection1",item="maxRamMB"} +solr_metrics_core_cache{cacheType="documentCache",core="collection1",item="ramBytesUsed"} +solr_metrics_core_cache{cacheType="documentCache",core="collection1",item="size"} +solr_metrics_core_cache{cacheType="documentCache",core="collection1",item="warmupTime"} +solr_metrics_core_cache{cacheType="fieldCache",core="collection1",item="entries_count"} +solr_metrics_core_cache{cacheType="fieldValueCache",core="collection1",item="cumulative_evictions"} +solr_metrics_core_cache{cacheType="fieldValueCache",core="collection1",item="cumulative_hitratio"} +solr_metrics_core_cache{cacheType="fieldValueCache",core="collection1",item="cumulative_hits"} +solr_metrics_core_cache{cacheType="fieldValueCache",core="collection1",item="cumulative_inserts"} +solr_metrics_core_cache{cacheType="fieldValueCache",core="collection1",item="cumulative_lookups"} +solr_metrics_core_cache{cacheType="fieldValueCache",core="collection1",item="evictions"} +solr_metrics_core_cache{cacheType="fieldValueCache",core="collection1",item="hitratio"} +solr_metrics_core_cache{cacheType="fieldValueCache",core="collection1",item="hits"} +solr_metrics_core_cache{cacheType="fieldValueCache",core="collection1",item="inserts"} +solr_metrics_core_cache{cacheType="fieldValueCache",core="collection1",item="lookups"} +solr_metrics_core_cache{cacheType="fieldValueCache",core="collection1",item="maxRamMB"} +solr_metrics_core_cache{cacheType="fieldValueCache",core="collection1",item="ramBytesUsed"} +solr_metrics_core_cache{cacheType="fieldValueCache",core="collection1",item="size"} +solr_metrics_core_cache{cacheType="fieldValueCache",core="collection1",item="warmupTime"} +solr_metrics_core_cache{cacheType="filterCache",core="collection1",item="cumulative_evictions"} +solr_metrics_core_cache{cacheType="filterCache",core="collection1",item="cumulative_hitratio"} +solr_metrics_core_cache{cacheType="filterCache",core="collection1",item="cumulative_hits"} +solr_metrics_core_cache{cacheType="filterCache",core="collection1",item="cumulative_inserts"} +solr_metrics_core_cache{cacheType="filterCache",core="collection1",item="cumulative_lookups"} +solr_metrics_core_cache{cacheType="filterCache",core="collection1",item="evictions"} +solr_metrics_core_cache{cacheType="filterCache",core="collection1",item="hitratio"} +solr_metrics_core_cache{cacheType="filterCache",core="collection1",item="hits"} +solr_metrics_core_cache{cacheType="filterCache",core="collection1",item="inserts"} +solr_metrics_core_cache{cacheType="filterCache",core="collection1",item="lookups"} +solr_metrics_core_cache{cacheType="filterCache",core="collection1",item="maxRamMB"} +solr_metrics_core_cache{cacheType="filterCache",core="collection1",item="ramBytesUsed"} +solr_metrics_core_cache{cacheType="filterCache",core="collection1",item="size"} +solr_metrics_core_cache{cacheType="filterCache",core="collection1",item="warmupTime"} +solr_metrics_core_cache{cacheType="perSegFilter",core="collection1",item="cumulative_evictions"} +solr_metrics_core_cache{cacheType="perSegFilter",core="collection1",item="cumulative_hitratio"} +solr_metrics_core_cache{cacheType="perSegFilter",core="collection1",item="cumulative_hits"} +solr_metrics_core_cache{cacheType="perSegFilter",core="collection1",item="cumulative_inserts"} +solr_metrics_core_cache{cacheType="perSegFilter",core="collection1",item="cumulative_lookups"} +solr_metrics_core_cache{cacheType="perSegFilter",core="collection1",item="evictions"} +solr_metrics_core_cache{cacheType="perSegFilter",core="collection1",item="hitratio"} +solr_metrics_core_cache{cacheType="perSegFilter",core="collection1",item="hits"} +solr_metrics_core_cache{cacheType="perSegFilter",core="collection1",item="inserts"} +solr_metrics_core_cache{cacheType="perSegFilter",core="collection1",item="lookups"} +solr_metrics_core_cache{cacheType="perSegFilter",core="collection1",item="maxRamMB"} +solr_metrics_core_cache{cacheType="perSegFilter",core="collection1",item="ramBytesUsed"} +solr_metrics_core_cache{cacheType="perSegFilter",core="collection1",item="size"} +solr_metrics_core_cache{cacheType="perSegFilter",core="collection1",item="warmupTime"} +solr_metrics_core_cache{cacheType="queryResultCache",core="collection1",item="cumulative_evictions"} +solr_metrics_core_cache{cacheType="queryResultCache",core="collection1",item="cumulative_hitratio"} +solr_metrics_core_cache{cacheType="queryResultCache",core="collection1",item="cumulative_hits"} +solr_metrics_core_cache{cacheType="queryResultCache",core="collection1",item="cumulative_inserts"} +solr_metrics_core_cache{cacheType="queryResultCache",core="collection1",item="cumulative_lookups"} +solr_metrics_core_cache{cacheType="queryResultCache",core="collection1",item="evictions"} +solr_metrics_core_cache{cacheType="queryResultCache",core="collection1",item="hitratio"} +solr_metrics_core_cache{cacheType="queryResultCache",core="collection1",item="hits"} +solr_metrics_core_cache{cacheType="queryResultCache",core="collection1",item="inserts"} +solr_metrics_core_cache{cacheType="queryResultCache",core="collection1",item="lookups"} +solr_metrics_core_cache{cacheType="queryResultCache",core="collection1",item="maxRamMB"} +solr_metrics_core_cache{cacheType="queryResultCache",core="collection1",item="ramBytesUsed"} +solr_metrics_core_cache{cacheType="queryResultCache",core="collection1",item="size"} +solr_metrics_core_cache{cacheType="queryResultCache",core="collection1",item="warmupTime"} +solr_metrics_core_cache{cacheType="statsCache",core="collection1",item="lookups"} +solr_metrics_core_cache{cacheType="statsCache",core="collection1",item="mergeToGlobalStats"} +solr_metrics_core_cache{cacheType="statsCache",core="collection1",item="missingGlobalFieldStats"} +solr_metrics_core_cache{cacheType="statsCache",core="collection1",item="missingGlobalTermStats"} +solr_metrics_core_cache{cacheType="statsCache",core="collection1",item="receiveGlobalStats"} +solr_metrics_core_cache{cacheType="statsCache",core="collection1",item="retrieveStats"} +solr_metrics_core_cache{cacheType="statsCache",core="collection1",item="returnLocalStats"} +solr_metrics_core_cache{cacheType="statsCache",core="collection1",item="sendGlobalStats"} +solr_metrics_core_cache{cacheType="statsCache",core="collection1",item="useCachedGlobalStats"} +solr_metrics_core_cache{core="collection1",item="hits",type="liveDocsCache"} +solr_metrics_core_cache{core="collection1",item="inserts",type="liveDocsCache"} +solr_metrics_core_cache{core="collection1",item="naiveHits",type="liveDocsCache"} +# TYPE solr_metrics_core_highlighter_requests_total counter +solr_metrics_core_highlighter_requests_total{core="collection1",item="breakIterator",type="SolrBoundaryScanner"} +solr_metrics_core_highlighter_requests_total{core="collection1",item="colored",type="SolrFragmentsBuilder"} +solr_metrics_core_highlighter_requests_total{core="collection1",item="default",type="SolrBoundaryScanner"} +solr_metrics_core_highlighter_requests_total{core="collection1",item="default",type="SolrEncoder"} +solr_metrics_core_highlighter_requests_total{core="collection1",item="default",type="SolrFormatter"} +solr_metrics_core_highlighter_requests_total{core="collection1",item="default",type="SolrFragListBuilder"} +solr_metrics_core_highlighter_requests_total{core="collection1",item="default",type="SolrFragmenter"} +solr_metrics_core_highlighter_requests_total{core="collection1",item="default",type="SolrFragmentsBuilder"} +solr_metrics_core_highlighter_requests_total{core="collection1",item="gap",type="SolrFragmenter"} +solr_metrics_core_highlighter_requests_total{core="collection1",item="html",type="SolrEncoder"} +solr_metrics_core_highlighter_requests_total{core="collection1",item="html",type="SolrFormatter"} +solr_metrics_core_highlighter_requests_total{core="collection1",item="regex",type="SolrFragmenter"} +solr_metrics_core_highlighter_requests_total{core="collection1",item="simple",type="SolrFragListBuilder"} +solr_metrics_core_highlighter_requests_total{core="collection1",item="single",type="SolrFragListBuilder"} +solr_metrics_core_highlighter_requests_total{core="collection1",item="weighted",type="SolrFragListBuilder"} +# TYPE solr_metrics_core_index_size_bytes gauge +solr_metrics_core_index_size_bytes{core="collection1"} +# TYPE solr_metrics_core_requests_total counter +solr_metrics_core_requests_total{category="ADMIN",core="collection1",handler="/admin/file",type="clientErrors"} +solr_metrics_core_requests_total{category="ADMIN",core="collection1",handler="/admin/file",type="errors"} +solr_metrics_core_requests_total{category="ADMIN",core="collection1",handler="/admin/file",type="requests"} +solr_metrics_core_requests_total{category="ADMIN",core="collection1",handler="/admin/file",type="serverErrors"} +solr_metrics_core_requests_total{category="ADMIN",core="collection1",handler="/admin/file",type="timeouts"} +solr_metrics_core_requests_total{category="ADMIN",core="collection1",handler="/admin/luke",type="clientErrors"} +solr_metrics_core_requests_total{category="ADMIN",core="collection1",handler="/admin/luke",type="errors"} +solr_metrics_core_requests_total{category="ADMIN",core="collection1",handler="/admin/luke",type="requests"} +solr_metrics_core_requests_total{category="ADMIN",core="collection1",handler="/admin/luke",type="serverErrors"} +solr_metrics_core_requests_total{category="ADMIN",core="collection1",handler="/admin/luke",type="timeouts"} +solr_metrics_core_requests_total{category="ADMIN",core="collection1",handler="/admin/mbeans",type="clientErrors"} +solr_metrics_core_requests_total{category="ADMIN",core="collection1",handler="/admin/mbeans",type="errors"} +solr_metrics_core_requests_total{category="ADMIN",core="collection1",handler="/admin/mbeans",type="requests"} +solr_metrics_core_requests_total{category="ADMIN",core="collection1",handler="/admin/mbeans",type="serverErrors"} +solr_metrics_core_requests_total{category="ADMIN",core="collection1",handler="/admin/mbeans",type="timeouts"} +solr_metrics_core_requests_total{category="ADMIN",core="collection1",handler="/admin/ping",type="clientErrors"} +solr_metrics_core_requests_total{category="ADMIN",core="collection1",handler="/admin/ping",type="errors"} +solr_metrics_core_requests_total{category="ADMIN",core="collection1",handler="/admin/ping",type="requests"} +solr_metrics_core_requests_total{category="ADMIN",core="collection1",handler="/admin/ping",type="serverErrors"} +solr_metrics_core_requests_total{category="ADMIN",core="collection1",handler="/admin/ping",type="timeouts"} +solr_metrics_core_requests_total{category="ADMIN",core="collection1",handler="/admin/plugins",type="clientErrors"} +solr_metrics_core_requests_total{category="ADMIN",core="collection1",handler="/admin/plugins",type="errors"} +solr_metrics_core_requests_total{category="ADMIN",core="collection1",handler="/admin/plugins",type="requests"} +solr_metrics_core_requests_total{category="ADMIN",core="collection1",handler="/admin/plugins",type="serverErrors"} +solr_metrics_core_requests_total{category="ADMIN",core="collection1",handler="/admin/plugins",type="timeouts"} +solr_metrics_core_requests_total{category="ADMIN",core="collection1",handler="/admin/segments",type="clientErrors"} +solr_metrics_core_requests_total{category="ADMIN",core="collection1",handler="/admin/segments",type="errors"} +solr_metrics_core_requests_total{category="ADMIN",core="collection1",handler="/admin/segments",type="requests"} +solr_metrics_core_requests_total{category="ADMIN",core="collection1",handler="/admin/segments",type="serverErrors"} +solr_metrics_core_requests_total{category="ADMIN",core="collection1",handler="/admin/segments",type="timeouts"} +solr_metrics_core_requests_total{category="ADMIN",core="collection1",handler="/admin/system",type="clientErrors"} +solr_metrics_core_requests_total{category="ADMIN",core="collection1",handler="/admin/system",type="errors"} +solr_metrics_core_requests_total{category="ADMIN",core="collection1",handler="/admin/system",type="requests"} +solr_metrics_core_requests_total{category="ADMIN",core="collection1",handler="/admin/system",type="serverErrors"} +solr_metrics_core_requests_total{category="ADMIN",core="collection1",handler="/admin/system",type="timeouts"} +solr_metrics_core_requests_total{category="ADMIN",core="collection1",handler="/config",type="clientErrors"} +solr_metrics_core_requests_total{category="ADMIN",core="collection1",handler="/config",type="errors"} +solr_metrics_core_requests_total{category="ADMIN",core="collection1",handler="/config",type="requests"} +solr_metrics_core_requests_total{category="ADMIN",core="collection1",handler="/config",type="serverErrors"} +solr_metrics_core_requests_total{category="ADMIN",core="collection1",handler="/config",type="timeouts"} +solr_metrics_core_requests_total{category="ADMIN",core="collection1",handler="/schema",type="clientErrors"} +solr_metrics_core_requests_total{category="ADMIN",core="collection1",handler="/schema",type="errors"} +solr_metrics_core_requests_total{category="ADMIN",core="collection1",handler="/schema",type="requests"} +solr_metrics_core_requests_total{category="ADMIN",core="collection1",handler="/schema",type="serverErrors"} +solr_metrics_core_requests_total{category="ADMIN",core="collection1",handler="/schema",type="timeouts"} +solr_metrics_core_requests_total{category="ADMIN",core="collection1",handler="/tasks/cancel",type="clientErrors"} +solr_metrics_core_requests_total{category="ADMIN",core="collection1",handler="/tasks/cancel",type="errors"} +solr_metrics_core_requests_total{category="ADMIN",core="collection1",handler="/tasks/cancel",type="requests"} +solr_metrics_core_requests_total{category="ADMIN",core="collection1",handler="/tasks/cancel",type="serverErrors"} +solr_metrics_core_requests_total{category="ADMIN",core="collection1",handler="/tasks/cancel",type="timeouts"} +solr_metrics_core_requests_total{category="ADMIN",core="collection1",handler="/tasks/list",type="clientErrors"} +solr_metrics_core_requests_total{category="ADMIN",core="collection1",handler="/tasks/list",type="errors"} +solr_metrics_core_requests_total{category="ADMIN",core="collection1",handler="/tasks/list",type="requests"} +solr_metrics_core_requests_total{category="ADMIN",core="collection1",handler="/tasks/list",type="serverErrors"} +solr_metrics_core_requests_total{category="ADMIN",core="collection1",handler="/tasks/list",type="timeouts"} +solr_metrics_core_requests_total{category="QUERY",core="collection1",handler="/debug/dump",type="clientErrors"} +solr_metrics_core_requests_total{category="QUERY",core="collection1",handler="/debug/dump",type="errors"} +solr_metrics_core_requests_total{category="QUERY",core="collection1",handler="/debug/dump",type="requests"} +solr_metrics_core_requests_total{category="QUERY",core="collection1",handler="/debug/dump",type="serverErrors"} +solr_metrics_core_requests_total{category="QUERY",core="collection1",handler="/debug/dump",type="timeouts"} +solr_metrics_core_requests_total{category="QUERY",core="collection1",handler="/export",type="clientErrors"} +solr_metrics_core_requests_total{category="QUERY",core="collection1",handler="/export",type="errors"} +solr_metrics_core_requests_total{category="QUERY",core="collection1",handler="/export",type="requests"} +solr_metrics_core_requests_total{category="QUERY",core="collection1",handler="/export",type="serverErrors"} +solr_metrics_core_requests_total{category="QUERY",core="collection1",handler="/export",type="timeouts"} +solr_metrics_core_requests_total{category="QUERY",core="collection1",handler="/export[shard]",type="clientErrors"} +solr_metrics_core_requests_total{category="QUERY",core="collection1",handler="/export[shard]",type="errors"} +solr_metrics_core_requests_total{category="QUERY",core="collection1",handler="/export[shard]",type="requests"} +solr_metrics_core_requests_total{category="QUERY",core="collection1",handler="/export[shard]",type="serverErrors"} +solr_metrics_core_requests_total{category="QUERY",core="collection1",handler="/export[shard]",type="timeouts"} +solr_metrics_core_requests_total{category="QUERY",core="collection1",handler="/get",type="clientErrors"} +solr_metrics_core_requests_total{category="QUERY",core="collection1",handler="/get",type="errors"} +solr_metrics_core_requests_total{category="QUERY",core="collection1",handler="/get",type="requests"} +solr_metrics_core_requests_total{category="QUERY",core="collection1",handler="/get",type="serverErrors"} +solr_metrics_core_requests_total{category="QUERY",core="collection1",handler="/get",type="timeouts"} +solr_metrics_core_requests_total{category="QUERY",core="collection1",handler="/get[shard]",type="clientErrors"} +solr_metrics_core_requests_total{category="QUERY",core="collection1",handler="/get[shard]",type="errors"} +solr_metrics_core_requests_total{category="QUERY",core="collection1",handler="/get[shard]",type="requests"} +solr_metrics_core_requests_total{category="QUERY",core="collection1",handler="/get[shard]",type="serverErrors"} +solr_metrics_core_requests_total{category="QUERY",core="collection1",handler="/get[shard]",type="timeouts"} +solr_metrics_core_requests_total{category="QUERY",core="collection1",handler="/graph",type="clientErrors"} +solr_metrics_core_requests_total{category="QUERY",core="collection1",handler="/graph",type="errors"} +solr_metrics_core_requests_total{category="QUERY",core="collection1",handler="/graph",type="requests"} +solr_metrics_core_requests_total{category="QUERY",core="collection1",handler="/graph",type="serverErrors"} +solr_metrics_core_requests_total{category="QUERY",core="collection1",handler="/graph",type="timeouts"} +solr_metrics_core_requests_total{category="QUERY",core="collection1",handler="/query",type="clientErrors"} +solr_metrics_core_requests_total{category="QUERY",core="collection1",handler="/query",type="errors"} +solr_metrics_core_requests_total{category="QUERY",core="collection1",handler="/query",type="requests"} +solr_metrics_core_requests_total{category="QUERY",core="collection1",handler="/query",type="serverErrors"} +solr_metrics_core_requests_total{category="QUERY",core="collection1",handler="/query",type="timeouts"} +solr_metrics_core_requests_total{category="QUERY",core="collection1",handler="/query[shard]",type="clientErrors"} +solr_metrics_core_requests_total{category="QUERY",core="collection1",handler="/query[shard]",type="errors"} +solr_metrics_core_requests_total{category="QUERY",core="collection1",handler="/query[shard]",type="requests"} +solr_metrics_core_requests_total{category="QUERY",core="collection1",handler="/query[shard]",type="serverErrors"} +solr_metrics_core_requests_total{category="QUERY",core="collection1",handler="/query[shard]",type="timeouts"} +solr_metrics_core_requests_total{category="QUERY",core="collection1",handler="/select",type="clientErrors"} +solr_metrics_core_requests_total{category="QUERY",core="collection1",handler="/select",type="errors"} +solr_metrics_core_requests_total{category="QUERY",core="collection1",handler="/select",type="requests"} +solr_metrics_core_requests_total{category="QUERY",core="collection1",handler="/select",type="serverErrors"} +solr_metrics_core_requests_total{category="QUERY",core="collection1",handler="/select",type="timeouts"} +solr_metrics_core_requests_total{category="QUERY",core="collection1",handler="/select[shard]",type="clientErrors"} +solr_metrics_core_requests_total{category="QUERY",core="collection1",handler="/select[shard]",type="errors"} +solr_metrics_core_requests_total{category="QUERY",core="collection1",handler="/select[shard]",type="requests"} +solr_metrics_core_requests_total{category="QUERY",core="collection1",handler="/select[shard]",type="serverErrors"} +solr_metrics_core_requests_total{category="QUERY",core="collection1",handler="/select[shard]",type="timeouts"} +solr_metrics_core_requests_total{category="QUERY",core="collection1",handler="/stream",type="clientErrors"} +solr_metrics_core_requests_total{category="QUERY",core="collection1",handler="/stream",type="errors"} +solr_metrics_core_requests_total{category="QUERY",core="collection1",handler="/stream",type="requests"} +solr_metrics_core_requests_total{category="QUERY",core="collection1",handler="/stream",type="serverErrors"} +solr_metrics_core_requests_total{category="QUERY",core="collection1",handler="/stream",type="timeouts"} +solr_metrics_core_requests_total{category="QUERY",core="collection1",handler="/terms",type="clientErrors"} +solr_metrics_core_requests_total{category="QUERY",core="collection1",handler="/terms",type="errors"} +solr_metrics_core_requests_total{category="QUERY",core="collection1",handler="/terms",type="requests"} +solr_metrics_core_requests_total{category="QUERY",core="collection1",handler="/terms",type="serverErrors"} +solr_metrics_core_requests_total{category="QUERY",core="collection1",handler="/terms",type="timeouts"} +solr_metrics_core_requests_total{category="QUERY",core="collection1",handler="/terms[shard]",type="clientErrors"} +solr_metrics_core_requests_total{category="QUERY",core="collection1",handler="/terms[shard]",type="errors"} +solr_metrics_core_requests_total{category="QUERY",core="collection1",handler="/terms[shard]",type="requests"} +solr_metrics_core_requests_total{category="QUERY",core="collection1",handler="/terms[shard]",type="serverErrors"} +solr_metrics_core_requests_total{category="QUERY",core="collection1",handler="/terms[shard]",type="timeouts"} +solr_metrics_core_requests_total{category="REPLICATION",core="collection1",handler="/replication",type="clientErrors"} +solr_metrics_core_requests_total{category="REPLICATION",core="collection1",handler="/replication",type="errors"} +solr_metrics_core_requests_total{category="REPLICATION",core="collection1",handler="/replication",type="requests"} +solr_metrics_core_requests_total{category="REPLICATION",core="collection1",handler="/replication",type="serverErrors"} +solr_metrics_core_requests_total{category="REPLICATION",core="collection1",handler="/replication",type="timeouts"} +solr_metrics_core_requests_total{category="UPDATE",core="collection1",handler="/update",type="clientErrors"} +solr_metrics_core_requests_total{category="UPDATE",core="collection1",handler="/update",type="errors"} +solr_metrics_core_requests_total{category="UPDATE",core="collection1",handler="/update",type="requests"} +solr_metrics_core_requests_total{category="UPDATE",core="collection1",handler="/update",type="serverErrors"} +solr_metrics_core_requests_total{category="UPDATE",core="collection1",handler="/update",type="timeouts"} +solr_metrics_core_requests_total{category="UPDATE",core="collection1",handler="/update/cbor",type="clientErrors"} +solr_metrics_core_requests_total{category="UPDATE",core="collection1",handler="/update/cbor",type="errors"} +solr_metrics_core_requests_total{category="UPDATE",core="collection1",handler="/update/cbor",type="requests"} +solr_metrics_core_requests_total{category="UPDATE",core="collection1",handler="/update/cbor",type="serverErrors"} +solr_metrics_core_requests_total{category="UPDATE",core="collection1",handler="/update/cbor",type="timeouts"} +solr_metrics_core_requests_total{category="UPDATE",core="collection1",handler="/update/csv",type="clientErrors"} +solr_metrics_core_requests_total{category="UPDATE",core="collection1",handler="/update/csv",type="errors"} +solr_metrics_core_requests_total{category="UPDATE",core="collection1",handler="/update/csv",type="requests"} +solr_metrics_core_requests_total{category="UPDATE",core="collection1",handler="/update/csv",type="serverErrors"} +solr_metrics_core_requests_total{category="UPDATE",core="collection1",handler="/update/csv",type="timeouts"} +solr_metrics_core_requests_total{category="UPDATE",core="collection1",handler="/update/json",type="clientErrors"} +solr_metrics_core_requests_total{category="UPDATE",core="collection1",handler="/update/json",type="errors"} +solr_metrics_core_requests_total{category="UPDATE",core="collection1",handler="/update/json",type="requests"} +solr_metrics_core_requests_total{category="UPDATE",core="collection1",handler="/update/json",type="serverErrors"} +solr_metrics_core_requests_total{category="UPDATE",core="collection1",handler="/update/json",type="timeouts"} +solr_metrics_core_requests_total{category="UPDATE",core="collection1",handler="/update/json/docs",type="clientErrors"} +solr_metrics_core_requests_total{category="UPDATE",core="collection1",handler="/update/json/docs",type="errors"} +solr_metrics_core_requests_total{category="UPDATE",core="collection1",handler="/update/json/docs",type="requests"} +solr_metrics_core_requests_total{category="UPDATE",core="collection1",handler="/update/json/docs",type="serverErrors"} +solr_metrics_core_requests_total{category="UPDATE",core="collection1",handler="/update/json/docs",type="timeouts"} +solr_metrics_core_requests_total{category="UPDATE",core="collection1",handler="update",type="clientErrors"} +solr_metrics_core_requests_total{category="UPDATE",core="collection1",handler="update",type="errors"} +solr_metrics_core_requests_total{category="UPDATE",core="collection1",handler="update",type="requests"} +solr_metrics_core_requests_total{category="UPDATE",core="collection1",handler="update",type="serverErrors"} +solr_metrics_core_requests_total{category="UPDATE",core="collection1",handler="update",type="timeouts"} +solr_metrics_core_requests_total{category="UPDATE",core="collection1",handler="updateHandler",type="commits"} +solr_metrics_core_requests_total{category="UPDATE",core="collection1",handler="updateHandler",type="cumulativeAdds"} +solr_metrics_core_requests_total{category="UPDATE",core="collection1",handler="updateHandler",type="cumulativeDeletesById"} +solr_metrics_core_requests_total{category="UPDATE",core="collection1",handler="updateHandler",type="cumulativeDeletesByQuery"} +solr_metrics_core_requests_total{category="UPDATE",core="collection1",handler="updateHandler",type="cumulativeErrors"} +solr_metrics_core_requests_total{category="UPDATE",core="collection1",handler="updateHandler",type="expungeDeletes"} +solr_metrics_core_requests_total{category="UPDATE",core="collection1",handler="updateHandler",type="merges"} +solr_metrics_core_requests_total{category="UPDATE",core="collection1",handler="updateHandler",type="optimizes"} +solr_metrics_core_requests_total{category="UPDATE",core="collection1",handler="updateHandler",type="rollbacks"} +solr_metrics_core_requests_total{category="UPDATE",core="collection1",handler="updateHandler",type="splits"} +# TYPE solr_metrics_core_requests_time_total counter +solr_metrics_core_requests_time_total{category="ADMIN",core="collection1",handler="/admin/file"} +solr_metrics_core_requests_time_total{category="ADMIN",core="collection1",handler="/admin/luke"} +solr_metrics_core_requests_time_total{category="ADMIN",core="collection1",handler="/admin/mbeans"} +solr_metrics_core_requests_time_total{category="ADMIN",core="collection1",handler="/admin/ping"} +solr_metrics_core_requests_time_total{category="ADMIN",core="collection1",handler="/admin/plugins"} +solr_metrics_core_requests_time_total{category="ADMIN",core="collection1",handler="/admin/segments"} +solr_metrics_core_requests_time_total{category="ADMIN",core="collection1",handler="/admin/system"} +solr_metrics_core_requests_time_total{category="ADMIN",core="collection1",handler="/config"} +solr_metrics_core_requests_time_total{category="ADMIN",core="collection1",handler="/schema"} +solr_metrics_core_requests_time_total{category="ADMIN",core="collection1",handler="/tasks/cancel"} +solr_metrics_core_requests_time_total{category="ADMIN",core="collection1",handler="/tasks/list"} +solr_metrics_core_requests_time_total{category="QUERY",core="collection1",handler="/debug/dump"} +solr_metrics_core_requests_time_total{category="QUERY",core="collection1",handler="/export"} +solr_metrics_core_requests_time_total{category="QUERY",core="collection1",handler="/export[shard]"} +solr_metrics_core_requests_time_total{category="QUERY",core="collection1",handler="/get"} +solr_metrics_core_requests_time_total{category="QUERY",core="collection1",handler="/get[shard]"} +solr_metrics_core_requests_time_total{category="QUERY",core="collection1",handler="/graph"} +solr_metrics_core_requests_time_total{category="QUERY",core="collection1",handler="/query"} +solr_metrics_core_requests_time_total{category="QUERY",core="collection1",handler="/query[shard]"} +solr_metrics_core_requests_time_total{category="QUERY",core="collection1",handler="/select"} +solr_metrics_core_requests_time_total{category="QUERY",core="collection1",handler="/select[shard]"} +solr_metrics_core_requests_time_total{category="QUERY",core="collection1",handler="/stream"} +solr_metrics_core_requests_time_total{category="QUERY",core="collection1",handler="/terms"} +solr_metrics_core_requests_time_total{category="QUERY",core="collection1",handler="/terms[shard]"} +solr_metrics_core_requests_time_total{category="REPLICATION",core="collection1",handler="/replication"} +solr_metrics_core_requests_time_total{category="UPDATE",core="collection1",handler="/update"} +solr_metrics_core_requests_time_total{category="UPDATE",core="collection1",handler="/update/cbor"} +solr_metrics_core_requests_time_total{category="UPDATE",core="collection1",handler="/update/csv"} +solr_metrics_core_requests_time_total{category="UPDATE",core="collection1",handler="/update/json"} +solr_metrics_core_requests_time_total{category="UPDATE",core="collection1",handler="/update/json/docs"} +solr_metrics_core_requests_time_total{category="UPDATE",core="collection1",handler="update"} +# TYPE solr_metrics_core_searcher_documents gauge +solr_metrics_core_searcher_documents{core="collection1",type="deletedDocs"} +solr_metrics_core_searcher_documents{core="collection1",type="fullSortCount"} +solr_metrics_core_searcher_documents{core="collection1",type="indexCommitSize"} +solr_metrics_core_searcher_documents{core="collection1",type="indexVersion"} +solr_metrics_core_searcher_documents{core="collection1",type="maxDoc"} +solr_metrics_core_searcher_documents{core="collection1",type="numDocs"} +solr_metrics_core_searcher_documents{core="collection1",type="skipSortCount"} +solr_metrics_core_searcher_documents{core="collection1",type="warmupTime"} +# TYPE solr_metrics_core_tlog_total counter +solr_metrics_core_tlog_total{core="collection1",item="applyingBuffered"} +solr_metrics_core_tlog_total{core="collection1",item="copyOverOldUpdates"} +solr_metrics_core_tlog_total{core="collection1",item="replay"} +# TYPE solr_metrics_core_update_handler gauge +solr_metrics_core_update_handler{category="REPLICATION",core="collection1",handler="/replication",type="generation"} +solr_metrics_core_update_handler{category="UPDATE",core="collection1",handler="updateHandler",type="adds"} +solr_metrics_core_update_handler{category="UPDATE",core="collection1",handler="updateHandler",type="autoCommits"} +solr_metrics_core_update_handler{category="UPDATE",core="collection1",handler="updateHandler",type="deletesById"} +solr_metrics_core_update_handler{category="UPDATE",core="collection1",handler="updateHandler",type="deletesByQuery"} +solr_metrics_core_update_handler{category="UPDATE",core="collection1",handler="updateHandler",type="docsPending"} +solr_metrics_core_update_handler{category="UPDATE",core="collection1",handler="updateHandler",type="errors"} +solr_metrics_core_update_handler{category="UPDATE",core="collection1",handler="updateHandler",type="softAutoCommits"} diff --git a/solr/core/src/test/org/apache/solr/handler/admin/MetricsHandlerTest.java b/solr/core/src/test/org/apache/solr/handler/admin/MetricsHandlerTest.java index c2ccdb43baf..217c07433e4 100644 --- a/solr/core/src/test/org/apache/solr/handler/admin/MetricsHandlerTest.java +++ b/solr/core/src/test/org/apache/solr/handler/admin/MetricsHandlerTest.java @@ -18,6 +18,13 @@ package org.apache.solr.handler.admin; import com.codahale.metrics.Counter; +import com.codahale.metrics.Gauge; +import com.codahale.metrics.SettableGauge; +import io.prometheus.metrics.model.snapshots.CounterSnapshot; +import io.prometheus.metrics.model.snapshots.GaugeSnapshot; +import io.prometheus.metrics.model.snapshots.Labels; +import io.prometheus.metrics.model.snapshots.MetricSnapshot; +import io.prometheus.metrics.model.snapshots.MetricSnapshots; import java.util.Arrays; import java.util.HashMap; import java.util.Map; @@ -31,7 +38,9 @@ import org.apache.solr.core.SolrCore; import org.apache.solr.handler.RequestHandlerBase; import org.apache.solr.metrics.MetricsMap; +import org.apache.solr.metrics.SolrMetricManager; import org.apache.solr.metrics.SolrMetricsContext; +import org.apache.solr.metrics.prometheus.SolrPrometheusExporter; import org.apache.solr.request.SolrQueryRequest; import org.apache.solr.request.SolrRequestHandler; import org.apache.solr.response.SolrQueryResponse; @@ -57,6 +66,26 @@ public static void beforeClass() throws Exception { // test escapes c = h.getCoreContainer().getMetricManager().counter(null, "solr.jetty", "solrtest_foo:bar"); c.inc(3); + + // Manually register for Prometheus exporter tests + registerGauge("solr.jvm", "gc.G1-Old-Generation.count"); + registerGauge("solr.jvm", "gc.G1-Old-Generation.time"); + registerGauge("solr.jvm", "memory.heap.committed"); + registerGauge("solr.jvm", "memory.pools.CodeHeap-'non-nmethods'.committed"); + registerGauge("solr.jvm", "threads.count"); + registerGauge("solr.jvm", "os.availableProcessors"); + registerGauge("solr.jvm", "buffers.direct.Count"); + registerGauge("solr.jvm", "buffers.direct.MemoryUsed"); + h.getCoreContainer() + .getMetricManager() + .meter(null, "solr.jetty", "org.eclipse.jetty.server.handler.DefaultHandler.2xx-responses"); + h.getCoreContainer() + .getMetricManager() + .counter( + null, "solr.jetty", "org.eclipse.jetty.server.handler.DefaultHandler.active-requests"); + h.getCoreContainer() + .getMetricManager() + .timer(null, "solr.jetty", "org.eclipse.jetty.server.handler.DefaultHandler.dispatches"); } @AfterClass @@ -677,6 +706,326 @@ public void testExprMetrics() throws Exception { assertTrue(map.toString(), map.containsKey("count")); } + @Test + @SuppressWarnings("unchecked") + public void testPrometheusMetricsCore() throws Exception { + MetricsHandler handler = new MetricsHandler(h.getCoreContainer()); + + SolrQueryResponse resp = new SolrQueryResponse(); + handler.handleRequestBody( + req( + CommonParams.QT, + "/admin/metrics", + MetricsHandler.COMPACT_PARAM, + "false", + CommonParams.WT, + "prometheus"), + resp); + + NamedList values = resp.getValues(); + assertNotNull(values.get("metrics")); + values = (NamedList) values.get("metrics"); + SolrPrometheusExporter exporter = (SolrPrometheusExporter) values.get("solr.core.collection1"); + assertNotNull(exporter); + MetricSnapshots actualSnapshots = exporter.collect(); + assertNotNull(actualSnapshots); + + MetricSnapshot actualSnapshot = + getMetricSnapshot(actualSnapshots, "solr_metrics_core_average_request_time"); + GaugeSnapshot.GaugeDataPointSnapshot actualGaugeDataPoint = + getGaugeDatapointSnapshot( + actualSnapshot, + Labels.of("category", "QUERY", "core", "collection1", "handler", "/select[shard]")); + assertEquals(0, actualGaugeDataPoint.getValue(), 0); + + actualSnapshot = getMetricSnapshot(actualSnapshots, "solr_metrics_core_requests"); + CounterSnapshot.CounterDataPointSnapshot actualCounterDataPoint = + getCounterDatapointSnapshot( + actualSnapshot, + Labels.of( + "category", + "QUERY", + "core", + "collection1", + "handler", + "/select[shard]", + "type", + "requests")); + assertEquals(0, actualCounterDataPoint.getValue(), 0); + + actualSnapshot = getMetricSnapshot(actualSnapshots, "solr_metrics_core_cache"); + actualGaugeDataPoint = + getGaugeDatapointSnapshot( + actualSnapshot, + Labels.of("cacheType", "fieldValueCache", "core", "collection1", "item", "hits")); + assertEquals(0, actualGaugeDataPoint.getValue(), 0); + + actualSnapshot = getMetricSnapshot(actualSnapshots, "solr_metrics_core_highlighter_requests"); + actualCounterDataPoint = + getCounterDatapointSnapshot( + actualSnapshot, + Labels.of("item", "default", "core", "collection1", "type", "SolrFragmenter")); + assertEquals(0, actualCounterDataPoint.getValue(), 0); + + actualSnapshot = getMetricSnapshot(actualSnapshots, "solr_metrics_core_requests_time"); + actualCounterDataPoint = + getCounterDatapointSnapshot( + actualSnapshot, + Labels.of("category", "QUERY", "core", "collection1", "handler", "/select[shard]")); + assertEquals(0, actualCounterDataPoint.getValue(), 0); + + actualSnapshot = getMetricSnapshot(actualSnapshots, "solr_metrics_core_searcher_documents"); + actualGaugeDataPoint = + getGaugeDatapointSnapshot( + actualSnapshot, Labels.of("core", "collection1", "type", "numDocs")); + assertEquals(0, actualGaugeDataPoint.getValue(), 0); + + actualSnapshot = getMetricSnapshot(actualSnapshots, "solr_metrics_core_update_handler"); + actualGaugeDataPoint = + getGaugeDatapointSnapshot( + actualSnapshot, + Labels.of( + "category", + "UPDATE", + "core", + "collection1", + "type", + "adds", + "handler", + "updateHandler")); + assertEquals(0, actualGaugeDataPoint.getValue(), 0); + + actualSnapshot = + getMetricSnapshot(actualSnapshots, "solr_metrics_core_average_searcher_warmup_time"); + actualGaugeDataPoint = + getGaugeDatapointSnapshot( + actualSnapshot, Labels.of("core", "collection1", "type", "warmup")); + assertEquals(0, actualGaugeDataPoint.getValue(), 0); + + handler.close(); + } + + @Test + @SuppressWarnings("unchecked") + public void testPrometheusMetricsNode() throws Exception { + MetricsHandler handler = new MetricsHandler(h.getCoreContainer()); + + SolrQueryResponse resp = new SolrQueryResponse(); + handler.handleRequestBody( + req( + CommonParams.QT, + "/admin/metrics", + MetricsHandler.COMPACT_PARAM, + "false", + CommonParams.WT, + "prometheus"), + resp); + + NamedList values = resp.getValues(); + assertNotNull(values.get("metrics")); + values = (NamedList) values.get("metrics"); + SolrPrometheusExporter exporter = (SolrPrometheusExporter) values.get("solr.node"); + assertNotNull(exporter); + MetricSnapshots actualSnapshots = exporter.collect(); + assertNotNull(actualSnapshots); + + MetricSnapshot actualSnapshot = + getMetricSnapshot(actualSnapshots, "solr_metrics_node_requests"); + + CounterSnapshot.CounterDataPointSnapshot actualCounterDataPoint = + getCounterDatapointSnapshot( + actualSnapshot, + Labels.of("category", "ADMIN", "handler", "/admin/info", "type", "requests")); + assertEquals(0, actualCounterDataPoint.getValue(), 0); + + actualSnapshot = getMetricSnapshot(actualSnapshots, "solr_metrics_node_requests_time"); + actualCounterDataPoint = + getCounterDatapointSnapshot( + actualSnapshot, Labels.of("category", "ADMIN", "handler", "/admin/info")); + assertEquals(0, actualCounterDataPoint.getValue(), 0); + + actualSnapshot = getMetricSnapshot(actualSnapshots, "solr_metrics_node_thread_pool"); + actualCounterDataPoint = + getCounterDatapointSnapshot( + actualSnapshot, + Labels.of( + "category", + "ADMIN", + "executer", + "parallelCoreAdminExecutor", + "handler", + "/admin/cores", + "task", + "completed")); + assertEquals(0, actualCounterDataPoint.getValue(), 0); + + actualSnapshot = getMetricSnapshot(actualSnapshots, "solr_metrics_node_connections"); + GaugeSnapshot.GaugeDataPointSnapshot actualGaugeDataPoint = + getGaugeDatapointSnapshot( + actualSnapshot, + Labels.of( + "category", + "UPDATE", + "handler", + "updateShardHandler", + "item", + "availableConnections")); + assertEquals(0, actualGaugeDataPoint.getValue(), 0); + + actualSnapshot = getMetricSnapshot(actualSnapshots, "solr_metrics_node_core_root_fs_bytes"); + actualGaugeDataPoint = + getGaugeDatapointSnapshot( + actualSnapshot, Labels.of("category", "CONTAINER", "item", "totalSpace")); + assertNotNull(actualGaugeDataPoint); + + actualSnapshot = getMetricSnapshot(actualSnapshots, "solr_metrics_node_cores"); + actualGaugeDataPoint = + getGaugeDatapointSnapshot( + actualSnapshot, Labels.of("category", "CONTAINER", "item", "lazy")); + assertEquals(0, actualGaugeDataPoint.getValue(), 0); + + handler.close(); + } + + @Test + @SuppressWarnings("unchecked") + public void testPrometheusMetricsJvm() throws Exception { + // Some JVM metrics are non-deterministic due to testing environment such as + // availableProcessors. We confirm snapshot exists and is nonNull instead. + MetricsHandler handler = new MetricsHandler(h.getCoreContainer()); + + SolrQueryResponse resp = new SolrQueryResponse(); + handler.handleRequestBody( + req( + CommonParams.QT, + "/admin/metrics", + MetricsHandler.COMPACT_PARAM, + "false", + CommonParams.WT, + "prometheus"), + resp); + + NamedList values = resp.getValues(); + assertNotNull(values.get("metrics")); + values = (NamedList) values.get("metrics"); + SolrPrometheusExporter exporter = (SolrPrometheusExporter) values.get("solr.jvm"); + assertNotNull(exporter); + MetricSnapshots actualSnapshots = exporter.collect(); + assertNotNull(actualSnapshots); + + MetricSnapshot actualSnapshot = getMetricSnapshot(actualSnapshots, "solr_metrics_jvm_threads"); + GaugeSnapshot.GaugeDataPointSnapshot actualGaugeDataPoint = + getGaugeDatapointSnapshot(actualSnapshot, Labels.of("item", "count")); + assertNotNull(actualGaugeDataPoint); + + actualSnapshot = getMetricSnapshot(actualSnapshots, "solr_metrics_jvm_memory_pools_bytes"); + actualGaugeDataPoint = + getGaugeDatapointSnapshot( + actualSnapshot, Labels.of("item", "committed", "space", "CodeHeap-'non-nmethods'")); + assertEquals(0, actualGaugeDataPoint.getValue(), 0); + + actualSnapshot = getMetricSnapshot(actualSnapshots, "solr_metrics_jvm_buffers"); + actualGaugeDataPoint = + getGaugeDatapointSnapshot(actualSnapshot, Labels.of("item", "Count", "pool", "direct")); + assertNotNull(actualGaugeDataPoint); + + actualSnapshot = getMetricSnapshot(actualSnapshots, "solr_metrics_jvm_heap"); + actualGaugeDataPoint = + getGaugeDatapointSnapshot(actualSnapshot, Labels.of("item", "committed", "memory", "heap")); + assertNotNull(actualGaugeDataPoint); + + actualSnapshot = getMetricSnapshot(actualSnapshots, "solr_metrics_jvm_buffers_bytes"); + actualGaugeDataPoint = + getGaugeDatapointSnapshot( + actualSnapshot, Labels.of("item", "MemoryUsed", "pool", "direct")); + assertNotNull(actualGaugeDataPoint); + + handler.close(); + } + + @Test + @SuppressWarnings("unchecked") + public void testPrometheusMetricsJetty() throws Exception { + MetricsHandler handler = new MetricsHandler(h.getCoreContainer()); + + SolrQueryResponse resp = new SolrQueryResponse(); + handler.handleRequestBody( + req( + CommonParams.QT, + "/admin/metrics", + MetricsHandler.COMPACT_PARAM, + "false", + CommonParams.WT, + "prometheus"), + resp); + + NamedList values = resp.getValues(); + assertNotNull(values.get("metrics")); + values = (NamedList) values.get("metrics"); + SolrPrometheusExporter exporter = (SolrPrometheusExporter) values.get("solr.jetty"); + assertNotNull(exporter); + MetricSnapshots actualSnapshots = exporter.collect(); + assertNotNull(actualSnapshots); + + MetricSnapshot actualSnapshot = + getMetricSnapshot(actualSnapshots, "solr_metrics_jetty_response"); + CounterSnapshot.CounterDataPointSnapshot actualCounterDatapoint = + getCounterDatapointSnapshot(actualSnapshot, Labels.of("status", "2xx")); + assertEquals(0, actualCounterDatapoint.getValue(), 0); + + actualSnapshot = getMetricSnapshot(actualSnapshots, "solr_metrics_jetty_requests"); + actualCounterDatapoint = + getCounterDatapointSnapshot(actualSnapshot, Labels.of("method", "active")); + assertEquals(0, actualCounterDatapoint.getValue(), 0); + + actualSnapshot = getMetricSnapshot(actualSnapshots, "solr_metrics_jetty_dispatches"); + actualCounterDatapoint = getCounterDatapointSnapshot(actualSnapshot, Labels.of()); + assertEquals(0, actualCounterDatapoint.getValue(), 0); + + handler.close(); + } + + @Test + @SuppressWarnings("unchecked") + public void testPrometheusMetricsFilter() throws Exception { + MetricsHandler handler = new MetricsHandler(h.getCoreContainer()); + + SolrQueryResponse resp = new SolrQueryResponse(); + handler.handleRequestBody( + req( + CommonParams.QT, + "/admin/metrics", + CommonParams.WT, + "prometheus", + "group", + "core", + "type", + "counter", + "prefix", + "QUERY"), + resp); + + NamedList values = resp.getValues(); + assertNotNull(values.get("metrics")); + values = (NamedList) values.get("metrics"); + SolrPrometheusExporter exporter = (SolrPrometheusExporter) values.get("solr.core.collection1"); + assertNotNull(exporter); + MetricSnapshots actualSnapshots = exporter.collect(); + assertNotNull(actualSnapshots); + + actualSnapshots.forEach( + (k) -> { + k.getDataPoints() + .forEach( + (datapoint) -> { + assertTrue(datapoint instanceof CounterSnapshot.CounterDataPointSnapshot); + assertEquals("QUERY", datapoint.getLabels().get("category")); + }); + }); + + handler.close(); + } + @Test public void testMetricsUnload() throws Exception { @@ -787,6 +1136,54 @@ public void testMetricsUnload() throws Exception { handler.close(); } + public static void registerGauge(String registry, String metricName) { + Gauge metric = + new SettableGauge<>() { + @Override + public void setValue(Number value) {} + + @Override + public Number getValue() { + return 0; + } + }; + h.getCoreContainer() + .getMetricManager() + .registerGauge( + null, + registry, + metric, + "", + SolrMetricManager.ResolutionStrategy.IGNORE, + metricName, + ""); + } + + private MetricSnapshot getMetricSnapshot(MetricSnapshots snapshots, String metricName) { + return snapshots.stream() + .filter(ss -> ss.getMetadata().getPrometheusName().equals(metricName)) + .findAny() + .get(); + } + + private GaugeSnapshot.GaugeDataPointSnapshot getGaugeDatapointSnapshot( + MetricSnapshot snapshot, Labels labels) { + return (GaugeSnapshot.GaugeDataPointSnapshot) + snapshot.getDataPoints().stream() + .filter(ss -> ss.getLabels().hasSameValues(labels)) + .findAny() + .get(); + } + + private CounterSnapshot.CounterDataPointSnapshot getCounterDatapointSnapshot( + MetricSnapshot snapshot, Labels labels) { + return (CounterSnapshot.CounterDataPointSnapshot) + snapshot.getDataPoints().stream() + .filter(ss -> ss.getLabels().hasSameValues(labels)) + .findAny() + .get(); + } + static class RefreshablePluginHolder extends PluginBag.PluginHolder { private DumpRequestHandler rh; diff --git a/solr/core/src/test/org/apache/solr/metrics/SolrCoreMetricTest.java b/solr/core/src/test/org/apache/solr/metrics/SolrCoreMetricTest.java new file mode 100644 index 00000000000..3a8b6e58cd7 --- /dev/null +++ b/solr/core/src/test/org/apache/solr/metrics/SolrCoreMetricTest.java @@ -0,0 +1,81 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.solr.metrics; + +import com.codahale.metrics.Metric; +import io.prometheus.metrics.model.snapshots.Labels; +import org.apache.solr.SolrTestCaseJ4; +import org.apache.solr.metrics.prometheus.SolrPrometheusExporter; +import org.apache.solr.metrics.prometheus.core.SolrCoreMetric; +import org.junit.Test; + +public class SolrCoreMetricTest extends SolrTestCaseJ4 { + + @Test + public void testStandaloneDefaultLabels() throws InterruptedException { + String expectedCoreName = "test_core"; + String expectedMetricName = "test_core_metric"; + Labels expectedLabels = Labels.of("core", expectedCoreName); + TestSolrCoreMetric testSolrCoreMetric = + new TestSolrCoreMetric(null, expectedCoreName, expectedMetricName, false); + + assertEquals(expectedCoreName, testSolrCoreMetric.coreName); + assertEquals(expectedMetricName, testSolrCoreMetric.metricName); + assertEquals(expectedLabels, testSolrCoreMetric.getLabels()); + } + + @Test + public void testCloudDefaultLabels() throws InterruptedException { + String expectedCoreName = "core_test_core_shard1_replica_n1"; + String expectedMetricName = "test_core_metric"; + String expectedCollectionName = "test_core"; + String expectedShardName = "shard1"; + String expectedReplicaName = "replica_n1"; + + Labels expectedLabels = + Labels.of( + "core", + expectedCoreName, + "collection", + expectedCollectionName, + "shard", + expectedShardName, + "replica", + expectedReplicaName); + TestSolrCoreMetric testSolrCoreMetric = + new TestSolrCoreMetric(null, expectedCoreName, expectedMetricName, true); + + assertEquals(expectedCoreName, testSolrCoreMetric.coreName); + assertEquals(expectedMetricName, testSolrCoreMetric.metricName); + assertEquals(expectedLabels, testSolrCoreMetric.getLabels()); + } + + static class TestSolrCoreMetric extends SolrCoreMetric { + public TestSolrCoreMetric( + Metric dropwizardMetric, String coreName, String metricName, boolean cloudMode) { + super(dropwizardMetric, coreName, metricName, cloudMode); + } + + @Override + public SolrCoreMetric parseLabels() { + return null; + } + + @Override + public void toPrometheus(SolrPrometheusExporter exporter) {} + } +} diff --git a/solr/core/src/test/org/apache/solr/metrics/SolrPrometheusExporterTest.java b/solr/core/src/test/org/apache/solr/metrics/SolrPrometheusExporterTest.java new file mode 100644 index 00000000000..445e02f326f --- /dev/null +++ b/solr/core/src/test/org/apache/solr/metrics/SolrPrometheusExporterTest.java @@ -0,0 +1,161 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.solr.metrics; + +import com.codahale.metrics.Counter; +import com.codahale.metrics.Gauge; +import com.codahale.metrics.Meter; +import com.codahale.metrics.Metric; +import com.codahale.metrics.SettableGauge; +import com.codahale.metrics.Timer; +import io.prometheus.metrics.model.snapshots.CounterSnapshot; +import io.prometheus.metrics.model.snapshots.GaugeSnapshot; +import io.prometheus.metrics.model.snapshots.Labels; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import org.apache.solr.SolrTestCaseJ4; +import org.apache.solr.metrics.prometheus.SolrMetric; +import org.apache.solr.metrics.prometheus.SolrPrometheusExporter; +import org.junit.Test; + +public class SolrPrometheusExporterTest extends SolrTestCaseJ4 { + @Test + public void testExportMeter() { + TestSolrPrometheusExporter testExporter = new TestSolrPrometheusExporter(); + String expectedMetricName = "test_metric"; + Meter metric = new Meter(); + metric.mark(123); + Labels expectedLabels = Labels.of("test", "test-value"); + + testExporter.exportMeter(expectedMetricName, metric, expectedLabels); + assertTrue(testExporter.getMetricCounters().containsKey(expectedMetricName)); + + CounterSnapshot.CounterDataPointSnapshot actual = + testExporter.getMetricCounters().get("test_metric").get(0); + assertEquals(123.0, actual.getValue(), 0); + assertEquals(expectedLabels, actual.getLabels()); + } + + @Test + public void testExportCounter() { + TestSolrPrometheusExporter testExporter = new TestSolrPrometheusExporter(); + String expectedMetricName = "test_metric"; + Counter metric = new Counter(); + metric.inc(123); + Labels expectedLabels = Labels.of("test", "test-value"); + + testExporter.exportCounter(expectedMetricName, metric, expectedLabels); + assertTrue(testExporter.getMetricCounters().containsKey(expectedMetricName)); + + CounterSnapshot.CounterDataPointSnapshot actual = + testExporter.getMetricCounters().get("test_metric").get(0); + assertEquals(123.0, actual.getValue(), 0); + assertEquals(expectedLabels, actual.getLabels()); + } + + @Test + public void testExportTimer() throws InterruptedException { + TestSolrPrometheusExporter testExporter = new TestSolrPrometheusExporter(); + String expectedMetricName = "test_metric"; + Timer metric = new Timer(); + Timer.Context context = metric.time(); + TimeUnit.SECONDS.sleep(5); + context.stop(); + + Labels expectedLabels = Labels.of("test", "test-value"); + testExporter.exportTimer(expectedMetricName, metric, expectedLabels); + assertTrue(testExporter.getMetricGauges().containsKey(expectedMetricName)); + + GaugeSnapshot.GaugeDataPointSnapshot actual = + testExporter.getMetricGauges().get("test_metric").get(0); + assertEquals(5000000000L, actual.getValue(), 500000000L); + assertEquals(expectedLabels, actual.getLabels()); + } + + @Test + public void testExportGaugeNumber() throws InterruptedException { + TestSolrPrometheusExporter testExporter = new TestSolrPrometheusExporter(); + String expectedMetricName = "test_metric"; + Gauge metric = + new SettableGauge<>() { + @Override + public void setValue(Number value) {} + + @Override + public Number getValue() { + return 123.0; + } + }; + + Labels expectedLabels = Labels.of("test", "test-value"); + testExporter.exportGauge(expectedMetricName, metric, expectedLabels); + assertTrue(testExporter.getMetricGauges().containsKey(expectedMetricName)); + + GaugeSnapshot.GaugeDataPointSnapshot actual = + testExporter.getMetricGauges().get("test_metric").get(0); + assertEquals(123.0, actual.getValue(), 0); + assertEquals(expectedLabels, actual.getLabels()); + } + + @Test + public void testExportGaugeMap() throws InterruptedException { + TestSolrPrometheusExporter testExporter = new TestSolrPrometheusExporter(); + String expectedMetricName = "test_metric"; + Gauge> metric = + new SettableGauge<>() { + @Override + public void setValue(Map value) {} + + @Override + public Map getValue() { + final Map expected = new HashMap<>(); + expected.put("test-item", 123.0); + return expected; + } + }; + + Labels labels = Labels.of("test", "test-value"); + testExporter.exportGauge(expectedMetricName, metric, labels); + assertTrue(testExporter.getMetricGauges().containsKey(expectedMetricName)); + + GaugeSnapshot.GaugeDataPointSnapshot actual = + testExporter.getMetricGauges().get("test_metric").get(0); + assertEquals(123.0, actual.getValue(), 0); + Labels expectedLabels = Labels.of("test", "test-value", "item", "test-item"); + assertEquals(expectedLabels, actual.getLabels()); + } + + static class TestSolrPrometheusExporter extends SolrPrometheusExporter { + @Override + public void exportDropwizardMetric(Metric dropwizardMetric, String metricName) {} + + @Override + public SolrMetric categorizeMetric(Metric dropwizardMetric, String metricName) { + return null; + } + + public Map> getMetricCounters() { + return metricCounters; + } + + public Map> getMetricGauges() { + return metricGauges; + } + } +} diff --git a/solr/core/src/test/org/apache/solr/response/TestPrometheusResponseWriter.java b/solr/core/src/test/org/apache/solr/response/TestPrometheusResponseWriter.java new file mode 100644 index 00000000000..f46f5a0f85d --- /dev/null +++ b/solr/core/src/test/org/apache/solr/response/TestPrometheusResponseWriter.java @@ -0,0 +1,105 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.solr.response; + +import com.codahale.metrics.Gauge; +import com.codahale.metrics.SettableGauge; +import java.nio.file.Files; +import org.apache.lucene.tests.util.LuceneTestCase; +import org.apache.solr.SolrTestCaseJ4; +import org.apache.solr.client.solrj.SolrClient; +import org.apache.solr.client.solrj.impl.NoOpResponseParser; +import org.apache.solr.client.solrj.request.QueryRequest; +import org.apache.solr.common.params.ModifiableSolrParams; +import org.apache.solr.common.util.NamedList; +import org.apache.solr.embedded.JettySolrRunner; +import org.apache.solr.metrics.SolrMetricManager; +import org.apache.solr.util.ExternalPaths; +import org.apache.solr.util.SolrJettyTestRule; +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.Test; + +/** Tests the {@link PrometheusResponseWriter} behavior */ +public class TestPrometheusResponseWriter extends SolrTestCaseJ4 { + + @ClassRule public static SolrJettyTestRule solrClientTestRule = new SolrJettyTestRule(); + public static JettySolrRunner jetty; + + @BeforeClass + public static void beforeClass() throws Exception { + solrClientTestRule.startSolr(LuceneTestCase.createTempDir()); + jetty = solrClientTestRule.getJetty(); + solrClientTestRule.newCollection().withConfigSet(ExternalPaths.DEFAULT_CONFIGSET).create(); + jetty.getCoreContainer().waitForLoadingCoresToFinish(30000); + // Manually register metrics not initializing from JettyTestRule + registerGauge( + jetty.getCoreContainer().getMetricManager(), + "solr.jvm", + "buffers.mapped - 'non-volatile memory'.Count"); + registerGauge( + jetty.getCoreContainer().getMetricManager(), + "solr.jvm", + "buffers.mapped - 'non-volatile memory'.MemoryUsed"); + registerGauge( + jetty.getCoreContainer().getMetricManager(), + "solr.jvm", + "buffers.mapped - 'non-volatile memory'.TotalCapacity"); + registerGauge(jetty.getCoreContainer().getMetricManager(), "solr.jvm", "os.cpuLoad"); + registerGauge(jetty.getCoreContainer().getMetricManager(), "solr.jvm", "os.freeMemorySize"); + registerGauge(jetty.getCoreContainer().getMetricManager(), "solr.jvm", "os.totalMemorySize"); + } + + @Test + public void testPrometheusOutput() throws Exception { + ModifiableSolrParams params = new ModifiableSolrParams(); + params.set("qt", "/admin/metrics"); + params.set("wt", "prometheus"); + QueryRequest req = new QueryRequest(params); + req.setResponseParser(new NoOpResponseParser("prometheus")); + try (SolrClient adminClient = getHttpSolrClient(jetty.getBaseUrl().toString()); ) { + NamedList res = adminClient.request(req); + assertNotNull("null response from server", res); + String actual = (String) res.get("response"); + String expectedOutput = + Files.readString(getFile("prometheus/solr-prometheus-output.txt").toPath()); + // Expression to strip out ending numeric values and JVM item tag as we only want to test for + // Prometheus metric names + actual = + actual.replaceAll( + "(?<=}).*|(?<=solr_metrics_jvm_gc\\{)(.*)(?=})|(?<=solr_metrics_jvm_gc_seconds\\{)(.*)(?=})", + ""); + assertEquals(expectedOutput, actual); + } + } + + private static void registerGauge( + SolrMetricManager metricManager, String registry, String metricName) { + Gauge metric = + new SettableGauge<>() { + @Override + public void setValue(Number value) {} + + @Override + public Number getValue() { + return 0; + } + }; + metricManager.registerGauge( + null, registry, metric, "", SolrMetricManager.ResolutionStrategy.IGNORE, metricName, ""); + } +} diff --git a/solr/licenses/prometheus-metrics-exposition-formats-1.1.0.jar.sha1 b/solr/licenses/prometheus-metrics-exposition-formats-1.1.0.jar.sha1 new file mode 100644 index 00000000000..60b2586d867 --- /dev/null +++ b/solr/licenses/prometheus-metrics-exposition-formats-1.1.0.jar.sha1 @@ -0,0 +1 @@ +466f12bdde0dcb5b5469d1bd01370db7b62bead3 diff --git a/solr/licenses/prometheus-metrics-exposition-formats-LICENSE-ASL.txt b/solr/licenses/prometheus-metrics-exposition-formats-LICENSE-ASL.txt new file mode 100644 index 00000000000..f49a4e16e68 --- /dev/null +++ b/solr/licenses/prometheus-metrics-exposition-formats-LICENSE-ASL.txt @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/solr/licenses/prometheus-metrics-exposition-formats-NOTICE.txt b/solr/licenses/prometheus-metrics-exposition-formats-NOTICE.txt new file mode 100644 index 00000000000..cbd3cd95bef --- /dev/null +++ b/solr/licenses/prometheus-metrics-exposition-formats-NOTICE.txt @@ -0,0 +1,11 @@ +Prometheus instrumentation library for JVM applications +Copyright 2012-2015 The Prometheus Authors + +This product includes software developed at +Boxever Ltd. (http://www.boxever.com/). + +This product includes software developed at +SoundCloud Ltd. (http://soundcloud.com/). + +This product includes software developed as part of the +Ocelli project by Netflix Inc. (https://github.com/Netflix/ocelli/). \ No newline at end of file diff --git a/solr/licenses/prometheus-metrics-model-1.1.0.jar.sha1 b/solr/licenses/prometheus-metrics-model-1.1.0.jar.sha1 new file mode 100644 index 00000000000..6cac482dd0c --- /dev/null +++ b/solr/licenses/prometheus-metrics-model-1.1.0.jar.sha1 @@ -0,0 +1 @@ +4a6093076e5b526aff26611e6c4f0eb4518211b3 diff --git a/solr/licenses/prometheus-metrics-model-LICENSE-ASL.txt b/solr/licenses/prometheus-metrics-model-LICENSE-ASL.txt new file mode 100644 index 00000000000..f49a4e16e68 --- /dev/null +++ b/solr/licenses/prometheus-metrics-model-LICENSE-ASL.txt @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/solr/licenses/prometheus-metrics-model-NOTICE.txt b/solr/licenses/prometheus-metrics-model-NOTICE.txt new file mode 100644 index 00000000000..cbd3cd95bef --- /dev/null +++ b/solr/licenses/prometheus-metrics-model-NOTICE.txt @@ -0,0 +1,11 @@ +Prometheus instrumentation library for JVM applications +Copyright 2012-2015 The Prometheus Authors + +This product includes software developed at +Boxever Ltd. (http://www.boxever.com/). + +This product includes software developed at +SoundCloud Ltd. (http://soundcloud.com/). + +This product includes software developed as part of the +Ocelli project by Netflix Inc. (https://github.com/Netflix/ocelli/). \ No newline at end of file diff --git a/solr/solr-ref-guide/modules/deployment-guide/pages/metrics-reporting.adoc b/solr/solr-ref-guide/modules/deployment-guide/pages/metrics-reporting.adoc index 75ce4974118..8295050aa9f 100644 --- a/solr/solr-ref-guide/modules/deployment-guide/pages/metrics-reporting.adoc +++ b/solr/solr-ref-guide/modules/deployment-guide/pages/metrics-reporting.adoc @@ -880,6 +880,10 @@ The response will look like this: ---- Like other request handlers, the Metrics API can also take the `wt` parameter to define the output format. +The Metrics API also provides an additional special `wt` parameter `prometheus` that will output metrics in Prometheus format. +This can be used for xref:monitoring-with-prometheus-and-grafana.adoc#metrics-api-with-prometheus-format[Monitoring with Prometheus and Grafana] + +*Note: The `group`, `type`, and `prefix` query parameters are still available for filtering Prometheus metrics but `regex`, `property`, `key` and `compact` are not supported.* [[metrics_examples]] === Examples diff --git a/solr/solr-ref-guide/modules/deployment-guide/pages/monitoring-with-prometheus-and-grafana.adoc b/solr/solr-ref-guide/modules/deployment-guide/pages/monitoring-with-prometheus-and-grafana.adoc index ee5ca30b915..9ce78463ca1 100644 --- a/solr/solr-ref-guide/modules/deployment-guide/pages/monitoring-with-prometheus-and-grafana.adoc +++ b/solr/solr-ref-guide/modules/deployment-guide/pages/monitoring-with-prometheus-and-grafana.adoc @@ -16,13 +16,21 @@ // specific language governing permissions and limitations // under the License. -If you use https://prometheus.io[Prometheus] and https://grafana.com[Grafana] for metrics storage and data visualization, Solr includes a Prometheus exporter to collect metrics and other data. +If you use https://prometheus.io[Prometheus] and https://grafana.com[Grafana] for metrics storage and data visualization, Solr provides 2 solutions to collect metrics and other data: + +* Prometheus Exporter +* Metrics API with Prometheus format The Prometheus exporter is included with the full Solr distribution, and is located under `prometheus-exporter/`. It is not included in the `slim` Solr distribution. A Prometheus exporter (`solr-exporter`) allows users to monitor not only Solr metrics which come from the xref:metrics-reporting.adoc#metrics-api[Metrics API], but also facet counts which come from xref:query-guide:faceting.adoc[] and responses to xref:configuration-guide:collections-api.adoc[] commands and xref:ping.adoc[] requests. +The Metrics API provides a Prometheus Response Writer to output Solr metrics natively to be scraped. +It is more efficient and drops the need of running the Prometheus Exporter but at the cost of a fixed output and not as flexible in terms of configurability. + +== Prometheus Exporter + This graphic provides a more detailed view: .solr-exporter Diagram @@ -37,7 +45,7 @@ See the section below <>. * Modify your Prometheus configuration to listen on the correct port. See the section below <> -== Starting the Exporter +=== Starting the Exporter You can start `solr-exporter` by running `./bin/solr-exporter` (Linux) or `.\bin\solr-exporter.cmd` (Windows) from the `prometheus-exporter/` directory. The metrics exposed by `solr-exporter` can be seen at the metrics endpoint: `\http://localhost:8983/solr/admin/metrics`. @@ -285,7 +293,7 @@ NOTE:: The Exporter needs the `commons-codec` library for SSL/BasicAuth, but doe Therefore the example reuses it from the Solr web app. Of course, you can use a different source. -== Exporter Configuration +=== Exporter Configuration The configuration for the `solr-exporter` defines the data to get from Solr. This includes the metrics, but can also include queries to the PingRequestHandler, the Collections API, and a query to any query request handler. @@ -558,7 +566,7 @@ For example, `solr-exporter` converts the JSON in the previous section to the fo solr_ping{base_url="http://localhost:8983/solr",core="collection1"} 1.0 ---- -== Prometheus Configuration +=== Prometheus Configuration Prometheus is a separate server that you need to download and deploy. More information can be found at the Prometheus https://prometheus.io/docs/prometheus/latest/getting_started/[Getting Started] page. @@ -583,7 +591,7 @@ doing a query for `solr_ping` metric in the Prometheus GUI: .Prometheus Solr Ping expression image::monitoring-with-prometheus-and-grafana/prometheus-solr-ping.png[image,width=800] -== Sample Grafana Dashboard +=== Sample Grafana Dashboard To use Grafana for visualization, it must be downloaded and deployed separately. More information can be found on the Grafana https://grafana.com/docs/grafana/latest/[Documentation] site. @@ -598,3 +606,29 @@ This screenshot shows what it might look like: .Grafana Dashboard image::monitoring-with-prometheus-and-grafana/grafana-solr-dashboard.png[image,width=800] + +== Metrics API with Prometheus format + +Prometheus Metrics are available natively directly from Solr by leveraging the Metrics API and setting `wt` parameter to `prometheus`: + +[source,text] +localhost:8983/solr/admin/metrics?wt=prometheus + +The Metrics API with the `prometheus` parameter does not provide any configurability and the Prometheus output is fixed. +Any metrics aggregations and/or filtering must be done on Grafana or the Prometheus server. + +=== Prometheus Configuration + +Like the Prometheus Exporter, the `prometheus.yml` needs to be configurated for the Prometheus Server to ingest metrics. +The difference is it must instead scrape the Metrics API with the `wt=prometheus` parameter directly from each host/port Solr is running on as in this example: + +[source,plain] +---- +scrape_configs: + - job_name: 'solr' + metrics_path: "/solr/admin/metrics" + static_configs: + - targets: ['localhost:8983', 'localhost:7574'] + params: + wt: ['prometheus'] +---- diff --git a/versions.lock b/versions.lock index 99c57bc94bf..53c8d2dd898 100644 --- a/versions.lock +++ b/versions.lock @@ -145,6 +145,8 @@ io.opentelemetry:opentelemetry-sdk-logs:1.35.0 (3 constraints: eb32b3b2) io.opentelemetry:opentelemetry-sdk-metrics:1.35.0 (3 constraints: eb32b3b2) io.opentelemetry:opentelemetry-sdk-trace:1.35.0 (3 constraints: eb32b3b2) io.perfmark:perfmark-api:0.26.0 (3 constraints: 21212b16) +io.prometheus:prometheus-metrics-exposition-formats:1.1.0 (1 constraints: 0405f335) +io.prometheus:prometheus-metrics-model:1.1.0 (2 constraints: 411b133b) io.prometheus:simpleclient:0.16.0 (3 constraints: 9d257513) io.prometheus:simpleclient_common:0.16.0 (1 constraints: 1a1139c0) io.prometheus:simpleclient_httpserver:0.16.0 (1 constraints: 3905353b)