2022-01-30 00:39:17 -07:00
|
|
|
package frc4388.utility;
|
|
|
|
|
|
|
|
|
|
import static org.fusesource.jansi.Ansi.ansi;
|
|
|
|
|
|
2022-03-21 12:29:03 -06:00
|
|
|
import java.io.ByteArrayOutputStream;
|
2022-01-30 00:39:17 -07:00
|
|
|
import java.io.IOException;
|
|
|
|
|
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-04-04 00:55:42 -06:00
|
|
|
import java.util.logging.ConsoleHandler;
|
2022-01-30 00:39:17 -07:00
|
|
|
import java.util.logging.Formatter;
|
2022-03-21 12:29:03 -06:00
|
|
|
import java.util.logging.Handler;
|
2022-01-30 00:39:17 -07:00
|
|
|
import java.util.logging.Level;
|
|
|
|
|
import java.util.logging.LogManager;
|
|
|
|
|
import java.util.logging.LogRecord;
|
|
|
|
|
import java.util.logging.Logger;
|
2022-03-21 12:29:03 -06:00
|
|
|
import java.util.logging.StreamHandler;
|
2022-01-30 00:39:17 -07:00
|
|
|
|
2022-03-21 15:32:35 -06:00
|
|
|
import com.diffplug.common.base.DurianPlugins;
|
|
|
|
|
import com.diffplug.common.base.Errors;
|
|
|
|
|
|
2022-01-30 00:39:17 -07:00
|
|
|
import org.fusesource.jansi.Ansi.Attribute;
|
|
|
|
|
import org.fusesource.jansi.Ansi.Color;
|
|
|
|
|
import org.fusesource.jansi.AnsiConsole;
|
2022-03-21 12:29:03 -06:00
|
|
|
import org.fusesource.jansi.AnsiPrintStream;
|
|
|
|
|
|
|
|
|
|
public class AnsiLogging {
|
2022-04-04 00:55:42 -06:00
|
|
|
public static final boolean ENABLED = true;
|
|
|
|
|
private static final AnsiPrintStream ANSI_CONSOLE_STREAM = AnsiConsole.err();
|
2022-03-21 12:29:03 -06:00
|
|
|
private static final Level LEVEL = Level.ALL;
|
2022-01-30 00:39:17 -07:00
|
|
|
|
2022-04-04 00:55:42 -06:00
|
|
|
public static Handler halLoggerHandler = new ConsoleHandler();
|
|
|
|
|
|
2022-01-30 00:39:17 -07:00
|
|
|
public static void systemInstall() {
|
2022-04-04 00:55:42 -06:00
|
|
|
if (!ENABLED) return;
|
2022-01-30 00:39:17 -07:00
|
|
|
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":
|
2022-03-21 12:29:03 -06:00
|
|
|
return LEVEL.getName();
|
2022-02-25 01:33:32 -07:00
|
|
|
case "handlers":
|
2022-03-21 12:29:03 -06:00
|
|
|
return LoggingAnsiConsoleHandler.class.getName();
|
2022-02-25 01:33:32 -07:00
|
|
|
default:
|
|
|
|
|
return n;
|
2022-01-30 00:39:17 -07:00
|
|
|
}
|
|
|
|
|
});
|
2022-03-21 12:29:03 -06:00
|
|
|
// Set the console to process ANSI escape codes.
|
|
|
|
|
ANSI_CONSOLE_STREAM.install();
|
|
|
|
|
// Sends standard output stream messages through a logger.
|
|
|
|
|
System.setOut(printStreamLogger(Logger.getGlobal(), "out", Level.INFO));
|
|
|
|
|
// Sends standard error output stream messages through a logger.
|
|
|
|
|
System.setErr(printStreamLogger(Logger.getGlobal(), "err", Level.SEVERE));
|
2022-03-21 15:32:35 -06:00
|
|
|
// This is registering a plugin that will log Durian errors to the console using a logger.
|
|
|
|
|
DurianPlugins.register(Errors.Plugins.Log.class, e -> Logger.getLogger(e.getStackTrace()[0].getClassName().substring(e.getStackTrace()[0].getClassName().lastIndexOf('.') + 1)).log(Level.SEVERE, e, e::getLocalizedMessage));
|
2022-04-04 00:55:42 -06:00
|
|
|
// Store the handler for HAL to use when sending errors to DriverStation.
|
|
|
|
|
halLoggerHandler = new LoggingAnsiConsoleHandler();
|
2022-01-30 00:39:17 -07:00
|
|
|
} catch (IOException exception) {
|
|
|
|
|
exception.printStackTrace(AnsiConsole.sysErr());
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-02-25 01:33:32 -07:00
|
|
|
|
2022-03-03 23:56:08 -07:00
|
|
|
/**
|
2022-03-21 12:29:03 -06:00
|
|
|
* This class is a StreamHandler that uses ANSI escape codes to colorize the log messages
|
2022-03-03 23:56:08 -07:00
|
|
|
*/
|
2022-03-21 12:29:03 -06:00
|
|
|
public static class LoggingAnsiConsoleHandler extends StreamHandler {
|
|
|
|
|
public LoggingAnsiConsoleHandler() {
|
|
|
|
|
super(ANSI_CONSOLE_STREAM, new LoggingAnsiFormatter());
|
|
|
|
|
setLevel(LEVEL);
|
2022-01-30 00:39:17 -07:00
|
|
|
}
|
2022-02-25 01:33:32 -07:00
|
|
|
|
2022-03-21 12:29:03 -06:00
|
|
|
private static class LoggingAnsiFormatter extends Formatter {
|
|
|
|
|
private static final ZoneId ZONE_ID = ZoneId.systemDefault();
|
|
|
|
|
// Specify colors for the different message levels.
|
|
|
|
|
private static final Map<Integer, String> LEVEL_COLORS = Map.of(Level.OFF.intValue(), "", Level.SEVERE.intValue(), ansi().fgBright(Color.RED).toString(), Level.WARNING.intValue(), ansi().fgBright(Color.YELLOW).toString(), Level.INFO.intValue(), ansi().fg(Color.GREEN).toString(), Level.CONFIG.intValue(), ansi().fgBright(Color.BLUE).toString(), Level.FINE.intValue(), ansi().fg(Color.CYAN).toString(), Level.FINER.intValue(), ansi().fg(Color.MAGENTA).toString(), Level.FINEST.intValue(), ansi().fgBright(Color.BLACK).toString(), Level.ALL.intValue(), ansi().fg(Color.DEFAULT).toString());
|
|
|
|
|
private static final String FORMAT = ansi().a("%s").bold().a(Attribute.UNDERLINE).a("[%tb %<td %<tk:%<tM:%<tS.%<tL] %s %s:").boldOff().a(Attribute.UNDERLINE_OFF).a("%s%s").a(Attribute.INTENSITY_FAINT).a("%s").boldOff().reset().newline().toString();
|
|
|
|
|
private static final String RESET = ansi().reset().toString();
|
2022-02-25 01:33:32 -07:00
|
|
|
|
2022-03-21 12:29:03 -06:00
|
|
|
private static String makeStackTraceString(Throwable throwable) {
|
2022-02-25 01:33:32 -07:00
|
|
|
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-03-21 12:29:03 -06:00
|
|
|
ZonedDateTime time = ZonedDateTime.ofInstant(logRecord.getInstant(), ZONE_ID);
|
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-03-21 12:29:03 -06:00
|
|
|
String throwable = Optional.ofNullable(logRecord.getThrown()).map(LoggingAnsiFormatter::makeStackTraceString).orElse("");
|
2022-03-03 23:56:08 -07:00
|
|
|
// Select the appropriate format string for the log level.
|
2022-03-21 12:29:03 -06:00
|
|
|
String color = LEVEL_COLORS.getOrDefault(logRecord.getLevel().intValue(), LEVEL_COLORS.get(Level.ALL.intValue()));
|
|
|
|
|
|
|
|
|
|
boolean multiline = message.lines().skip(1).findAny().isPresent();
|
|
|
|
|
boolean ansi = message.contains("\033");
|
|
|
|
|
String prefix = (ansi ? RESET : "") + (multiline ? System.lineSeparator() : " ");
|
|
|
|
|
|
2022-03-03 23:56:08 -07:00
|
|
|
// Format the log message.
|
2022-03-21 12:29:03 -06:00
|
|
|
return String.format(FORMAT, color, time, source, logRecord.getLevel().getLocalizedName(), prefix, message, throwable);
|
2022-01-30 00:39:17 -07:00
|
|
|
}
|
|
|
|
|
|
2022-03-21 12:29:03 -06:00
|
|
|
@Override
|
|
|
|
|
public String getHead(Handler h) {
|
|
|
|
|
return String.format("%s%s level set to %s%s%n", LEVEL_COLORS.get(h.getLevel().intValue()), h.getClass().getSimpleName(), h.getLevel().getName(), RESET);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public synchronized void publish(LogRecord logRecord) {
|
|
|
|
|
super.publish(logRecord);
|
|
|
|
|
flush();
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-01-30 00:39:17 -07:00
|
|
|
|
2022-03-21 12:29:03 -06:00
|
|
|
private static PrintStream printStreamLogger(Logger logger, String source, Level level) {
|
|
|
|
|
return new PrintStream(new ByteArrayOutputStream() {
|
2022-01-30 00:39:17 -07:00
|
|
|
@Override
|
2022-03-21 12:29:03 -06:00
|
|
|
public void flush() throws IOException {
|
|
|
|
|
String s = toString();
|
|
|
|
|
if (!s.isBlank()) logger.logp(level, null, source, toString());
|
|
|
|
|
reset();
|
2022-01-30 00:39:17 -07:00
|
|
|
}
|
2022-03-21 12:29:03 -06:00
|
|
|
}, true);
|
2022-01-30 00:39:17 -07:00
|
|
|
}
|
|
|
|
|
}
|