2022-01-30 00:39:17 -07:00
|
|
|
package frc4388.utility;
|
|
|
|
|
|
|
|
|
|
import static org.fusesource.jansi.Ansi.ansi;
|
|
|
|
|
|
|
|
|
|
import java.io.IOException;
|
|
|
|
|
import java.io.OutputStream;
|
|
|
|
|
import java.io.PrintStream;
|
|
|
|
|
import java.io.PrintWriter;
|
|
|
|
|
import java.io.StringWriter;
|
|
|
|
|
import java.time.ZoneId;
|
|
|
|
|
import java.time.ZonedDateTime;
|
2022-02-25 01:33:32 -07:00
|
|
|
import java.util.Map;
|
|
|
|
|
import java.util.Optional;
|
2022-01-30 00:39:17 -07:00
|
|
|
import java.util.logging.ConsoleHandler;
|
|
|
|
|
import java.util.logging.Formatter;
|
|
|
|
|
import java.util.logging.Level;
|
|
|
|
|
import java.util.logging.LogManager;
|
|
|
|
|
import java.util.logging.LogRecord;
|
|
|
|
|
import java.util.logging.Logger;
|
|
|
|
|
|
|
|
|
|
import org.fusesource.jansi.Ansi;
|
|
|
|
|
import org.fusesource.jansi.Ansi.Attribute;
|
|
|
|
|
import org.fusesource.jansi.Ansi.Color;
|
|
|
|
|
import org.fusesource.jansi.AnsiConsole;
|
|
|
|
|
|
|
|
|
|
public class AnsiLogging extends ConsoleHandler {
|
|
|
|
|
public static void systemInstall() {
|
|
|
|
|
try {
|
2022-03-01 17:29:13 -07:00
|
|
|
// Configure java.util.logging.Logger to output additional colored information.
|
2022-01-30 00:39:17 -07:00
|
|
|
LogManager.getLogManager().updateConfiguration(key -> (o, n) -> {
|
|
|
|
|
switch (key) {
|
2022-02-25 01:33:32 -07:00
|
|
|
case ".level":
|
|
|
|
|
return Level.ALL.getName();
|
|
|
|
|
case "handlers":
|
|
|
|
|
return AnsiColorConsoleHandler.class.getName();
|
|
|
|
|
default:
|
|
|
|
|
return n;
|
2022-01-30 00:39:17 -07:00
|
|
|
}
|
|
|
|
|
});
|
2022-03-01 17:29:13 -07:00
|
|
|
// Replace standard output streams with org.fusesource.jansi.AnsiPrintStreams.
|
2022-01-30 00:39:17 -07:00
|
|
|
AnsiConsole.systemInstall();
|
2022-03-01 17:29:13 -07:00
|
|
|
// Replace standard output stream with java.util.logging.Logger.
|
2022-02-25 01:33:32 -07:00
|
|
|
System.setOut(printStreamLogger(Logger.getGlobal(), Level.INFO));
|
2022-03-01 17:29:13 -07:00
|
|
|
// Replace standard error output stream with java.util.logging.Logger.
|
2022-01-30 00:39:17 -07:00
|
|
|
System.setErr(printStreamLogger(Logger.getGlobal(), Level.SEVERE));
|
|
|
|
|
} catch (IOException exception) {
|
|
|
|
|
exception.printStackTrace(AnsiConsole.sysErr());
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-02-25 01:33:32 -07:00
|
|
|
|
2022-03-03 23:56:08 -07:00
|
|
|
/**
|
|
|
|
|
* This class is a ConsoleHandler that uses ANSI escape codes to colorize the output
|
|
|
|
|
*/
|
2022-01-30 00:39:17 -07:00
|
|
|
public static class AnsiColorConsoleHandler extends ConsoleHandler {
|
|
|
|
|
@Override
|
|
|
|
|
public void publish(LogRecord logRecord) {
|
|
|
|
|
AnsiConsole.err().print(getFormatter().format(logRecord));
|
|
|
|
|
AnsiConsole.err().flush();
|
|
|
|
|
}
|
2022-02-25 01:33:32 -07:00
|
|
|
|
2022-01-30 00:39:17 -07:00
|
|
|
@Override
|
|
|
|
|
public Formatter getFormatter() {
|
|
|
|
|
return formatter;
|
|
|
|
|
}
|
2022-02-25 01:33:32 -07:00
|
|
|
|
2022-01-30 00:39:17 -07:00
|
|
|
private static final Formatter formatter = new Formatter() {
|
2022-02-25 01:33:32 -07:00
|
|
|
private final ZoneId zoneId = ZoneId.systemDefault();
|
2022-03-01 17:29:13 -07:00
|
|
|
// Specify and prepare formats for messages
|
2022-02-25 01:33:32 -07:00
|
|
|
private final Map<Integer, String> levelColors = Map.of(
|
|
|
|
|
Level.OFF.intValue(), "",
|
2022-03-01 17:29:13 -07:00
|
|
|
Level.SEVERE.intValue(), makeMessageFormatString(ansi().fgBright(Color.RED)),
|
|
|
|
|
Level.WARNING.intValue(), makeMessageFormatString(ansi().fgBright(Color.YELLOW)),
|
|
|
|
|
Level.INFO.intValue(), makeMessageFormatString(ansi().fg(Color.GREEN)),
|
|
|
|
|
Level.CONFIG.intValue(), makeMessageFormatString(ansi().fgBright(Color.BLUE)),
|
|
|
|
|
Level.FINE.intValue(), makeMessageFormatString(ansi().fg(Color.CYAN)),
|
|
|
|
|
Level.FINER.intValue(), makeMessageFormatString(ansi().fg(Color.MAGENTA)),
|
|
|
|
|
Level.FINEST.intValue(), makeMessageFormatString(ansi().fgBright(Color.BLACK)),
|
|
|
|
|
Level.ALL.intValue(), makeMessageFormatString(ansi().fg(Color.DEFAULT))
|
2022-02-25 01:33:32 -07:00
|
|
|
);
|
|
|
|
|
|
2022-03-01 17:29:13 -07:00
|
|
|
private String makeMessageFormatString(Ansi base) {
|
2022-02-25 01:33:32 -07:00
|
|
|
return base.bold().a(Attribute.UNDERLINE).a("[%1$tb %1$td %1$tk:%1$tM:%1$tS.%1$tL] %2$s %3$s:").boldOff().a(Attribute.UNDERLINE_OFF).a("%4$s%5$s").a(Attribute.INTENSITY_FAINT).a("%6$s").reset().a("%n").toString();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private String makeStackTraceString(Throwable throwable) {
|
|
|
|
|
StringWriter stringWriter = new StringWriter();
|
|
|
|
|
try (PrintWriter printWriter = new PrintWriter(stringWriter)) {
|
|
|
|
|
printWriter.println();
|
|
|
|
|
throwable.printStackTrace(printWriter);
|
|
|
|
|
}
|
|
|
|
|
return stringWriter.toString();
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-30 00:39:17 -07:00
|
|
|
@Override
|
|
|
|
|
public String format(LogRecord logRecord) {
|
2022-02-25 01:33:32 -07:00
|
|
|
ZonedDateTime time = ZonedDateTime.ofInstant(logRecord.getInstant(), zoneId);
|
2022-03-03 23:56:08 -07:00
|
|
|
// Get the logger name, source class name, and/or source method name.
|
2022-02-25 01:33:32 -07:00
|
|
|
String source = Optional.ofNullable(logRecord.getLoggerName()).or(() -> Optional.ofNullable(logRecord.getSourceClassName())).map(s -> s + " ").orElse("") + Optional.ofNullable(logRecord.getSourceMethodName()).orElse("");
|
2022-01-30 00:39:17 -07:00
|
|
|
String message = formatMessage(logRecord);
|
2022-03-03 23:56:08 -07:00
|
|
|
// Get the stack trace of the exception if it was thrown.
|
2022-02-25 01:33:32 -07:00
|
|
|
String throwable = Optional.ofNullable(logRecord.getThrown()).map(this::makeStackTraceString).orElse("");
|
2022-03-03 23:56:08 -07:00
|
|
|
// Select the appropriate format string for the log level.
|
2022-02-25 01:33:32 -07:00
|
|
|
String format = levelColors.getOrDefault(logRecord.getLevel().intValue(), levelColors.get(Level.ALL.intValue()));
|
2022-03-03 23:56:08 -07:00
|
|
|
// Format the log message.
|
|
|
|
|
return String.format(format, time, source, logRecord.getLevel().getLocalizedName(), message.lines().count() > 1 ? System.lineSeparator() : " ", message.contains("\033") ? "\033[0m" + message : message, throwable);
|
2022-01-30 00:39:17 -07:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-03 23:56:08 -07:00
|
|
|
/**
|
|
|
|
|
* Create a PrintStream that writes to the given logger at the given level
|
|
|
|
|
*
|
|
|
|
|
* @param logger The logger to use.
|
|
|
|
|
* @param level The level of the log message.
|
|
|
|
|
* @return A new PrintStream object.
|
|
|
|
|
*/
|
2022-01-30 00:39:17 -07:00
|
|
|
private static PrintStream printStreamLogger(Logger logger, Level level) {
|
|
|
|
|
return new PrintStream(new OutputStream() {
|
2022-03-03 23:56:08 -07:00
|
|
|
// This is a buffer that is used to store the characters that are written to the PrintStream.
|
2022-01-30 00:39:17 -07:00
|
|
|
private final StringBuilder stringBuilder = new StringBuilder();
|
|
|
|
|
|
2022-03-03 23:56:08 -07:00
|
|
|
/**
|
|
|
|
|
* If the character is a newline, flush the buffer to the logger, otherwise add the character to the
|
|
|
|
|
* buffer.
|
|
|
|
|
*/
|
2022-01-30 00:39:17 -07:00
|
|
|
@Override
|
2022-02-25 01:33:32 -07:00
|
|
|
public void write(int i) throws IOException {
|
2022-01-30 00:39:17 -07:00
|
|
|
if (i == '\n') {
|
|
|
|
|
logger.log(level, stringBuilder::toString);
|
|
|
|
|
stringBuilder.setLength(0);
|
2022-03-03 23:56:08 -07:00
|
|
|
} else stringBuilder.appendCodePoint(i);
|
2022-01-30 00:39:17 -07:00
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|