2021-12-23 10:42:24 +00:00
|
|
|
package com.onthegomap.planetiler.stats;
|
2021-04-16 00:54:33 +00:00
|
|
|
|
2022-01-28 01:23:24 +00:00
|
|
|
import static com.onthegomap.planetiler.util.Format.padLeft;
|
|
|
|
import static com.onthegomap.planetiler.util.Format.padRight;
|
2021-04-16 00:54:33 +00:00
|
|
|
|
2021-12-23 10:42:24 +00:00
|
|
|
import com.onthegomap.planetiler.util.DiskBacked;
|
|
|
|
import com.onthegomap.planetiler.util.Format;
|
|
|
|
import com.onthegomap.planetiler.util.MemoryEstimator;
|
|
|
|
import com.onthegomap.planetiler.worker.WorkQueue;
|
|
|
|
import com.onthegomap.planetiler.worker.Worker;
|
|
|
|
import com.onthegomap.planetiler.worker.WorkerPipeline;
|
2021-05-01 20:08:20 +00:00
|
|
|
import java.io.IOException;
|
|
|
|
import java.nio.file.Files;
|
|
|
|
import java.nio.file.Path;
|
2021-04-16 00:54:33 +00:00
|
|
|
import java.time.Duration;
|
|
|
|
import java.util.ArrayList;
|
|
|
|
import java.util.List;
|
2022-01-28 01:23:24 +00:00
|
|
|
import java.util.Locale;
|
2021-04-16 00:54:33 +00:00
|
|
|
import java.util.Map;
|
2021-06-04 11:22:40 +00:00
|
|
|
import java.util.Optional;
|
2021-04-16 00:54:33 +00:00
|
|
|
import java.util.TreeMap;
|
2021-08-05 01:22:20 +00:00
|
|
|
import java.util.concurrent.ExecutionException;
|
|
|
|
import java.util.concurrent.Future;
|
2021-04-16 00:54:33 +00:00
|
|
|
import java.util.concurrent.TimeUnit;
|
2021-08-05 01:22:20 +00:00
|
|
|
import java.util.concurrent.TimeoutException;
|
2021-04-16 00:54:33 +00:00
|
|
|
import java.util.concurrent.atomic.AtomicLong;
|
|
|
|
import java.util.function.DoubleFunction;
|
2021-10-20 01:57:47 +00:00
|
|
|
import java.util.function.Function;
|
2021-04-16 00:54:33 +00:00
|
|
|
import java.util.function.LongSupplier;
|
|
|
|
import java.util.function.Supplier;
|
|
|
|
import java.util.stream.Collectors;
|
|
|
|
import org.slf4j.Logger;
|
|
|
|
import org.slf4j.LoggerFactory;
|
|
|
|
|
2021-09-10 00:46:20 +00:00
|
|
|
/**
|
|
|
|
* Logs the progress of a long-running task (percent complete, queue sizes, CPU and memory usage, etc.)
|
|
|
|
*/
|
2021-10-20 01:57:47 +00:00
|
|
|
@SuppressWarnings({"UnusedReturnValue", "unused"})
|
2021-04-16 00:54:33 +00:00
|
|
|
public class ProgressLoggers {
|
|
|
|
|
2021-08-10 10:55:30 +00:00
|
|
|
private static final String COLOR_RESET = "\u001B[0m";
|
|
|
|
private static final String FG_RED = "\u001B[31m";
|
|
|
|
private static final String FG_GREEN = "\u001B[32m";
|
|
|
|
private static final String FG_YELLOW = "\u001B[33m";
|
|
|
|
private static final String FG_BLUE = "\u001B[34m";
|
2021-09-10 00:46:20 +00:00
|
|
|
private static final Logger LOGGER = LoggerFactory.getLogger(ProgressLoggers.class);
|
|
|
|
private final List<Object> loggers = new ArrayList<>();
|
2022-01-28 01:23:24 +00:00
|
|
|
private final Format format;
|
2021-08-10 10:55:30 +00:00
|
|
|
|
|
|
|
private static String fg(String fg, String string) {
|
|
|
|
return fg + string + COLOR_RESET;
|
|
|
|
}
|
|
|
|
|
|
|
|
private static String red(String string) {
|
|
|
|
return fg(FG_RED, string);
|
|
|
|
}
|
|
|
|
|
|
|
|
private static String green(String string) {
|
|
|
|
return fg(FG_GREEN, string);
|
|
|
|
}
|
|
|
|
|
|
|
|
private static String yellow(String string) {
|
|
|
|
return fg(FG_YELLOW, string);
|
|
|
|
}
|
|
|
|
|
|
|
|
private static String blue(String string) {
|
|
|
|
return fg(FG_BLUE, string);
|
|
|
|
}
|
|
|
|
|
2022-01-28 01:23:24 +00:00
|
|
|
private ProgressLoggers(Locale locale) {
|
|
|
|
this.format = Format.forLocale(locale);
|
2021-04-16 00:54:33 +00:00
|
|
|
}
|
|
|
|
|
2021-09-10 00:46:20 +00:00
|
|
|
public static ProgressLoggers create() {
|
2022-01-28 01:23:24 +00:00
|
|
|
return createForLocale(Format.DEFAULT_LOCALE);
|
|
|
|
}
|
|
|
|
|
|
|
|
public static ProgressLoggers createForLocale(Locale locale) {
|
|
|
|
return new ProgressLoggers(locale);
|
2021-04-16 00:54:33 +00:00
|
|
|
}
|
|
|
|
|
2021-09-10 00:46:20 +00:00
|
|
|
/** Adds "name: [ numCompleted rate/s ]" to the logger. */
|
2021-04-16 00:54:33 +00:00
|
|
|
public ProgressLoggers addRateCounter(String name, LongSupplier getValue) {
|
2021-08-10 10:55:30 +00:00
|
|
|
return addRateCounter(name, getValue, false);
|
|
|
|
}
|
|
|
|
|
2021-09-10 00:46:20 +00:00
|
|
|
/** Adds "name: [ numCompleted rate/s ]" to the logger. */
|
2021-08-10 10:55:30 +00:00
|
|
|
public ProgressLoggers addRateCounter(String name, AtomicLong getValue) {
|
|
|
|
return addRateCounter(name, getValue, false);
|
|
|
|
}
|
|
|
|
|
2021-09-10 00:46:20 +00:00
|
|
|
/**
|
|
|
|
* Adds "name: [ numCompleted rate/s ]" to the logger, colored green if {@code color=true} and rate > 0.
|
|
|
|
*/
|
2021-08-10 10:55:30 +00:00
|
|
|
public ProgressLoggers addRateCounter(String name, LongSupplier getValue, boolean color) {
|
2021-04-16 00:54:33 +00:00
|
|
|
AtomicLong last = new AtomicLong(getValue.getAsLong());
|
|
|
|
AtomicLong lastTime = new AtomicLong(System.nanoTime());
|
|
|
|
loggers.add(new ProgressLogger(name, () -> {
|
|
|
|
long now = System.nanoTime();
|
|
|
|
long valueNow = getValue.getAsLong();
|
|
|
|
double timeDiff = (now - lastTime.get()) * 1d / (1d * TimeUnit.SECONDS.toNanos(1));
|
|
|
|
double valueDiff = valueNow - last.get();
|
2021-08-05 01:22:20 +00:00
|
|
|
if (valueDiff < 0) {
|
|
|
|
valueDiff = valueNow;
|
|
|
|
}
|
2021-04-16 00:54:33 +00:00
|
|
|
last.set(valueNow);
|
|
|
|
lastTime.set(now);
|
2022-01-28 01:23:24 +00:00
|
|
|
String result =
|
|
|
|
"[ " + format.numeric(valueNow, true) + " " + format.numeric(valueDiff / timeDiff, true) + "/s ]";
|
2021-08-10 10:55:30 +00:00
|
|
|
return color && valueDiff > 0 ? green(result) : result;
|
2021-04-16 00:54:33 +00:00
|
|
|
}));
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2021-09-10 00:46:20 +00:00
|
|
|
/**
|
|
|
|
* Adds "name: [ numCompleted rate/s ]" to the logger, colored green if {@code color=true} and rate > 0.
|
|
|
|
*/
|
2021-08-10 10:55:30 +00:00
|
|
|
public ProgressLoggers addRateCounter(String name, AtomicLong value, boolean color) {
|
|
|
|
return addRateCounter(name, value::get, color);
|
2021-04-16 00:54:33 +00:00
|
|
|
}
|
|
|
|
|
2021-09-10 00:46:20 +00:00
|
|
|
/**
|
|
|
|
* Adds "name: [ numCompleted pctComplete% rate/s ]" to the logger where {@code total} is the total number of items to
|
|
|
|
* process.
|
|
|
|
*/
|
2022-03-01 01:52:30 +00:00
|
|
|
public ProgressLoggers addRatePercentCounter(String name, long total, AtomicLong value, boolean color) {
|
|
|
|
return addRatePercentCounter(name, total, value::get, color);
|
2021-04-16 00:54:33 +00:00
|
|
|
}
|
|
|
|
|
2021-09-10 00:46:20 +00:00
|
|
|
/**
|
|
|
|
* Adds "name: [ numCompleted pctComplete% rate/s ]" to the logger where {@code total} is the total number of items to
|
|
|
|
* process.
|
|
|
|
*/
|
2022-03-01 01:52:30 +00:00
|
|
|
public ProgressLoggers addRatePercentCounter(String name, long total, LongSupplier getValue, boolean color) {
|
|
|
|
return addRatePercentCounter(name, total, getValue, n -> format.numeric(n, true), color);
|
2021-10-20 01:57:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Adds "name: [ numCompleted pctComplete% rate/s ]" to the logger where {@code total} is the total number of bytes to
|
|
|
|
* process.
|
|
|
|
*/
|
2022-03-01 01:52:30 +00:00
|
|
|
public ProgressLoggers addStorageRatePercentCounter(String name, long total, LongSupplier getValue, boolean color) {
|
|
|
|
return addRatePercentCounter(name, total, getValue, n -> format.storage(n, true), color);
|
2021-10-20 01:57:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Adds "name: [ numCompleted pctComplete% rate/s ]" to the logger where {@code total} is the total number of items to
|
|
|
|
* process.
|
|
|
|
*/
|
|
|
|
public ProgressLoggers addRatePercentCounter(String name, long total, LongSupplier getValue,
|
2022-03-01 01:52:30 +00:00
|
|
|
Function<Number, String> formatter, boolean color) {
|
2021-08-14 09:55:00 +00:00
|
|
|
// if there's no total, we can't show progress so fall back to rate logger instead
|
|
|
|
if (total == 0) {
|
2022-03-01 01:52:30 +00:00
|
|
|
return addRateCounter(name, getValue, color);
|
2021-08-14 09:55:00 +00:00
|
|
|
}
|
2021-04-16 00:54:33 +00:00
|
|
|
AtomicLong last = new AtomicLong(getValue.getAsLong());
|
|
|
|
AtomicLong lastTime = new AtomicLong(System.nanoTime());
|
|
|
|
loggers.add(new ProgressLogger(name, () -> {
|
|
|
|
long now = System.nanoTime();
|
|
|
|
long valueNow = getValue.getAsLong();
|
|
|
|
double timeDiff = (now - lastTime.get()) * 1d / (1d * TimeUnit.SECONDS.toNanos(1));
|
|
|
|
double valueDiff = valueNow - last.get();
|
2021-08-05 01:22:20 +00:00
|
|
|
if (valueDiff < 0) {
|
|
|
|
valueDiff = valueNow;
|
|
|
|
}
|
2021-04-16 00:54:33 +00:00
|
|
|
last.set(valueNow);
|
|
|
|
lastTime.set(now);
|
2021-08-14 09:55:00 +00:00
|
|
|
String result =
|
2022-03-09 02:08:03 +00:00
|
|
|
"[ " + formatter.apply(valueNow) + " " + padLeft(format.percent(1f * valueNow / total), 4) + " " +
|
|
|
|
formatter.apply(valueDiff / timeDiff) + "/s ]";
|
2022-03-01 01:52:30 +00:00
|
|
|
return (color && valueDiff > 0) ? green(result) : result;
|
2021-04-16 00:54:33 +00:00
|
|
|
}));
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2021-09-10 00:46:20 +00:00
|
|
|
/**
|
|
|
|
* Adds "name: [ numComplete / total pctComplete% ]" to the logger where {@code total} is the total number of items to
|
|
|
|
* process.
|
|
|
|
*/
|
2021-04-16 00:54:33 +00:00
|
|
|
public ProgressLoggers addPercentCounter(String name, long total, AtomicLong getValue) {
|
|
|
|
loggers.add(new ProgressLogger(name, () -> {
|
|
|
|
long valueNow = getValue.get();
|
2021-08-10 10:55:30 +00:00
|
|
|
return "[ " + padLeft("" + valueNow, 3) + " / " + padLeft("" + total, 3) + " " + padLeft(
|
2022-01-28 01:23:24 +00:00
|
|
|
format.percent(1f * valueNow / total), 4) + " ]";
|
2021-04-16 00:54:33 +00:00
|
|
|
}));
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2021-09-10 00:46:20 +00:00
|
|
|
/** Adds the current number of items in a queue and the queue's size to the output. */
|
2021-04-16 00:54:33 +00:00
|
|
|
public ProgressLoggers addQueueStats(WorkQueue<?> queue) {
|
2022-03-09 02:08:03 +00:00
|
|
|
loggers.add(new WorkerPipelineLogger(() -> " -> " + padLeft("(" +
|
|
|
|
format.numeric(queue.getPending(), false) + "/" +
|
|
|
|
format.numeric(queue.getCapacity(), false) + ")", 9)
|
2021-04-16 00:54:33 +00:00
|
|
|
));
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2021-09-10 00:46:20 +00:00
|
|
|
public ProgressLoggers add(String obj) {
|
2021-04-16 00:54:33 +00:00
|
|
|
loggers.add(obj);
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2021-09-10 00:46:20 +00:00
|
|
|
public ProgressLoggers add(Supplier<String> obj) {
|
|
|
|
loggers.add(new Object() {
|
|
|
|
@Override
|
|
|
|
public String toString() {
|
|
|
|
return obj.get();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2021-05-01 20:08:20 +00:00
|
|
|
public ProgressLoggers addFileSize(Path file) {
|
2021-09-10 00:46:20 +00:00
|
|
|
return add(() -> {
|
2021-05-01 20:08:20 +00:00
|
|
|
String bytes;
|
|
|
|
try {
|
2022-01-28 01:23:24 +00:00
|
|
|
bytes = format.storage(Files.size(file), false);
|
2021-05-01 20:08:20 +00:00
|
|
|
} catch (IOException e) {
|
|
|
|
bytes = "-";
|
|
|
|
}
|
|
|
|
return " " + padRight(bytes, 5);
|
2021-09-10 00:46:20 +00:00
|
|
|
});
|
2021-05-01 20:08:20 +00:00
|
|
|
}
|
|
|
|
|
2021-08-22 09:37:57 +00:00
|
|
|
public ProgressLoggers addFileSize(DiskBacked longSupplier) {
|
2022-01-28 01:23:24 +00:00
|
|
|
return add(() -> " " + padRight(format.storage(longSupplier.diskUsageBytes(), false), 5));
|
2021-04-16 00:54:33 +00:00
|
|
|
}
|
|
|
|
|
2021-10-20 01:57:47 +00:00
|
|
|
/** Adds the total of disk and memory usage of {@code thing}. */
|
|
|
|
public <T extends DiskBacked & MemoryEstimator.HasEstimate> ProgressLoggers addFileSizeAndRam(T thing) {
|
|
|
|
return add(() -> {
|
|
|
|
long bytes = thing.diskUsageBytes() + thing.estimateMemoryUsageBytes();
|
2022-01-28 01:23:24 +00:00
|
|
|
return " " + padRight(format.storage(bytes, false), 5);
|
2021-10-20 01:57:47 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Adds the current size of a file on disk. */
|
|
|
|
public ProgressLoggers addFileSize(String name, DiskBacked file) {
|
2022-01-28 01:23:24 +00:00
|
|
|
loggers.add(new ProgressLogger(name, () -> format.storage(file.diskUsageBytes(), true)));
|
2021-10-20 01:57:47 +00:00
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2021-09-10 00:46:20 +00:00
|
|
|
/**
|
|
|
|
* Adds the average number of CPUs and % time in GC since last log along with memory usage, total memory, and memory
|
|
|
|
* used after last GC to the output.
|
|
|
|
*/
|
2021-04-16 00:54:33 +00:00
|
|
|
public ProgressLoggers addProcessStats() {
|
2022-01-28 01:23:24 +00:00
|
|
|
addOptionalDeltaLogger("cpus", ProcessInfo::getProcessCpuTime, num -> blue(format.decimal(num)));
|
2021-08-10 10:55:30 +00:00
|
|
|
addDeltaLogger("gc", ProcessInfo::getGcTime, num -> {
|
2022-01-28 01:23:24 +00:00
|
|
|
String formatted = format.percent(num);
|
2021-09-10 00:46:20 +00:00
|
|
|
return num > 0.6 ? red(formatted) : num > 0.3 ? yellow(formatted) : formatted;
|
2021-08-10 10:55:30 +00:00
|
|
|
});
|
2021-04-16 00:54:33 +00:00
|
|
|
loggers.add(new ProgressLogger("mem",
|
2022-03-01 13:43:19 +00:00
|
|
|
() -> format.storage(ProcessInfo.getUsedMemoryBytes(), false) + "/" +
|
|
|
|
format.storage(ProcessInfo.getMaxMemoryBytes(), false) +
|
2022-03-04 01:21:25 +00:00
|
|
|
ProcessInfo.getDirectMemoryUsage().stream()
|
|
|
|
.filter(usage -> usage > 0)
|
|
|
|
.mapToObj(mem -> " direct: " + format.storage(mem))
|
|
|
|
.findFirst()
|
|
|
|
.orElse("") +
|
2021-09-10 00:46:20 +00:00
|
|
|
ProcessInfo.getMemoryUsageAfterLastGC().stream()
|
2022-01-28 01:23:24 +00:00
|
|
|
.mapToObj(value -> " postGC: " + blue(format.storage(value, false)))
|
2021-09-10 00:46:20 +00:00
|
|
|
.findFirst()
|
|
|
|
.orElse("")
|
2021-07-24 21:10:48 +00:00
|
|
|
));
|
2021-04-16 00:54:33 +00:00
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2021-06-04 11:22:40 +00:00
|
|
|
private void addOptionalDeltaLogger(String name, Supplier<Optional<Duration>> supplier,
|
|
|
|
DoubleFunction<String> format) {
|
|
|
|
addDeltaLogger(name, () -> supplier.get().orElse(Duration.ZERO), format);
|
|
|
|
}
|
|
|
|
|
2021-09-10 00:46:20 +00:00
|
|
|
// adds a logger that keeps track of the value each time it is invoked and logs the change
|
2021-04-16 00:54:33 +00:00
|
|
|
private void addDeltaLogger(String name, Supplier<Duration> supplier, DoubleFunction<String> format) {
|
|
|
|
AtomicLong lastValue = new AtomicLong(supplier.get().toNanos());
|
|
|
|
AtomicLong lastTime = new AtomicLong(System.nanoTime());
|
|
|
|
loggers.add(new ProgressLogger(name, () -> {
|
|
|
|
long currentValue = supplier.get().toNanos();
|
|
|
|
if (currentValue < 0) {
|
|
|
|
return "-";
|
|
|
|
}
|
|
|
|
long currentTime = System.nanoTime();
|
|
|
|
double rate = 1d * (currentValue - lastValue.get()) / (currentTime - lastTime.get());
|
|
|
|
lastTime.set(currentTime);
|
|
|
|
lastValue.set(currentValue);
|
|
|
|
return padLeft(format.apply(rate), 3);
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
|
2021-09-10 00:46:20 +00:00
|
|
|
/** Adds the CPU utilization of every thread starting with {@code prefix} since the last log to output. */
|
2021-04-16 00:54:33 +00:00
|
|
|
public ProgressLoggers addThreadPoolStats(String name, String prefix) {
|
2021-08-05 11:09:52 +00:00
|
|
|
boolean first = loggers.isEmpty() || !(loggers.get(loggers.size() - 1) instanceof WorkerPipelineLogger);
|
2021-04-16 00:54:33 +00:00
|
|
|
try {
|
2021-04-30 10:31:56 +00:00
|
|
|
Map<Long, ProcessInfo.ThreadState> lastThreads = ProcessInfo.getThreadStats();
|
2021-04-16 00:54:33 +00:00
|
|
|
AtomicLong lastTime = new AtomicLong(System.nanoTime());
|
2021-08-05 11:09:52 +00:00
|
|
|
loggers.add(new WorkerPipelineLogger(() -> {
|
2021-04-16 00:54:33 +00:00
|
|
|
var oldAndNewThreads = new TreeMap<>(lastThreads);
|
|
|
|
var newThreads = ProcessInfo.getThreadStats();
|
|
|
|
oldAndNewThreads.putAll(newThreads);
|
|
|
|
|
|
|
|
long currentTime = System.nanoTime();
|
|
|
|
double timeDiff = 1d * (currentTime - lastTime.get());
|
|
|
|
String percents = oldAndNewThreads.values().stream()
|
|
|
|
.filter(thread -> thread.name().startsWith(prefix))
|
|
|
|
.map(thread -> {
|
|
|
|
if (!newThreads.containsKey(thread.id())) {
|
|
|
|
return " -%";
|
|
|
|
}
|
2021-09-10 00:46:20 +00:00
|
|
|
long last = lastThreads.getOrDefault(thread.id(), ProcessInfo.ThreadState.DEFAULT).cpuTime().toNanos();
|
2022-01-28 01:23:24 +00:00
|
|
|
return padLeft(format.percent(1d * (thread.cpuTime().toNanos() - last) / timeDiff), 3);
|
2021-04-16 00:54:33 +00:00
|
|
|
}).collect(Collectors.joining(" ", "(", ")"));
|
|
|
|
|
|
|
|
lastTime.set(currentTime);
|
|
|
|
lastThreads.putAll(newThreads);
|
2021-08-10 10:55:30 +00:00
|
|
|
return (first ? " " : " -> ") + name + percents;
|
2021-04-16 00:54:33 +00:00
|
|
|
}));
|
2021-08-10 10:55:30 +00:00
|
|
|
} catch (Throwable ignored) {
|
2021-04-16 00:54:33 +00:00
|
|
|
// can't get CPU stats per-thread
|
|
|
|
}
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2021-09-10 00:46:20 +00:00
|
|
|
/** Adds the CPU utilization since last log of every thread in a {@link Worker} pool to output. */
|
2021-04-16 00:54:33 +00:00
|
|
|
public ProgressLoggers addThreadPoolStats(String name, Worker worker) {
|
|
|
|
return addThreadPoolStats(name, worker.getPrefix());
|
|
|
|
}
|
|
|
|
|
|
|
|
public void log() {
|
|
|
|
LOGGER.info(getLog());
|
|
|
|
}
|
|
|
|
|
2021-09-10 00:46:20 +00:00
|
|
|
public String getLog() {
|
|
|
|
return loggers.stream()
|
|
|
|
.map(Object::toString)
|
|
|
|
.collect(Collectors.joining(""))
|
|
|
|
.replaceAll(System.lineSeparator() + "\\s*", System.lineSeparator() + " ");
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Adds the current estimated size of an in-memory object to the output. */
|
2021-05-04 12:02:22 +00:00
|
|
|
public ProgressLoggers addInMemoryObject(String name, MemoryEstimator.HasEstimate object) {
|
2022-01-28 01:23:24 +00:00
|
|
|
loggers.add(new ProgressLogger(name, () -> format.storage(object.estimateMemoryUsageBytes(), true)));
|
2021-04-16 00:54:33 +00:00
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2021-09-10 00:46:20 +00:00
|
|
|
/** Adds the alternating worker thread pool / queue / worker thread pool stats for the pipeline to the output. */
|
2021-08-05 11:09:52 +00:00
|
|
|
public ProgressLoggers addPipelineStats(WorkerPipeline<?> pipeline) {
|
|
|
|
if (pipeline != null) {
|
|
|
|
addPipelineStats(pipeline.previous());
|
|
|
|
if (pipeline.inputQueue() != null) {
|
|
|
|
addQueueStats(pipeline.inputQueue());
|
2021-04-16 00:54:33 +00:00
|
|
|
}
|
2021-08-05 11:09:52 +00:00
|
|
|
if (pipeline.worker() != null) {
|
|
|
|
addThreadPoolStats(pipeline.name(), pipeline.worker());
|
2021-04-16 00:54:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2021-09-10 00:46:20 +00:00
|
|
|
/** Adds a linebreak to the output. */
|
2021-07-26 00:49:58 +00:00
|
|
|
public ProgressLoggers newLine() {
|
2021-09-10 00:46:20 +00:00
|
|
|
return add(System.lineSeparator());
|
2021-07-26 00:49:58 +00:00
|
|
|
}
|
|
|
|
|
2021-09-10 00:46:20 +00:00
|
|
|
/** Invoke {@link #log()} at a fixed duration until {@code future} completes. */
|
2021-08-05 01:22:20 +00:00
|
|
|
public void awaitAndLog(Future<?> future, Duration logInterval) {
|
|
|
|
while (!await(future, logInterval)) {
|
|
|
|
log();
|
|
|
|
}
|
2022-03-01 01:52:30 +00:00
|
|
|
log();
|
2021-08-05 01:22:20 +00:00
|
|
|
}
|
|
|
|
|
2021-09-10 00:46:20 +00:00
|
|
|
/** Returns true if the future is done, false if {@code duration} has elapsed. */
|
2021-08-05 01:22:20 +00:00
|
|
|
private static boolean await(Future<?> future, Duration duration) {
|
|
|
|
try {
|
|
|
|
future.get(duration.toNanos(), TimeUnit.NANOSECONDS);
|
|
|
|
return true;
|
|
|
|
} catch (InterruptedException | ExecutionException e) {
|
|
|
|
throw new IllegalStateException(e);
|
|
|
|
} catch (TimeoutException e) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-28 01:23:24 +00:00
|
|
|
private record ProgressLogger(String name, Supplier<String> fn) {
|
2021-04-16 00:54:33 +00:00
|
|
|
|
|
|
|
@Override
|
|
|
|
public String toString() {
|
|
|
|
return " " + name + ": " + fn.get();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-28 01:23:24 +00:00
|
|
|
private record WorkerPipelineLogger(Supplier<String> fn) {
|
2021-04-16 00:54:33 +00:00
|
|
|
|
|
|
|
@Override
|
|
|
|
public String toString() {
|
|
|
|
return fn.get();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|