kopia lustrzana https://github.com/onthegomap/planetiler
tests for new monitoring utils
rodzic
6d227e690f
commit
2f13a74929
4835
grafana.json
4835
grafana.json
Plik diff jest za duży
Load Diff
|
@ -9,8 +9,10 @@ import io.prometheus.client.GaugeMetricFamily;
|
||||||
import io.prometheus.client.Histogram;
|
import io.prometheus.client.Histogram;
|
||||||
import io.prometheus.client.exporter.BasicAuthHttpConnectionFactory;
|
import io.prometheus.client.exporter.BasicAuthHttpConnectionFactory;
|
||||||
import io.prometheus.client.exporter.PushGateway;
|
import io.prometheus.client.exporter.PushGateway;
|
||||||
|
import io.prometheus.client.exporter.common.TextFormat;
|
||||||
import io.prometheus.client.hotspot.DefaultExports;
|
import io.prometheus.client.hotspot.DefaultExports;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.StringWriter;
|
||||||
import java.lang.management.ManagementFactory;
|
import java.lang.management.ManagementFactory;
|
||||||
import java.lang.management.OperatingSystemMXBean;
|
import java.lang.management.OperatingSystemMXBean;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
@ -37,24 +39,26 @@ public class PrometheusStats implements Stats {
|
||||||
|
|
||||||
private final CollectorRegistry registry = new CollectorRegistry();
|
private final CollectorRegistry registry = new CollectorRegistry();
|
||||||
private final Timers timers = new Timers();
|
private final Timers timers = new Timers();
|
||||||
private static final String NAMESPACE = "flatmap";
|
|
||||||
private static final String BASE = "flatmap_";
|
private static final String BASE = "flatmap_";
|
||||||
private final PushGateway pg;
|
private PushGateway pg;
|
||||||
private final ScheduledExecutorService executor;
|
private ScheduledExecutorService executor;
|
||||||
private final String job;
|
private final String job;
|
||||||
private final Map<String, Path> filesToMonitor = Collections.synchronizedMap(new LinkedHashMap<>());
|
private final Map<String, Path> filesToMonitor = Collections.synchronizedMap(new LinkedHashMap<>());
|
||||||
private final Map<String, MemoryEstimator.HasEstimate> heapObjectsToMonitor = Collections
|
private final Map<String, MemoryEstimator.HasEstimate> heapObjectsToMonitor = Collections
|
||||||
.synchronizedMap(new LinkedHashMap<>());
|
.synchronizedMap(new LinkedHashMap<>());
|
||||||
|
|
||||||
public PrometheusStats(String destination, String job, Duration interval) {
|
public PrometheusStats(String job) {
|
||||||
this.job = job;
|
this.job = job;
|
||||||
try {
|
DefaultExports.register(registry);
|
||||||
DefaultExports.register(registry);
|
new ThreadDetailsExports().register(registry);
|
||||||
new ThreadDetailsExports().register(registry);
|
new InProgressTasks().register(registry);
|
||||||
new InProgressTasks().register(registry);
|
new FileSizeCollector().register(registry);
|
||||||
new FileSizeCollector().register(registry);
|
new HeapObjectSizeCollector().register(registry);
|
||||||
new HeapObjectSizeCollector().register(registry);
|
}
|
||||||
|
|
||||||
|
public PrometheusStats(String destination, String job, Duration interval) {
|
||||||
|
this(job);
|
||||||
|
try {
|
||||||
URL url = new URL(destination);
|
URL url = new URL(destination);
|
||||||
pg = new PushGateway(url);
|
pg = new PushGateway(url);
|
||||||
if (url.getUserInfo() != null) {
|
if (url.getUserInfo() != null) {
|
||||||
|
@ -134,6 +138,15 @@ public class PrometheusStats implements Stats {
|
||||||
emittedFeatures.labels(Integer.toString(z), layer).inc(number);
|
emittedFeatures.labels(Integer.toString(z), layer).inc(number);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getMetricsAsString() {
|
||||||
|
try (StringWriter writer = new StringWriter()) {
|
||||||
|
TextFormat.write004(writer, registry.metricFamilySamples());
|
||||||
|
return writer.toString();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new IllegalStateException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private final Histogram tilesWrittenBytes = Histogram
|
private final Histogram tilesWrittenBytes = Histogram
|
||||||
.build(BASE + "mbtiles_tile_written_bytes", "Written tile sizes by zoom level")
|
.build(BASE + "mbtiles_tile_written_bytes", "Written tile sizes by zoom level")
|
||||||
.buckets(1_000, 10_000, 100_000, 500_000)
|
.buckets(1_000, 10_000, 100_000, 500_000)
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
package com.onthegomap.flatmap.monitoring;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
public class CounterTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSingleThreadedCounter() {
|
||||||
|
var counter = Counter.newSingleThreadCounter();
|
||||||
|
assertEquals(0, counter.get());
|
||||||
|
|
||||||
|
counter.inc();
|
||||||
|
assertEquals(1, counter.get());
|
||||||
|
counter.incBy(2);
|
||||||
|
assertEquals(3, counter.get());
|
||||||
|
counter.incBy(-1);
|
||||||
|
assertEquals(2, counter.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMultiThreadedCounter() throws InterruptedException {
|
||||||
|
var counter = Counter.newMultiThreadCounter();
|
||||||
|
Thread t1 = new Thread(() -> {
|
||||||
|
counter.incBy(1);
|
||||||
|
counter.incBy(1);
|
||||||
|
});
|
||||||
|
t1.start();
|
||||||
|
Thread t2 = new Thread(() -> {
|
||||||
|
counter.incBy(1);
|
||||||
|
});
|
||||||
|
t2.start();
|
||||||
|
t1.join();
|
||||||
|
t2.join();
|
||||||
|
assertEquals(3, counter.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMultiThreadedSubCounter() throws InterruptedException {
|
||||||
|
var counter = Counter.newMultiThreadCounter();
|
||||||
|
Thread t1 = new Thread(() -> {
|
||||||
|
var subCounter = counter.counterForThread();
|
||||||
|
subCounter.incBy(1);
|
||||||
|
subCounter.incBy(1);
|
||||||
|
});
|
||||||
|
t1.start();
|
||||||
|
Thread t2 = new Thread(() -> {
|
||||||
|
var subCounter = counter.counterForThread();
|
||||||
|
subCounter.incBy(1);
|
||||||
|
});
|
||||||
|
t2.start();
|
||||||
|
t1.join();
|
||||||
|
t2.join();
|
||||||
|
assertEquals(3, counter.get());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,154 @@
|
||||||
|
package com.onthegomap.flatmap.monitoring;
|
||||||
|
|
||||||
|
import static io.prometheus.client.Collector.NANOSECONDS_PER_SECOND;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
|
import static org.junit.jupiter.api.Assertions.fail;
|
||||||
|
import static org.junit.jupiter.api.DynamicTest.dynamicTest;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
import org.junit.jupiter.api.DynamicTest;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.TestFactory;
|
||||||
|
import org.junit.jupiter.api.io.TempDir;
|
||||||
|
|
||||||
|
public class PrometheusStatsTest {
|
||||||
|
|
||||||
|
@TestFactory
|
||||||
|
public Stream<DynamicTest> testInitialStat() {
|
||||||
|
PrometheusStats stats = new PrometheusStats("job");
|
||||||
|
String metrics = stats.getMetricsAsString();
|
||||||
|
return testContains(metrics,
|
||||||
|
"^jvm_thread_cpu_time_seconds_total\\{",
|
||||||
|
"^jvm_thread_user_time_seconds_total\\{",
|
||||||
|
"^jvm_system_load_avg ",
|
||||||
|
"^jvm_available_processors [0-9\\.]+$"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTimer() {
|
||||||
|
PrometheusStats stats = new PrometheusStats("job");
|
||||||
|
stats.time("task1", () -> {
|
||||||
|
assertContainsStat("^flatmap_task1_running 1", stats);
|
||||||
|
assertContainsStat("^flatmap_task1_elapsed_time_seconds [0-9\\.]+$", stats);
|
||||||
|
assertContainsStat("^flatmap_task1_cpu_time_seconds [0-9\\.]+$", stats);
|
||||||
|
});
|
||||||
|
assertContainsStat("^flatmap_task1_running 0", stats);
|
||||||
|
assertContainsStat("^flatmap_task1_elapsed_time_seconds [0-9\\.]+$", stats);
|
||||||
|
assertContainsStat("^flatmap_task1_cpu_time_seconds [0-9\\.]+$", stats);
|
||||||
|
|
||||||
|
assertFalse(stats.timers().all().get("task1").running());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGauge() {
|
||||||
|
PrometheusStats stats = new PrometheusStats("job");
|
||||||
|
stats.gauge("gauge1", 1);
|
||||||
|
stats.gauge("gauge2", () -> 2);
|
||||||
|
assertContainsStat("^flatmap_gauge1 1", stats);
|
||||||
|
assertContainsStat("^flatmap_gauge2 2", stats);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testProcessedElement() {
|
||||||
|
PrometheusStats stats = new PrometheusStats("job");
|
||||||
|
stats.processedElement("type1", "layer1");
|
||||||
|
stats.processedElement("type1", "layer1");
|
||||||
|
stats.processedElement("type1", "layer2");
|
||||||
|
assertContainsStat("^flatmap_renderer_elements_processed_total\\{.*layer1.* 2", stats);
|
||||||
|
assertContainsStat("^flatmap_renderer_elements_processed_total\\{.*layer2.* 1", stats);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDataError() {
|
||||||
|
PrometheusStats stats = new PrometheusStats("job");
|
||||||
|
stats.dataError("err1");
|
||||||
|
stats.dataError("err1");
|
||||||
|
stats.dataError("err2");
|
||||||
|
assertContainsStat("^flatmap_bad_input_data_total\\{.*err1.* 2", stats);
|
||||||
|
assertContainsStat("^flatmap_bad_input_data_total\\{.*err2.* 1", stats);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEmittedFeatures() {
|
||||||
|
PrometheusStats stats = new PrometheusStats("job");
|
||||||
|
stats.emittedFeatures(0, "layer1", 2);
|
||||||
|
stats.emittedFeatures(0, "layer1", 2);
|
||||||
|
stats.emittedFeatures(0, "layer2", 1);
|
||||||
|
assertContainsStat("^flatmap_renderer_features_emitted_total\\{.*layer1.* 4", stats);
|
||||||
|
assertContainsStat("^flatmap_renderer_features_emitted_total\\{.*layer2.* 1", stats);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testWroteTile() {
|
||||||
|
PrometheusStats stats = new PrometheusStats("job");
|
||||||
|
stats.wroteTile(0, 10);
|
||||||
|
stats.wroteTile(0, 10_000);
|
||||||
|
assertContainsStat("^flatmap_mbtiles_tile_written_bytes_bucket\\{.*le=\"1000\\..* 1", stats);
|
||||||
|
assertContainsStat("^flatmap_mbtiles_tile_written_bytes_bucket\\{.*le=\"10000\\..* 2", stats);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMonitorFile(@TempDir Path path) throws IOException {
|
||||||
|
PrometheusStats stats = new PrometheusStats("job");
|
||||||
|
stats.monitorFile("test", path);
|
||||||
|
assertContainsStat("^flatmap_file_test_size_bytes 0", stats);
|
||||||
|
|
||||||
|
Files.writeString(path.resolve("data"), "abc");
|
||||||
|
assertContainsStat("^flatmap_file_test_size_bytes [0-9]", stats);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMonitorInMemoryObject() {
|
||||||
|
PrometheusStats stats = new PrometheusStats("job");
|
||||||
|
stats.monitorInMemoryObject("test", () -> 10);
|
||||||
|
assertContainsStat("^flatmap_heap_object_test_size_bytes 10", stats);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Counter.Readable counterAt(int num) {
|
||||||
|
var result = Counter.newSingleThreadCounter();
|
||||||
|
result.incBy(num);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCounter() {
|
||||||
|
PrometheusStats stats = new PrometheusStats("job");
|
||||||
|
stats.counter("counter1", () -> 1);
|
||||||
|
stats.counter("counter2", "label", () -> Map.of(
|
||||||
|
"value1", counterAt(1),
|
||||||
|
"value2", counterAt(2)
|
||||||
|
));
|
||||||
|
stats.longCounter("long").incBy(100);
|
||||||
|
stats.nanoCounter("nanos").incBy((long) (NANOSECONDS_PER_SECOND / 2));
|
||||||
|
assertContainsStat("^flatmap_counter1_total 1", stats);
|
||||||
|
assertContainsStat("^flatmap_counter2_total\\{.*label=\"value1\".* 1", stats);
|
||||||
|
assertContainsStat("^flatmap_counter2_total\\{.*label=\"value2\".* 2", stats);
|
||||||
|
assertContainsStat("^flatmap_long_total 100", stats);
|
||||||
|
assertContainsStat("^flatmap_nanos_total 0.5", stats);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Stream<DynamicTest> testContains(String stats, String... regexes) {
|
||||||
|
return Stream.of(regexes).map(re -> dynamicTest(re, () -> assertContainsStat(re, stats)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void assertContainsStat(String regex, PrometheusStats stats) {
|
||||||
|
assertContainsStat(regex, stats.getMetricsAsString());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void assertContainsStat(String regex, String stats) {
|
||||||
|
Pattern pattern = Pattern.compile(regex);
|
||||||
|
for (String line : stats.split("\n")) {
|
||||||
|
if (!line.startsWith("#") && pattern.matcher(line).find()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
System.err.println(stats);
|
||||||
|
fail("could not find " + regex);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
package com.onthegomap.flatmap.monitoring;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
public class TimerTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTimer() {
|
||||||
|
Timer timer = new Timer().start();
|
||||||
|
ProcessTime elapsed1 = timer.elapsed();
|
||||||
|
ProcessTime elapsed2 = timer.stop().elapsed();
|
||||||
|
ProcessTime elapsed3 = timer.elapsed();
|
||||||
|
|
||||||
|
assertEquals(elapsed2.wall(), elapsed3.wall());
|
||||||
|
assertLessThan(elapsed1.wall(), elapsed2.wall());
|
||||||
|
assertFalse(elapsed3.cpu().isEmpty(), "no CPU time");
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T extends Comparable<T>> void assertLessThan(T a, T b) {
|
||||||
|
assertTrue(a.compareTo(b) < 0, a + " is not less than " + b);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
package com.onthegomap.flatmap.monitoring;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
public class TimersTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTimers() {
|
||||||
|
Timers timers = new Timers();
|
||||||
|
assertTrue(timers.all().isEmpty());
|
||||||
|
timers.printSummary();
|
||||||
|
|
||||||
|
timers.time("task1", () -> {
|
||||||
|
assertTrue(timers.all().get("task1").running());
|
||||||
|
});
|
||||||
|
|
||||||
|
assertFalse(timers.all().get("task1").running());
|
||||||
|
|
||||||
|
var finish = timers.startTimer("task2");
|
||||||
|
assertTrue(timers.all().get("task2").running());
|
||||||
|
finish.stop();
|
||||||
|
assertFalse(timers.all().get("task2").running());
|
||||||
|
|
||||||
|
timers.printSummary();
|
||||||
|
}
|
||||||
|
}
|
Ładowanie…
Reference in New Issue