You've already forked Arcturus-Morningstar-Extended
mirror of
https://github.com/duckietm/Arcturus-Morningstar-Extended.git
synced 2026-06-19 15:06:19 +00:00
style(logging): colorize adaptive console logs
Route console log level and logger columns through custom Logback converters so terminals with ANSI support get colored severity badges and compact colored class names. Keep the same habbo.console.style auto/ansi/plain behavior as the startup splash, including plain fallback for non-interactive output, NO_COLOR, and legacy Windows console paths. The file appenders keep their existing verbose patterns unchanged, so debug/error log files remain plain and grep-friendly. Cover the level formatter, logger formatter, override behavior, and Logback pattern wiring with tests.
This commit is contained in:
@@ -18,6 +18,7 @@ import com.eu.habbo.plugin.events.emulator.EmulatorStartShutdownEvent;
|
||||
import com.eu.habbo.plugin.events.emulator.EmulatorStoppedEvent;
|
||||
import com.eu.habbo.threading.ThreadPooling;
|
||||
import com.eu.habbo.util.imager.badges.BadgeImager;
|
||||
import com.eu.habbo.util.logback.ConsoleStyle;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -368,34 +369,7 @@ public final class Emulator {
|
||||
}
|
||||
|
||||
static boolean shouldStyleConsole(Map<String, String> environment, boolean interactiveConsole, String osName, String styleProperty) {
|
||||
String style = styleProperty == null ? "auto" : styleProperty.trim().toLowerCase(Locale.ROOT);
|
||||
if (style.equals("ansi") || style.equals("color") || style.equals("colours") || style.equals("colors")) {
|
||||
return true;
|
||||
}
|
||||
if (style.equals("plain") || style.equals("none") || style.equals("false") || style.equals("off")) {
|
||||
return false;
|
||||
}
|
||||
if (!interactiveConsole) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Map<String, String> env = environment == null ? Collections.emptyMap() : environment;
|
||||
if (env.containsKey("NO_COLOR")) {
|
||||
return false;
|
||||
}
|
||||
if (env.containsKey("WT_SESSION") || env.containsKey("ANSICON") || "ON".equalsIgnoreCase(env.get("ConEmuANSI"))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
String term = env.getOrDefault("TERM", "");
|
||||
if (term.equalsIgnoreCase("dumb")) {
|
||||
return false;
|
||||
}
|
||||
if (!term.isBlank() && (term.contains("xterm") || term.contains("ansi") || term.contains("screen") || term.contains("tmux"))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return osName == null || !osName.toLowerCase(Locale.ROOT).startsWith("windows");
|
||||
return ConsoleStyle.isEnabled(environment, interactiveConsole, osName, styleProperty);
|
||||
}
|
||||
|
||||
private static String fit(String value, int width) {
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.eu.habbo.util.logback;
|
||||
|
||||
import ch.qos.logback.classic.pattern.ClassicConverter;
|
||||
import ch.qos.logback.classic.spi.ILoggingEvent;
|
||||
|
||||
public class ConsoleLevelConverter extends ClassicConverter {
|
||||
@Override
|
||||
public String convert(ILoggingEvent event) {
|
||||
return ConsoleStyle.level(event == null ? null : event.getLevel(), ConsoleStyle.isRuntimeEnabled());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.eu.habbo.util.logback;
|
||||
|
||||
import ch.qos.logback.classic.pattern.ClassicConverter;
|
||||
import ch.qos.logback.classic.spi.ILoggingEvent;
|
||||
|
||||
public class ConsoleLoggerConverter extends ClassicConverter {
|
||||
@Override
|
||||
public String convert(ILoggingEvent event) {
|
||||
return ConsoleStyle.logger(event == null ? "" : event.getLoggerName(), ConsoleStyle.isRuntimeEnabled());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
package com.eu.habbo.util.logback;
|
||||
|
||||
import ch.qos.logback.classic.Level;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
public final class ConsoleStyle {
|
||||
private static final String ANSI_RESET = "\u001B[0m";
|
||||
private static final String ANSI_BOLD = "\u001B[1m";
|
||||
private static final String ANSI_DIM = "\u001B[2m";
|
||||
private static final String ANSI_CYAN = "\u001B[36m";
|
||||
private static final String ANSI_GREEN = "\u001B[32m";
|
||||
private static final String ANSI_YELLOW = "\u001B[33m";
|
||||
private static final String ANSI_RED = "\u001B[31m";
|
||||
|
||||
private static final int LOGGER_WIDTH = 22;
|
||||
|
||||
private ConsoleStyle() {
|
||||
}
|
||||
|
||||
public static boolean isRuntimeEnabled() {
|
||||
return isEnabled(
|
||||
System.getenv(),
|
||||
System.console() != null,
|
||||
System.getProperty("os.name", "Unknown"),
|
||||
System.getProperty("habbo.console.style", "auto"));
|
||||
}
|
||||
|
||||
public static boolean isEnabled(Map<String, String> environment, boolean interactiveConsole, String osName, String styleProperty) {
|
||||
String style = styleProperty == null ? "auto" : styleProperty.trim().toLowerCase(Locale.ROOT);
|
||||
if (style.equals("ansi") || style.equals("color") || style.equals("colours") || style.equals("colors")) {
|
||||
return true;
|
||||
}
|
||||
if (style.equals("plain") || style.equals("none") || style.equals("false") || style.equals("off")) {
|
||||
return false;
|
||||
}
|
||||
if (!interactiveConsole) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Map<String, String> env = environment == null ? Collections.emptyMap() : environment;
|
||||
if (env.containsKey("NO_COLOR")) {
|
||||
return false;
|
||||
}
|
||||
if (env.containsKey("WT_SESSION") || env.containsKey("ANSICON") || "ON".equalsIgnoreCase(env.get("ConEmuANSI"))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
String term = env.getOrDefault("TERM", "");
|
||||
if (term.equalsIgnoreCase("dumb")) {
|
||||
return false;
|
||||
}
|
||||
if (!term.isBlank() && (term.contains("xterm") || term.contains("ansi") || term.contains("screen") || term.contains("tmux"))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return osName == null || !osName.toLowerCase(Locale.ROOT).startsWith("windows");
|
||||
}
|
||||
|
||||
public static String level(Level level, boolean styled) {
|
||||
String name = level == null ? "INFO" : level.toString();
|
||||
String plain = String.format("%-5s", name);
|
||||
|
||||
if (!styled) {
|
||||
return plain;
|
||||
}
|
||||
|
||||
if (Level.ERROR.equals(level)) {
|
||||
return ANSI_BOLD + ANSI_RED + "[x] " + plain + ANSI_RESET;
|
||||
}
|
||||
if (Level.WARN.equals(level)) {
|
||||
return ANSI_YELLOW + "[!] " + plain + ANSI_RESET;
|
||||
}
|
||||
if (Level.DEBUG.equals(level) || Level.TRACE.equals(level)) {
|
||||
return ANSI_DIM + "[.] " + plain + ANSI_RESET;
|
||||
}
|
||||
|
||||
return ANSI_GREEN + "[i] " + plain + ANSI_RESET;
|
||||
}
|
||||
|
||||
public static String logger(String loggerName, boolean styled) {
|
||||
String compact = compactLoggerName(loggerName);
|
||||
String plain = fit(compact, LOGGER_WIDTH);
|
||||
return styled ? ANSI_CYAN + plain + ANSI_RESET : plain;
|
||||
}
|
||||
|
||||
private static String compactLoggerName(String loggerName) {
|
||||
if (loggerName == null || loggerName.isBlank()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
int lastDot = loggerName.lastIndexOf('.');
|
||||
return lastDot >= 0 ? loggerName.substring(lastDot + 1) : loggerName;
|
||||
}
|
||||
|
||||
private static String fit(String value, int width) {
|
||||
String safe = value == null ? "" : value;
|
||||
if (safe.length() > width) {
|
||||
return safe.substring(0, Math.max(0, width - 3)) + "...";
|
||||
}
|
||||
|
||||
return String.format("%-" + width + "s", safe);
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration>
|
||||
<conversionRule conversionWord="morningstarLevel" class="com.eu.habbo.util.logback.ConsoleLevelConverter" />
|
||||
<conversionRule conversionWord="morningstarLogger" class="com.eu.habbo.util.logback.ConsoleLoggerConverter" />
|
||||
|
||||
<appender name="Console" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>%d{HH:mm:ss.SSS} %-5level [%-12thread] %-22logger{0} | %msg%n</pattern>
|
||||
<pattern>%d{HH:mm:ss.SSS} %morningstarLevel [%-12thread] %morningstarLogger | %msg%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
|
||||
@@ -13,7 +13,8 @@ class ConsoleLogbackLayoutTest {
|
||||
void consolePatternKeepsStartupMessagesReadable() throws Exception {
|
||||
String logback = Files.readString(Path.of("src/main/resources/logback.xml"));
|
||||
|
||||
assertTrue(logback.contains("%-22logger{0}"), "console should show compact class names");
|
||||
assertTrue(logback.contains("morningstarLevel"), "console should use the adaptive level formatter");
|
||||
assertTrue(logback.contains("morningstarLogger"), "console should use the adaptive logger formatter");
|
||||
assertTrue(logback.contains("| %msg%n"), "console should leave a clear message column");
|
||||
assertFalse(logback.contains("%-36logger{36}"), "wide package loggers waste console space");
|
||||
}
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
package com.eu.habbo.util.logback;
|
||||
|
||||
import ch.qos.logback.classic.Level;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class ConsoleStyleTest {
|
||||
@Test
|
||||
void formatsLevelWithIconAndColorWhenStyled() {
|
||||
String formatted = ConsoleStyle.level(Level.WARN, true);
|
||||
|
||||
assertTrue(formatted.contains("\u001B["));
|
||||
assertTrue(formatted.contains("[!] WARN "));
|
||||
assertTrue(formatted.endsWith("\u001B[0m"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void formatsLevelAsPlainTextWhenNotStyled() {
|
||||
assertEquals("WARN ", ConsoleStyle.level(Level.WARN, false));
|
||||
}
|
||||
|
||||
@Test
|
||||
void formatsLoggerWithColorWhenStyled() {
|
||||
String formatted = ConsoleStyle.logger("com.eu.habbo.networking.Server", true);
|
||||
|
||||
assertTrue(formatted.contains("\u001B["));
|
||||
assertTrue(formatted.contains("Server"));
|
||||
assertTrue(formatted.endsWith("\u001B[0m"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void keepsLoggerPlainAndCompactWhenNotStyled() {
|
||||
assertEquals("Server ", ConsoleStyle.logger("com.eu.habbo.networking.Server", false));
|
||||
}
|
||||
|
||||
@Test
|
||||
void honorsPlainOverrideEvenInWindowsTerminal() {
|
||||
assertFalse(ConsoleStyle.isEnabled(
|
||||
Map.of("WT_SESSION", "abc123"),
|
||||
true,
|
||||
"Windows 11",
|
||||
"plain"));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user