From a08b027e494cfbf94f5877422f007cbd609943c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Spie=C3=9F?= Date: Sat, 4 May 2024 21:02:45 +0200 Subject: [PATCH] Allow using jda with older slf4j-api --- .../jda/internal/utils/FallbackLogger.java | 2 +- .../dv8tion/jda/internal/utils/JDALogger.java | 88 ++++++++++++++++--- 2 files changed, 75 insertions(+), 15 deletions(-) diff --git a/src/main/java/net/dv8tion/jda/internal/utils/FallbackLogger.java b/src/main/java/net/dv8tion/jda/internal/utils/FallbackLogger.java index 1726930539..78eda0e16f 100644 --- a/src/main/java/net/dv8tion/jda/internal/utils/FallbackLogger.java +++ b/src/main/java/net/dv8tion/jda/internal/utils/FallbackLogger.java @@ -24,7 +24,7 @@ import java.time.LocalDateTime; -class FallbackLogger extends LegacyAbstractLogger +public class FallbackLogger extends LegacyAbstractLogger { private final String name; diff --git a/src/main/java/net/dv8tion/jda/internal/utils/JDALogger.java b/src/main/java/net/dv8tion/jda/internal/utils/JDALogger.java index 86052cb1a4..3ea31feb2f 100644 --- a/src/main/java/net/dv8tion/jda/internal/utils/JDALogger.java +++ b/src/main/java/net/dv8tion/jda/internal/utils/JDALogger.java @@ -16,13 +16,17 @@ package net.dv8tion.jda.internal.utils; -import org.apache.commons.collections4.map.CaseInsensitiveMap; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.slf4j.helpers.NOPLogger; import org.slf4j.spi.SLF4JServiceProvider; import java.io.PrintWriter; import java.io.StringWriter; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.util.HashMap; import java.util.Map; import java.util.ServiceLoader; @@ -45,16 +49,52 @@ public class JDALogger public static final String DISABLE_FALLBACK_PROPERTY_NAME = "net.dv8tion.jda.disableFallbackLogger"; /** - * Whether an implementation of {@link SLF4JServiceProvider}was found. + * Whether an implementation of {@link SLF4JServiceProvider} was found. *
If false, JDA will use its fallback logger. * *

The fallback logger can be disabled with {@link #setFallbackLoggerEnabled(boolean)} * or using the system property {@value #DISABLE_FALLBACK_PROPERTY_NAME}. */ - public static final boolean SLF4J_ENABLED = ServiceLoader.load(SLF4JServiceProvider.class).iterator().hasNext(); + public static final boolean SLF4J_ENABLED; private static boolean disableFallback = Boolean.getBoolean(DISABLE_FALLBACK_PROPERTY_NAME); + private static final MethodHandle fallbackLoggerConstructor; - private static final Map LOGS = new CaseInsensitiveMap<>(); + static + { + boolean hasLoggerImpl = false; + try + { + Class provider = Class.forName("org.slf4j.spi.SLF4JServiceProvider"); + hasLoggerImpl = ServiceLoader.load(provider).iterator().hasNext(); + } + catch (ClassNotFoundException ignored) + { + disableFallback = true; // only works with SLF4J 2.0+ + } + + SLF4J_ENABLED = hasLoggerImpl; + + // Dynamically load fallback logger to avoid static initializer errors + + MethodHandle constructor = null; + try + { + MethodHandles.Lookup lookup = MethodHandles.publicLookup(); + Class fallbackLoggerClass = Class.forName("net.dv8tion.jda.internal.utils.FallbackLogger"); + constructor = lookup.findConstructor(fallbackLoggerClass, MethodType.methodType(void.class, String.class)); + } + catch ( + ClassNotFoundException | + ExceptionInInitializerError | + IllegalAccessException | + NoClassDefFoundError | + NoSuchMethodException ignored + ) {} + + fallbackLoggerConstructor = constructor; + } + + private static final Map LOGS = new HashMap<>(); private JDALogger() {} @@ -86,8 +126,7 @@ public static Logger getLog(String name) { if (SLF4J_ENABLED || disableFallback) return LoggerFactory.getLogger(name); - printFallbackWarning(); - return LOGS.computeIfAbsent(name, FallbackLogger::new); + return newFallbackLogger(name); } } @@ -108,20 +147,41 @@ public static Logger getLog(Class clazz) { if (SLF4J_ENABLED || disableFallback) return LoggerFactory.getLogger(clazz); - printFallbackWarning(); - return LOGS.computeIfAbsent(clazz.getName(), (n) -> new FallbackLogger(clazz.getSimpleName())); + return newFallbackLogger(clazz.getSimpleName()); } } private static void printFallbackWarning() { - if (LOGS.isEmpty()) + Logger logger = newFallbackLogger(JDALogger.class.getSimpleName()); + logger.warn("Using fallback logger due to missing SLF4J implementation."); + logger.warn("Please setup a logging framework to use JDA."); + logger.warn("You can use our logging setup guide https://jda.wiki/setup/logging/"); + logger.warn("To disable the fallback logger, add the slf4j-nop dependency or use JDALogger.setFallbackLoggerEnabled(false)"); + } + + private static Logger newFallbackLogger(String name) + { + if (disableFallback || fallbackLoggerConstructor == null) + return NOPLogger.NOP_LOGGER; + + try + { + synchronized (LOGS) + { + if (LOGS.containsKey(name)) + return LOGS.get(name); + Logger logger = (Logger) fallbackLoggerConstructor.invoke(name); + boolean isFirstFallback = LOGS.isEmpty(); + LOGS.put(name, logger); + if (isFirstFallback) + printFallbackWarning(); + return logger; + } + } + catch (Throwable e) { - Logger logger = LOGS.computeIfAbsent(JDALogger.class.getSimpleName(), (n) -> new FallbackLogger(JDALogger.class.getSimpleName())); - logger.warn("Using fallback logger due to missing SLF4J implementation."); - logger.warn("Please setup a logging framework to use JDA."); - logger.warn("You can use our logging setup guide https://jda.wiki/setup/logging/"); - logger.warn("To disable the fallback logger, add the slf4j-nop dependency or use JDALogger.setFallbackLoggerEnabled(false)"); + throw new IllegalStateException("Failed to initialize fallback logger", e); } }