Files
JavaUtils/FabianUtil.java
fszimnau bad7fa46eb - added Logger
- added logging capabilities to ProgressHandler classes
2025-10-01 11:17:39 +02:00

526 lines
14 KiB
Java

package FIXME;
import java.io.*;
import java.math.BigDecimal;
import java.math.MathContext;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.DecimalFormat;
import java.time.Duration;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
import java.util.logging.Level;
public class FabianUtil {
private static final Logger LOG = new Logger(FabianUtil.class);
private static final String STD_DIR_TEMP = "/FIXME/temp/";
private static final String TEST_OUTPUT_DIR = "testOutput/";
public static final String ENDING_TXT = "txt";
public static final String ENDING_JSON = "json";
public static final String ENDING_HTML = "html";
public static final String ENDING_PDF = "pdf";
public static final String ENDING_TIFF = "tiff";
private FabianUtil() {}
public static class Logger {
private final java.util.logging.Logger logger;
protected Logger(Class<?> clazz) {
this(java.util.logging.Logger.getLogger(clazz.getName()));
}
protected Logger(java.util.logging.Logger logger) {
this.logger = logger;
}
public boolean isLoggable(Level level) {
return logger.isLoggable(level);
}
public void severe(String msg) {
logger.severe(msg);
}
public void severe(String msg, Object param) {
logger.log(Level.SEVERE, msg, param);
}
public void severe(String msg, Object... params) {
logger.log(Level.SEVERE, msg, params);
}
public void severe(Supplier<String> msgSupplier) {
logger.severe(msgSupplier);
}
public void warning(String msg) {
logger.warning(msg);
}
public void warning(String msg, Object param) {
logger.log(Level.WARNING, msg, param);
}
public void warning(String msg, Object... params) {
logger.log(Level.WARNING, msg, params);
}
public void warning(Supplier<String> msgSupplier) {
logger.warning(msgSupplier);
}
public void info(String msg) {
logger.info(msg);
}
public void info(String msg, Object param) {
logger.log(Level.INFO, msg, param);
}
public void info(String msg, Object... params) {
logger.log(Level.INFO, msg, params);
}
public void info(Supplier<String> msgSupplier) {
logger.info(msgSupplier);
}
public void config(String msg) {
logger.config(msg);
}
public void config(String msg, Object param) {
logger.log(Level.CONFIG, msg, param);
}
public void config(String msg, Object... params) {
logger.log(Level.CONFIG, msg, params);
}
public void config(Supplier<String> msgSupplier) {
logger.config(msgSupplier);
}
public void fine(String msg) {
logger.fine(msg);
}
public void fine(String msg, Object param) {
logger.log(Level.FINE, msg, param);
}
public void fine(String msg, Object... params) {
logger.log(Level.FINE, msg, params);
}
public void fine(Supplier<String> msgSupplier) {
logger.fine(msgSupplier);
}
public void finer(String msg) {
logger.finer(msg);
}
public void finer(String msg, Object param) {
logger.log(Level.FINER, msg, param);
}
public void finer(String msg, Object... params) {
logger.log(Level.FINER, msg, params);
}
public void finer(Supplier<String> msgSupplier) {
logger.finer(msgSupplier);
}
public void finest(String msg) {
logger.finest(msg);
}
public void finest(String msg, Object param) {
logger.log(Level.FINEST, msg, param);
}
public void finest(String msg, Object... params) {
logger.log(Level.FINEST, msg, params);
}
public void finest(Supplier<String> msgSupplier) {
logger.finest(msgSupplier);
}
}
public static class ProgressHandler {
private static final Logger LOG = new Logger(ProgressHandler.class);
private static final DecimalFormat TIME_DECIMAL_FORMAT = new DecimalFormat("00");
protected static final int DEFAULT_LOG_DISTANCE = 50;
protected static final int DEFAULT_TIME_LOG_DISTANCE = 15;
private final ZonedDateTime startTime;
private final BigDecimal logDistance;
private final int timeLogDistance;
private BigDecimal counter = BigDecimal.ZERO;
private ZonedDateTime lastLoggedAt;
protected final PrintStream printer;
public ProgressHandler() {
this(DEFAULT_LOG_DISTANCE, DEFAULT_TIME_LOG_DISTANCE, null);
}
public ProgressHandler(int logDistance) {
this(logDistance, DEFAULT_TIME_LOG_DISTANCE, null);
}
public ProgressHandler(int logDistance, int timeLogDistance) {
this(logDistance, timeLogDistance, null);
}
public ProgressHandler(int logDistance, int timeLogDistance, PrintStream printer) {
startTime = ZonedDateTime.now();
this.logDistance = validateBD(logDistance, "Log-Distanz");
this.timeLogDistance = validateInt(timeLogDistance, "Zeitbasierte Log-Distanz");
this.printer = printer;
}
protected BigDecimal validateBD(int bd, String name) {
return BigDecimal.valueOf(validateInt(bd, name));
}
protected int validateInt(int n, String name) {
if (n < 1) {
throw new RuntimeException(name + " darf nicht 0 oder negativ sein.");
}
return n;
}
protected Logger logger() {
return LOG;
}
protected final synchronized BigDecimal counter() {
return counter;
}
protected final synchronized int timeLogDistance() {
return timeLogDistance;
}
public synchronized void tickAndLog() {
tick();
logIf();
}
protected synchronized void tick() {
counter = counter.add(BigDecimal.ONE);
}
public synchronized void logAndTick() {
logIf();
tick();
}
public synchronized void logIf() {
if (shouldLog()) {
log();
}
}
protected synchronized boolean shouldLog() {
return (printer != null || logger().isLoggable(Level.INFO)) && (counter.remainder(logDistance).signum() == 0 || isBored());
}
private synchronized boolean isBored() {
return getTimeSinceLastLogged() >= timeLogDistance;
}
protected synchronized long getTimeSinceLastLogged() {
ZonedDateTime time = lastLoggedAt == null ? startTime : lastLoggedAt;
return time.until(ZonedDateTime.now(), ChronoUnit.SECONDS);
}
public synchronized void log() {
Duration timePassed = getTimePassed();
if (printer != null) {
printer.println(counter + " Stück erledigt. " + formatDuration(timePassed) + " vergangen.");
} else {
logger().info("{} Stück erledigt. {} vergangen.", counter, formatDuration(timePassed));
}
updateLastLoggedAt();
}
protected synchronized void updateLastLoggedAt() {
lastLoggedAt = ZonedDateTime.now();
}
protected synchronized Duration getTimePassed() {
return Duration.ofSeconds(startTime.until(ZonedDateTime.now(), ChronoUnit.SECONDS));
}
protected String formatDuration(Duration dur) {
return formatNumber(dur.toHours()) + ":" + formatNumber(dur.toMinutesPart()) + ":" + formatNumber(dur.toSecondsPart());
}
protected String formatNumber(Number n) {
return TIME_DECIMAL_FORMAT.format(n == null ? 0 : n);
}
public synchronized void logFinal() {
Duration timePassed = getTimePassed();
if (printer != null) {
printer.println(counter + " Stück (100 %) erledigt. " + formatDuration(timePassed) + " vergangen.");
} else {
logger().info("{} Stück (100 %) erledigt. {} vergangen.", counter, formatDuration(timePassed));
}
}
}
public static class ProgressHandlerWithTotal extends ProgressHandler {
private static final Logger LOG = new Logger(ProgressHandlerWithTotal.class);
private static final DecimalFormat PERCENTAGE_FORMAT = new DecimalFormat("00.00");
private static final BigDecimal ONE_HUNDRED_PERCENT = BigDecimal.valueOf(100);
private final BigDecimal total;
public ProgressHandlerWithTotal(int total) {
this(DEFAULT_LOG_DISTANCE, DEFAULT_TIME_LOG_DISTANCE, total, null);
}
public ProgressHandlerWithTotal(int logDistance, int total) {
this(logDistance, DEFAULT_TIME_LOG_DISTANCE, total, null);
}
public ProgressHandlerWithTotal(int logDistance, int timeLogDistance, int total) {
this(logDistance, timeLogDistance, total, null);
}
public ProgressHandlerWithTotal(int logDistance, int timeLogDistance, int total, PrintStream printer) {
super(logDistance, timeLogDistance, printer);
this.total = validateBD(total, "Gesamt-Anzahl");
}
@Override
protected Logger logger() {
return LOG;
}
@Override
protected synchronized boolean shouldLog() {
return super.shouldLog() && counter().compareTo(total) <= 0;
}
@Override
public synchronized void log() {
BigDecimal counter = counter();
BigDecimal percentage = ONE_HUNDRED_PERCENT.divide(total, MathContext.DECIMAL64).multiply(counter);
Duration timePassed = getTimePassed();
String remainingTimePart = "";
if (counter.signum() != 0) {
BigDecimal secondsPassed = BigDecimal.valueOf(timePassed.getSeconds());
BigDecimal totalSeconds = secondsPassed.divide(counter, MathContext.DECIMAL64).multiply(total);
BigDecimal secondsToGo = totalSeconds.subtract(secondsPassed);
Duration timeToGo = Duration.of(secondsToGo.longValue(), ChronoUnit.SECONDS);
remainingTimePart = ", " + formatDuration(timeToGo) + " verbleibend";
}
String counterPrint = total.compareTo(BigDecimal.TEN) >= 0 ? formatNumber(counter) : counter.toString();
if (printer != null) {
printer.println(counterPrint + " Stück von " + total + " (" + PERCENTAGE_FORMAT.format(percentage) + " %) erledigt. "
+ formatDuration(timePassed) + " vergangen" + remainingTimePart + ".");
} else {
logger().info("{} Stück von {} ({} %) erledigt. {} vergangen{}.",
counterPrint, total, PERCENTAGE_FORMAT.format(percentage), formatDuration(timePassed), remainingTimePart);
}
updateLastLoggedAt();
}
public void logContinuouslyTimeBased() {
new Thread(this::logTimeBased).start();
}
private synchronized void logTimeBased() {
while (counter().compareTo(total) != 0) {
logIf();
long timeSinceLastLogged = getTimeSinceLastLogged();
try {
TimeUnit.SECONDS.sleep(timeSinceLastLogged - timeLogDistance());
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
return;
}
}
}
}
public static void writeTestOutput(String filename, String ending, String content) {
writeToFile(STD_DIR_TEMP + TEST_OUTPUT_DIR, filename, ending, content);
}
public static void writeTestOutput(String filename, String ending, byte[] content) {
writeToFile(STD_DIR_TEMP + TEST_OUTPUT_DIR, filename, ending, content);
}
public static void writeTestOutput(String filename, String ending, InputStream content) {
writeToFile(STD_DIR_TEMP + TEST_OUTPUT_DIR, filename, ending, content);
}
public static void writeTestOutput(String filename, String ending, Reader content) {
writeToFile(STD_DIR_TEMP + TEST_OUTPUT_DIR, filename, ending, content);
}
public static void writeDump(String ending, String content) {
writeToFile(STD_DIR_TEMP, "dump", ending, content);
}
public static void writeDump(String ending, byte[] content) {
writeToFile(STD_DIR_TEMP, "dump", ending, content);
}
public static void writeDump(String ending, InputStream content) {
writeToFile(STD_DIR_TEMP, "dump", ending, content);
}
public static void writeDump(String ending, Reader content) {
writeToFile(STD_DIR_TEMP, "dump", ending, content);
}
public static void writeDump(String filename, String ending, String content) {
writeToFile(STD_DIR_TEMP, filename, ending, content);
}
public static void writeDump(String filename, String ending, byte[] content) {
writeToFile(STD_DIR_TEMP, filename, ending, content);
}
public static void writeDump(String filename, String ending, InputStream content) {
writeToFile(STD_DIR_TEMP, filename, ending, content);
}
public static void writeDump(String filename, String ending, Reader content) {
writeToFile(STD_DIR_TEMP, filename, ending, content);
}
public static void writeToFile(String path, String filename, String ending, String content) {
writeToFile(path, filename, ending, new StringReader(content));
}
public static void writeToFile(String path, String filename, String ending, byte[] content) {
writeToFile(path, filename, ending, new InputStreamReader(new ByteArrayInputStream(content)));
}
public static void writeToFile(String path, String filename, String ending, InputStream content) {
writeToFile(path, filename, ending, new InputStreamReader(content));
}
public static void writeToFile(String path, String filename, String ending, Reader content) {
Path toFile = Paths.get(path + filename + "." + ending);
LOG.fine("Schreibe in Datei {}", toFile);
try (content) {
BufferedWriter out = Files.newBufferedWriter(toFile);
content.transferTo(out);
/* varianten: selber pipen:
OutputStream out = Files.newOutputStream(Paths.get(path + filename + "." + ending));
content.transferTo(out);
oder mit reader/ writer:
BufferedWriter out = Files.newBufferedWriter(Paths.get(path + filename + "." + ending));
content.transferTo(out);
oder mit byte-array:
Files.write(Paths.get(path + filename + "." + ending), content);
*/
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}