Skip to content

Commit

Permalink
Update SLF4J api and jackson (#2674)
Browse files Browse the repository at this point in the history
* Replace SimpleLogger with FallbackLogger
* Add system property to disable fallback logger
  • Loading branch information
MinnDevelopment committed May 12, 2024
1 parent 92bcc32 commit 087f68f
Show file tree
Hide file tree
Showing 6 changed files with 185 additions and 481 deletions.
4 changes: 2 additions & 2 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ rootProject.name = "JDA"
dependencyResolutionManagement {
versionCatalogs {
create("libs") {
version("jackson", "2.16.0")
version("jackson", "2.17.0")
library("jackson-core", "com.fasterxml.jackson.core", "jackson-core").versionRef("jackson")
library("jackson-databind", "com.fasterxml.jackson.core", "jackson-databind").versionRef("jackson")
bundle("jackson", listOf("jackson-core", "jackson-databind"))
Expand All @@ -22,7 +22,7 @@ dependencyResolutionManagement {
library("junit", "org.junit.jupiter", "junit-jupiter" ).version("5.10.2")
library("mockito", "org.mockito", "mockito-core" ).version("5.11.0")
library("reflections", "org.reflections", "reflections" ).version("0.10.2")
library("slf4j", "org.slf4j", "slf4j-api" ).version("1.7.36")
library("slf4j", "org.slf4j", "slf4j-api" ).version("2.0.13")
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
import net.dv8tion.jda.api.utils.data.DataObject;
import net.dv8tion.jda.internal.JDAImpl;
import net.dv8tion.jda.internal.entities.GuildImpl;
import net.dv8tion.jda.internal.utils.JDALogger;

public class GuildBanHandler extends SocketHandler
{
Expand All @@ -45,7 +44,7 @@ protected Long handleInternally(DataObject content)
if (guild == null)
{
getJDA().getEventCache().cache(EventCache.Type.GUILD, id, responseNumber, allContent, this::handle);
EventCache.LOG.debug("Received Guild Member {} event for a Guild not yet cached.", JDALogger.getLazyString(() -> banned ? "Ban" : "Unban"));
EventCache.LOG.debug("Received Guild Member {} event for a Guild not yet cached.", banned ? "Ban" : "Unban");
return null;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@
import net.dv8tion.jda.internal.entities.MemberImpl;
import net.dv8tion.jda.internal.entities.channel.concrete.PrivateChannelImpl;
import net.dv8tion.jda.internal.requests.WebSocketClient;
import net.dv8tion.jda.internal.utils.JDALogger;

import java.util.List;
import java.util.Objects;
Expand Down Expand Up @@ -73,8 +72,7 @@ protected Long handleInternally(DataObject content)

if (emojiId == null && emojiName == null)
{
WebSocketClient.LOG.debug("Received a reaction {} with no name nor id. json: {}",
JDALogger.getLazyString(() -> add ? "add" : "remove"), content);
WebSocketClient.LOG.debug("Received a reaction {} with no name nor id. json: {}", add ? "add" : "remove", content);
return null;
}
final long guildId = content.getUnsignedLong("guild_id", 0);
Expand Down
81 changes: 81 additions & 0 deletions src/main/java/net/dv8tion/jda/internal/utils/FallbackLogger.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*
* Copyright 2015 Austin Keener, Michael Ritter, Florian Spieß, and the JDA contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package net.dv8tion.jda.internal.utils;

import org.slf4j.Marker;
import org.slf4j.event.Level;
import org.slf4j.helpers.FormattingTuple;
import org.slf4j.helpers.LegacyAbstractLogger;
import org.slf4j.helpers.MessageFormatter;

import java.time.LocalDateTime;

public class FallbackLogger extends LegacyAbstractLogger
{
private final String name;

public FallbackLogger(String name)
{
this.name = name;
}

@Override
protected String getFullyQualifiedCallerName()
{
return null;
}

@Override
protected void handleNormalizedLoggingCall(Level level, Marker marker, String messagePattern, Object[] arguments, Throwable throwable)
{
LocalDateTime now = LocalDateTime.now();
FormattingTuple result = MessageFormatter.arrayFormat(messagePattern, arguments);
System.err.printf("%1$tF %1$tT [%2$s] [%3$s] %4$s%n", now, name, level, result.getMessage());
if (throwable != null)
throwable.printStackTrace(System.err);
}

@Override
public boolean isTraceEnabled()
{
return false;
}

@Override
public boolean isDebugEnabled()
{
return false;
}

@Override
public boolean isInfoEnabled()
{
return true;
}

@Override
public boolean isWarnEnabled()
{
return true;
}

@Override
public boolean isErrorEnabled()
{
return true;
}
}
136 changes: 100 additions & 36 deletions src/main/java/net/dv8tion/jda/internal/utils/JDALogger.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,74 +16,104 @@

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;

/**
* This class serves as a LoggerFactory for JDA's internals.
* <br>It will either return a Logger from a SLF4J implementation via {@link org.slf4j.LoggerFactory} if present,
* or an instance of a custom {@link SimpleLogger} (From slf4j-simple).
* or an instance of a custom {@link FallbackLogger}.
* <p>
* It also has the utility method {@link #getLazyString(LazyEvaluation)} which is used to lazily construct Strings for Logging.
*
* @see #setFallbackLoggerEnabled(boolean)
*/
public class JDALogger
{
/**
* Marks whether or not a SLF4J <code>StaticLoggerBinder</code> (pre 1.8.x) or
* <code>SLF4JServiceProvider</code> implementation (1.8.x+) was found. If false, JDA will use its fallback logger.
* <br>This variable is initialized during static class initialization.
* The name of the system property, which controls whether the fallback logger is disabled.
*
* <p>{@value}
*/
public static final String DISABLE_FALLBACK_PROPERTY_NAME = "net.dv8tion.jda.disableFallbackLogger";

/**
* Whether an implementation of {@link SLF4JServiceProvider} was found.
* <br>If false, JDA will use its fallback logger.
*
* <p>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;
private static boolean disableFallback = Boolean.getBoolean(DISABLE_FALLBACK_PROPERTY_NAME);
private static final MethodHandle fallbackLoggerConstructor;

static
{
boolean tmp = false;

boolean hasLoggerImpl = false;
try
{
Class.forName("org.slf4j.impl.StaticLoggerBinder");

tmp = true;
Class<?> provider = Class.forName("org.slf4j.spi.SLF4JServiceProvider");
hasLoggerImpl = ServiceLoader.load(provider).iterator().hasNext();
}
catch (ClassNotFoundException eStatic)
catch (ClassNotFoundException ignored)
{
// there was no static logger binder (SLF4J pre-1.8.x)

try
{
Class<?> serviceProviderInterface = Class.forName("org.slf4j.spi.SLF4JServiceProvider");
disableFallback = true; // only works with SLF4J 2.0+
}

// check if there is a service implementation for the service, indicating a provider for SLF4J 1.8.x+ is installed
tmp = ServiceLoader.load(serviceProviderInterface).iterator().hasNext();
}
catch (ClassNotFoundException eService)
{
// there was no service provider interface (SLF4J 1.8.x+)
SLF4J_ENABLED = hasLoggerImpl;

//prints warning of missing implementation
LoggerFactory.getLogger(JDALogger.class);
// Dynamically load fallback logger to avoid static initializer errors

tmp = false;
}
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));
}

SLF4J_ENABLED = tmp;
catch (
ClassNotFoundException |
ExceptionInInitializerError |
IllegalAccessException |
NoClassDefFoundError |
NoSuchMethodException ignored
) {}

fallbackLoggerConstructor = constructor;
}

private static final Map<String, Logger> LOGS = new CaseInsensitiveMap<>();
private static final Map<String, Logger> LOGS = new HashMap<>();

private JDALogger() {}

/**
* Disables the automatic fallback logger that JDA uses when no SLF4J implementation is found.
*
* @param enabled
* False, to disable the fallback logger
*/
public static void setFallbackLoggerEnabled(boolean enabled)
{
disableFallback = !enabled;
}

/**
* Will get the {@link org.slf4j.Logger} with the given log-name
* or create and cache a fallback logger if there is no SLF4J implementation present.
* <p>
* The fallback logger will be an instance of a slightly modified version of SLF4Js SimpleLogger.
* The fallback logger uses a constant logging configuration and prints directly to {@link System#err}.
*
* @param name
* The name of the Logger
Expand All @@ -94,17 +124,17 @@ public static Logger getLog(String name)
{
synchronized (LOGS)
{
if (SLF4J_ENABLED)
if (SLF4J_ENABLED || disableFallback)
return LoggerFactory.getLogger(name);
return LOGS.computeIfAbsent(name, SimpleLogger::new);
return newFallbackLogger(name);
}
}

/**
* Will get the {@link org.slf4j.Logger} for the given Class
* or create and cache a fallback logger if there is no SLF4J implementation present.
* <p>
* The fallback logger will be an instance of a slightly modified version of SLF4Js SimpleLogger.
* The fallback logger uses a constant logging configuration and prints directly to {@link System#err}.
*
* @param clazz
* The class used for the Logger name
Expand All @@ -115,9 +145,43 @@ public static Logger getLog(Class<?> clazz)
{
synchronized (LOGS)
{
if (SLF4J_ENABLED)
if (SLF4J_ENABLED || disableFallback)
return LoggerFactory.getLogger(clazz);
return LOGS.computeIfAbsent(clazz.getName(), (n) -> new SimpleLogger(clazz.getSimpleName()));
return newFallbackLogger(clazz.getSimpleName());
}
}

private static void printFallbackWarning()
{
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)
{
throw new IllegalStateException("Failed to initialize fallback logger", e);
}
}

Expand All @@ -144,7 +208,7 @@ public String toString()
{
StringWriter sw = new StringWriter();
ex.printStackTrace(new PrintWriter(sw));
return "Error while evaluating lazy String... " + sw.toString();
return "Error while evaluating lazy String... " + sw;
}
}
};
Expand Down

0 comments on commit 087f68f

Please sign in to comment.