blob: e95bea4683169dca75f1315f789690260d59f51f [file] [log] [blame]
/*
* 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.util.stats;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import com.codahale.metrics.Counter;
import com.codahale.metrics.Gauge;
import com.codahale.metrics.Histogram;
import com.codahale.metrics.Meter;
import com.codahale.metrics.MetricFilter;
import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.Snapshot;
import com.codahale.metrics.Timer;
import org.apache.solr.SolrTestCaseJ4;
import org.apache.solr.common.MapWriter;
import org.apache.solr.common.util.NamedList;
import org.apache.solr.metrics.AggregateMetric;
import org.apache.solr.metrics.MetricsMap;
import org.apache.solr.metrics.SolrMetricManager;
import org.junit.Test;
public class MetricUtilsTest extends SolrTestCaseJ4 {
@Test
@SuppressWarnings({"unchecked"})
public void testSolrTimerGetSnapshot() {
// create a timer with up to 100 data points
final Timer timer = new Timer();
final int iterations = random().nextInt(100);
for (int i = 0; i < iterations; ++i) {
timer.update(Math.abs(random().nextInt()) + 1, TimeUnit.NANOSECONDS);
}
// obtain timer metrics
Map<String,Object> map = new HashMap<>();
MetricUtils.convertTimer("", timer, MetricUtils.ALL_PROPERTIES, false, false, ".", (k, v) -> {
((MapWriter) v).toMap(map);
});
@SuppressWarnings({"rawtypes"})
NamedList lst = new NamedList(map);
// check that expected metrics were obtained
assertEquals(14, lst.size());
final Snapshot snapshot = timer.getSnapshot();
// cannot test avgRequestsPerMinute directly because mean rate changes as time increases!
// assertEquals(lst.get("avgRequestsPerSecond"), timer.getMeanRate());
assertEquals(timer.getFiveMinuteRate(), lst.get("5minRate"));
assertEquals(timer.getFifteenMinuteRate(), lst.get("15minRate"));
assertEquals(MetricUtils.nsToMs(snapshot.getMean()), lst.get("mean_ms"));
assertEquals(MetricUtils.nsToMs(snapshot.getMedian()), lst.get("median_ms"));
assertEquals(MetricUtils.nsToMs(snapshot.get75thPercentile()), lst.get("p75_ms"));
assertEquals(MetricUtils.nsToMs(snapshot.get95thPercentile()), lst.get("p95_ms"));
assertEquals(MetricUtils.nsToMs(snapshot.get99thPercentile()), lst.get("p99_ms"));
assertEquals(MetricUtils.nsToMs(snapshot.get999thPercentile()), lst.get("p999_ms"));
}
@Test
@SuppressWarnings({"unchecked"})
public void testMetrics() throws Exception {
MetricRegistry registry = new MetricRegistry();
Counter counter = registry.counter("counter");
counter.inc();
Timer timer = registry.timer("timer");
Timer.Context ctx = timer.time();
Thread.sleep(150);
ctx.stop();
Meter meter = registry.meter("meter");
meter.mark();
Histogram histogram = registry.histogram("histogram");
histogram.update(10);
// SOLR-14252: check that negative values are supported correctly
// NB a-d represent the same metric from multiple nodes
AggregateMetric am1 = new AggregateMetric();
registry.register("aggregate1", am1);
am1.set("a", -10);
am1.set("b", 1);
am1.set("b", -2);
am1.set("c", -3);
am1.set("d", -5);
// SOLR-14252: check that aggregation of non-Number metrics don't trigger NullPointerException
AggregateMetric am2 = new AggregateMetric();
registry.register("aggregate2", am2);
am2.set("a", false);
am2.set("b", true);
Gauge<String> gauge = () -> "foobar";
registry.register("gauge", gauge);
Gauge<Long> error = () -> {throw new InternalError("Memory Pool not found error");};
registry.register("memory.expected.error", error);
MetricsMap metricsMapWithMap = new MetricsMap((detailed, map) -> {
map.put("foo", "bar");
});
registry.register("mapWithMap", metricsMapWithMap);
MetricsMap metricsMap = new MetricsMap(map -> {
map.putNoEx("foo", "bar");
});
registry.register("map", metricsMap);
SolrMetricManager.GaugeWrapper<Map<String,Object>> gaugeWrapper = new SolrMetricManager.GaugeWrapper<>(metricsMap, "foo-tag");
registry.register("wrappedGauge", gaugeWrapper);
MetricUtils.toMaps(registry, Collections.singletonList(MetricFilter.ALL), MetricFilter.ALL,
MetricUtils.ALL_PROPERTIES, false, false, false, false, (k, o) -> {
@SuppressWarnings({"rawtypes"})
Map<String, Object> v = new HashMap<>();
if (o != null) {
((MapWriter) o).toMap(v);
}
if (k.startsWith("counter")) {
assertEquals(1L, v.get("count"));
} else if (k.startsWith("gauge")) {
assertEquals("foobar", v.get("value"));
} else if (k.startsWith("timer")) {
assertEquals(1L, v.get("count"));
assertTrue(((Number)v.get("min_ms")).intValue() > 100);
} else if (k.startsWith("meter")) {
assertEquals(1L, v.get("count"));
} else if (k.startsWith("histogram")) {
assertEquals(1L, v.get("count"));
} else if (k.startsWith("aggregate1")) {
assertEquals(4, v.get("count"));
Map<String, Object> values = (Map<String, Object>)v.get("values");
assertNotNull(values);
assertEquals(4, values.size());
Map<String, Object> update = (Map<String, Object>)values.get("a");
assertEquals(-10, update.get("value"));
assertEquals(1, update.get("updateCount"));
update = (Map<String, Object>)values.get("b");
assertEquals(-2, update.get("value"));
assertEquals(2, update.get("updateCount"));
assertEquals(-10D, v.get("min"));
assertEquals(-2D, v.get("max"));
assertEquals(-5D, v.get("mean"));
} else if (k.startsWith("aggregate2")) {
// SOLR-14252: non-Number metric aggregations should return 0 rather than throwing NPE
assertEquals(2, v.get("count"));
assertEquals(0D, v.get("min"));
assertEquals(0D, v.get("max"));
assertEquals(0D, v.get("mean"));
} else if (k.startsWith("memory.expected.error")) {
assertTrue(v.isEmpty());
} else if (k.startsWith("map") || k.startsWith("wrapped")) {
assertNotNull(v.toString(), v.get("value"));
assertTrue(v.toString(), v.get("value") instanceof Map);
assertEquals(v.toString(), "bar", ((Map) v.get("value")).get("foo"));
}
});
// test compact format
MetricUtils.toMaps(registry, Collections.singletonList(MetricFilter.ALL), MetricFilter.ALL,
MetricUtils.ALL_PROPERTIES, false, false, true, false, (k, o) -> {
if (k.startsWith("counter")) {
assertTrue(o instanceof Long);
assertEquals(1L, o);
} else if (k.startsWith("gauge")) {
assertTrue(o instanceof String);
assertEquals("foobar", o);
} else if (k.startsWith("timer")) {
assertTrue(o instanceof MapWriter);
Map<String, Object> v = new HashMap<>();
((MapWriter) o).toMap(v);
assertEquals(1L, v.get("count"));
assertTrue(((Number)v.get("min_ms")).intValue() > 100);
} else if (k.startsWith("meter")) {
assertTrue(o instanceof MapWriter);
Map<String, Object> v = new HashMap<>();
((MapWriter) o).toMap(v);
assertEquals(1L, v.get("count"));
} else if (k.startsWith("histogram")) {
assertTrue(o instanceof MapWriter);
Map<String, Object> v = new HashMap<>();
((MapWriter) o).toMap(v);
assertEquals(1L, v.get("count"));
} else if (k.startsWith("aggregate1")) {
assertTrue(o instanceof MapWriter);
Map<String, Object> v = new HashMap<>();
((MapWriter) o).toMap(v);
assertEquals(4, v.get("count"));
Map<String, Object> values = (Map<String, Object>)v.get("values");
assertNotNull(values);
assertEquals(4, values.size());
Map<String, Object> update = (Map<String, Object>)values.get("a");
assertEquals(-10, update.get("value"));
assertEquals(1, update.get("updateCount"));
update = (Map<String, Object>)values.get("b");
assertEquals(-2, update.get("value"));
assertEquals(2, update.get("updateCount"));
} else if (k.startsWith("aggregate2")) {
assertTrue(o instanceof MapWriter);
Map<String, Object> v = new HashMap<>();
((MapWriter) o).toMap(v);
assertEquals(2, v.get("count"));
Map<String, Object> values = (Map<String, Object>)v.get("values");
assertNotNull(values);
assertEquals(2, values.size());
Map<String, Object> update = (Map<String, Object>)values.get("a");
assertEquals(false, update.get("value"));
assertEquals(1, update.get("updateCount"));
update = (Map<String, Object>)values.get("b");
assertEquals(true, update.get("value"));
assertEquals(1, update.get("updateCount"));
} else if (k.startsWith("memory.expected.error")) {
assertNull(o);
} else if (k.startsWith("map") || k.startsWith("wrapped")) {
assertTrue(o instanceof MapWriter);
MapWriter writer = (MapWriter) o;
assertEquals(1, writer._size());
assertEquals("bar", writer._get("foo", null));
} else {
assertTrue(o instanceof MapWriter);
Map<String, Object> v = new HashMap<>();
((MapWriter) o).toMap(v);
assertEquals(1L, v.get("count"));
}
});
}
}