package net.tuis.ubench;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.Thread.UncaughtExceptionHandler;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.atomic.AtomicLong;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.LongStream;
import java.util.stream.Stream;
/**
* Collection of common static utility methods used in other areas of the
* package.
*
* @author rolf
*
*/
public final class UUtils {
private UUtils() {
// private constructor, no instances possible.
}
// Create a LOGGER instance for the **PACKAGE**.
// This will be the owner of the ubench namespace.
private static final Logger LOGGER = Logger.getLogger(UScale.class.getPackage().getName());
/**
* Simple wrapper that forces initialization of the package-level LOGGER
* instance.
*
* @param clazz
* The class to be logged.
* @return A Logger using the name of the class as its hierarchy.
*/
public static Logger getLogger(final Class<?> clazz) {
if (!clazz.getPackage().getName().startsWith(LOGGER.getName())) {
throw new IllegalArgumentException(String.format("Class %s is not a child of the package %s",
clazz.getName(), LOGGER.getName()));
}
LOGGER.fine(() -> String.format("Locating logger for class %s", clazz));
return Logger.getLogger(clazz.getName());
}
/**
* Enable regular logging for all UBench code at the specified level.
*
* @param level
* the level to log for.
*/
public static void setStandaloneLogging(Level level) {
LOGGER.setUseParentHandlers(false);
for (Handler h : LOGGER.getHandlers()) {
LOGGER.removeHandler(h);
}
StdoutHandler handler = new StdoutHandler();
handler.setFormatter(new InlineFormatter());
LOGGER.addHandler(handler);
final UncaughtExceptionHandler ueh = Thread.getDefaultUncaughtExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler((t, e) -> {
LOGGER.log(Level.SEVERE, "Uncaught Exception in thread " + t.getName(), e);
if (ueh != null) {
ueh.uncaughtException(t, e);
}
});
setLogLevel(level);
}
/**
* Enable the specified debug level messages to be output. Note that both
* this Logger and whatever Handler you use, have to be set to enable the
* required log level for the handler to output the messages. If this UBench
* code is logging 'stand alone' then this method will also change the
* output level of the log handlers.
*
* @param level The log level to activate for future log levels.
*/
public static void setLogLevel(Level level) {
// all other ubench loggers inherit from here.
LOGGER.finer("Changing logging from " + LOGGER.getLevel());
LOGGER.setLevel(level);
if (!LOGGER.getUseParentHandlers()) {
LOGGER.setLevel(level);
Stream.of(LOGGER.getHandlers()).forEach(h -> h.setLevel(level));
}
LOGGER.finer("Changed logging to " + LOGGER.getLevel());
}
private static final AtomicLong NANO_TICK = new AtomicLong(-1);
/**
* The minimum increment that the System.nanotime() can do. The nanotime
* value returned by the system does not necessarily increment by 1, this
* value indicates the smallest observed increment of the timer.
* @return The number of nanoseconds in the smallest recorded clock tick.
*/
public static long getNanoTick() {
synchronized(NANO_TICK) {
long tick = NANO_TICK.get();
if (tick > 0) {
return tick;
}
tick = computeTick();
NANO_TICK.set(tick);
return tick;
}
}
private static final long singleTick() {
final long start = System.nanoTime();
long end = start;
while (end == start) {
end = System.nanoTime();
}
return end - start;
}
private static long computeTick() {
final long ticklen = LongStream.range(0, 1000).map(i -> singleTick()).min().getAsLong();
LOGGER.fine(() -> String.format("Incremental System.nanotime() tick is %d", ticklen));
return ticklen;
}
/**
* Load a resource stored in the classpath, as a String.
*
* @param path
* the system resource to read
* @return the resource as a String.
*/
public static String readResource(String path) {
final long start = System.nanoTime();
try (InputStream is = UScale.class.getClassLoader().getResourceAsStream(path);) {
int len = 0;
byte[] buffer = new byte[2048];
ByteArrayOutputStream baos = new ByteArrayOutputStream();
while ((len = is.read(buffer)) >= 0) {
baos.write(buffer, 0, len);
}
return new String(baos.toByteArray(), StandardCharsets.UTF_8);
} catch (IOException e) {
LOGGER.log(Level.WARNING, e, () -> "IOException loading resource " + path);
throw new IllegalStateException("Unable to read class loaded stream " + path, e);
} catch (RuntimeException re) {
LOGGER.log(Level.WARNING, re, () -> "Unexpected exception loading resource " + path);
throw re;
} finally {
LOGGER.fine(() -> String.format("Loaded resource %s in %.3fms", path,
(System.nanoTime() - start) / 1000000.0));
}
}
}