package cz.fidentis.analyst; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PipedInputStream; import java.io.PipedOutputStream; import java.io.PrintStream; import java.text.SimpleDateFormat; import java.time.Duration; import java.util.logging.Level; /** * A logging class that can be used from anywhere to display debugging or other * messages. * <p> * Messages are printed into the system standard output by default. But can be also * redirected to system error output or into the pipe. * </p> * <p> * If the messages are redirected into pipe, then <b>a consumer has to be running * concurrently</b> and reading the data through the {@link #read()} method. * <b>Otherwise, the program can freeze due to the full buffer!</b> * </p> * <p> * FIDENTIS application automatically redirects logged messages to the output window * via the {@code OutputWindowThread}. * </p> * <p> * <b>Usage:</b> * <ul> * <li>To print a message, use {@code Logger.print("message")}. A timestamp is added automatically.</li> * <li>To print message together with duration, call {@code Logger log = Logger.measureTime();}, * then call the measured operations and finally call {@code log.printDuration("message")}. * a timestamp and measured duration are added to the log message automatically.</li> * </ul> * </p> * * @author Radek Oslejsek */ public final class Logger { private static final int PIPE_SIZE = 10240; // default is 1024 private static PrintStream out = System.out; private static BufferedReader in; private long lastTime = System.currentTimeMillis(); private Logger() { } /** * Redirects messages into a pipe. * <b>A consumer has to be running concurrently</b> and reading the messages * through the {@link #read()} method. * Otherwise, the program can freeze due to the full buffer! */ public static synchronized void redirectToPipe() { if (in != null) { return; } PipedOutputStream pOut = new PipedOutputStream(); out = new PrintStream(pOut); try { in = new BufferedReader(new InputStreamReader(new PipedInputStream(pOut, PIPE_SIZE))); } catch (IOException ex) { java.util.logging.Logger.getLogger(Logger.class.getName()).log(Level.SEVERE, null, ex); } } /** * Redirects messages into the system standard output. */ public static synchronized void redirectToStdOut() { out = System.out; in = null; } /** * Redirects messages into the system error output. */ public static synchronized void redirectToStdErr() { out = System.err; in = null; } /** * Returns an event message. If the logger is redirected to standard or error output, * then {@code null} is returned. Also, if the is no message, {@code null} is returned. * * @return Event message or {@code null} * @throws IOException on error in reading the message buffer. */ public static String read() throws IOException { if (in == null/* || !in.ready()*/) { return null; } return in.readLine(); } /** * Prints a message. The log time added automatically. * * @param msg Message to be logged */ public static void print(String msg) { String outMsg = new SimpleDateFormat("HH:mm:ss").format(System.currentTimeMillis()) + " [duration unknown]: " + msg.trim(); out.println(outMsg); out.flush(); } /** * Starts measuring of some operation. * Call {@link #printDuration(java.lang.String)} on returned object * to print the measured duration. * * @return An object used to print measured duration and message */ public static Logger measureTime() { Logger ret = new Logger(); ret.lastTime = System.currentTimeMillis(); return ret; } /** * Prints the message about an operation and the duration of the operation. * The duration is computed as the difference between current system time and * the time of calling {@link #measureTime()}. The time difference is measured in * milliseconds and printed as [+mm:ss.SSS] where mm=minutes, ss=seconds, * and SSS=milliseconds. * * @param msg Message to be printed */ public void printDuration(String msg) { Duration duration = Duration.ofMillis(System.currentTimeMillis() - lastTime); String outMsg = new SimpleDateFormat("HH:mm:ss").format(System.currentTimeMillis()) + " [duration " + String.format( "%02d:%02d.%03d", duration.toMinutes(), duration.toSecondsPart(), duration.toMillisPart()) + "]: " + msg.trim(); out.println(outMsg); out.flush(); } }