From 3c691ffe8614b8e7fea476778068b71b40b30115 Mon Sep 17 00:00:00 2001 From: JFronny Date: Fri, 15 Mar 2024 11:43:44 +0100 Subject: [PATCH] fix(base): Separate bootstrap stage with fallback JPL->JUL->our JPL logger redirection --- .../java/io/gitlab/jfronny/libjf/LibJf.java | 16 +++ .../gitlab/jfronny/libjf/log/JULBridge.java | 98 +++++++++++++++++++ .../config/impl/entrypoint/JfConfigSafe.java | 8 +- 3 files changed, 121 insertions(+), 1 deletion(-) create mode 100644 libjf-base/src/main/java/io/gitlab/jfronny/libjf/log/JULBridge.java diff --git a/libjf-base/src/main/java/io/gitlab/jfronny/libjf/LibJf.java b/libjf-base/src/main/java/io/gitlab/jfronny/libjf/LibJf.java index 6441da4..b2f7228 100644 --- a/libjf-base/src/main/java/io/gitlab/jfronny/libjf/LibJf.java +++ b/libjf-base/src/main/java/io/gitlab/jfronny/libjf/LibJf.java @@ -1,9 +1,11 @@ package io.gitlab.jfronny.libjf; +import io.gitlab.jfronny.commons.logger.DelegateLogger; import io.gitlab.jfronny.commons.logger.HotswapLoggerFinder; import io.gitlab.jfronny.commons.logger.SystemLoggerPlus; import io.gitlab.jfronny.commons.serialize.gson.api.v2.GsonHolders; import io.gitlab.jfronny.libjf.gson.HiddenAnnotationExclusionStrategy; +import io.gitlab.jfronny.libjf.log.JULBridge; import io.gitlab.jfronny.libjf.log.SLF4JPlatformLogger; import net.fabricmc.loader.api.entrypoint.PreLaunchEntrypoint; import org.slf4j.LoggerFactory; @@ -23,10 +25,24 @@ public class LibJf implements PreLaunchEntrypoint { } private static boolean setup = false; + private static boolean bootstrapped = false; public static void setup() { + bootstrap(); if (!setup) { setup = true; HotswapLoggerFinder.updateAllStrategies((name, module, level) -> new SLF4JPlatformLogger(LoggerFactory.getLogger(name))); } } + + public static void bootstrap() { + if (!bootstrapped) { + bootstrapped = true; + if (LOGGER instanceof DelegateLogger) { + LOGGER.debug("HotswapLoggerFinder was loaded successfully. No need to install JULBridge."); + } else { + JULBridge.install(); + LOGGER.debug("JULBridge was installed. If available in your launcher, please enable the ServiceLoader fix for better performance."); + } + } + } } diff --git a/libjf-base/src/main/java/io/gitlab/jfronny/libjf/log/JULBridge.java b/libjf-base/src/main/java/io/gitlab/jfronny/libjf/log/JULBridge.java new file mode 100644 index 0000000..6afeb0c --- /dev/null +++ b/libjf-base/src/main/java/io/gitlab/jfronny/libjf/log/JULBridge.java @@ -0,0 +1,98 @@ +package io.gitlab.jfronny.libjf.log; + +import io.gitlab.jfronny.commons.StringFormatter; + +import java.text.MessageFormat; +import java.util.Objects; +import java.util.ResourceBundle; +import java.util.logging.Handler; +import java.util.logging.Level; +import java.util.logging.LogManager; +import java.util.logging.LogRecord; + +/** + * Based on SLF4JBridgeHandler, modified for LibJF. + */ +public class JULBridge extends Handler { + public static void install() { + var rootLogger = getRootLogger(); + rootLogger.addHandler(new JULBridge()); + boolean bridgeFound = false; + for (Handler handler : rootLogger.getHandlers()) { + if (handler instanceof JULBridge) { + bridgeFound = true; + continue; + } + rootLogger.removeHandler(handler); + } + if (!bridgeFound) { + // Someone is wrapping our handlers. No matter, just add it again. + rootLogger.addHandler(new JULBridge()); + } + } + + private static java.util.logging.Logger getRootLogger() { + return LogManager.getLogManager().getLogger(""); + } + + private JULBridge() { + } + + @Override + public void flush() { + // Do nothing + } + + @Override + public void close() { + // Do nothing + } + + private final Module module = JULBridge.class.getClassLoader().getUnnamedModule(); + private final EarlyLoggerSetup loggerFinder = new EarlyLoggerSetup(); + + private System.Logger getLoggerFor(LogRecord record) { + return loggerFinder.getLogger(Objects.requireNonNullElse(record.getLoggerName(), ""), module); + } + + private static System.Logger.Level getLevel(LogRecord record) { + System.Logger.Level level; + if (record.getLevel().equals(Level.OFF)) level = System.Logger.Level.OFF; + else if (record.getLevel().equals(Level.ALL)) level = System.Logger.Level.ALL; + else if (record.getLevel().intValue() >= Level.SEVERE.intValue()) { + level = System.Logger.Level.ERROR; + } else if (record.getLevel().intValue() >= Level.WARNING.intValue()) { + level = System.Logger.Level.WARNING; + } else if (record.getLevel().intValue() >= Level.INFO.intValue()) { + level = System.Logger.Level.INFO; + } else { + level = System.Logger.Level.DEBUG; + } + return level; + } + + @Override + public void publish(LogRecord record) { + if (record == null) return; + System.Logger logger = getLoggerFor(record); + System.Logger.Level level = getLevel(record); + String message = record.getMessage(); + ResourceBundle bundle = null; + if (message == null) message = ""; + else bundle = record.getResourceBundle(); + Object[] params = record.getParameters(); + Throwable thrown = record.getThrown(); + if (thrown == null) { + logger.log(level, bundle, message, params); + } else { + if (params != null && params.length > 0) { + String[] strings = new String[params.length]; + for (int i = 0; i < params.length; i++) { + strings[i] = StringFormatter.toString(params[i]); + } + message = new MessageFormat(message).format(strings); + } + logger.log(level, bundle, message, thrown); + } + } +} diff --git a/libjf-config-core-v2/src/main/java/io/gitlab/jfronny/libjf/config/impl/entrypoint/JfConfigSafe.java b/libjf-config-core-v2/src/main/java/io/gitlab/jfronny/libjf/config/impl/entrypoint/JfConfigSafe.java index 948dffe..6fcd3bb 100644 --- a/libjf-config-core-v2/src/main/java/io/gitlab/jfronny/libjf/config/impl/entrypoint/JfConfigSafe.java +++ b/libjf-config-core-v2/src/main/java/io/gitlab/jfronny/libjf/config/impl/entrypoint/JfConfigSafe.java @@ -26,7 +26,13 @@ public class JfConfigSafe implements PreLaunchEntrypoint { } } TRANSLATION_SUPPLIER = s -> { - String translated = Language.getInstance().get(s); + String translated; + try { + translated = Language.getInstance().get(s); + } catch (Throwable t) { + LibJf.LOGGER.debug("Failed to translate key " + s, t); + return null; + } return translated.equals(s) ? null : translated; }; }