diff --git a/common/src/main/java/com/discordsrv/common/config/main/ConsoleConfig.java b/common/src/main/java/com/discordsrv/common/config/main/ConsoleConfig.java index dfbe204b..06f0c4c8 100644 --- a/common/src/main/java/com/discordsrv/common/config/main/ConsoleConfig.java +++ b/common/src/main/java/com/discordsrv/common/config/main/ConsoleConfig.java @@ -85,6 +85,19 @@ public class ConsoleConfig { public boolean blacklist = true; } + @Comment("Options for how to deal with exception stack traces in the console") + public Exceptions exceptions = new Exceptions(); + + public static class Exceptions { + + @Comment("The time after which the exact same exception will not be forwarded to the console channel, in minutes\n" + + "0 to not filter out duplicate exceptions, -1 to always filter out duplicate exceptions") + public int filterOutDuplicatesMinutes = 5; + + @Comment("If the message alongside a exception should also be ignored when ignoring duplicate exceptions") + public boolean alsoBlockMessages = true; + } + } @ConfigSerializable diff --git a/common/src/main/java/com/discordsrv/common/feature/console/SingleConsoleHandler.java b/common/src/main/java/com/discordsrv/common/feature/console/SingleConsoleHandler.java index a7057998..5a3e4fe3 100644 --- a/common/src/main/java/com/discordsrv/common/feature/console/SingleConsoleHandler.java +++ b/common/src/main/java/com/discordsrv/common/feature/console/SingleConsoleHandler.java @@ -41,6 +41,8 @@ import com.discordsrv.common.feature.console.entry.LogMessage; import com.discordsrv.common.feature.console.message.ConsoleMessage; import com.discordsrv.common.helper.TemporaryLocalData; import com.discordsrv.common.logging.LogLevel; +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; import net.dv8tion.jda.api.entities.Message; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.exception.ExceptionUtils; @@ -64,9 +66,6 @@ public class SingleConsoleHandler { private final Logger logger; private ConsoleConfig config; private String key; - private Queue messageQueue; - private Deque> sendQueue; - private Future queueProcessingFuture; private boolean shutdown = false; private final AtomicLong latestChannelId = new AtomicLong(0); @@ -75,8 +74,12 @@ public class SingleConsoleHandler { private final AtomicLong mostRecentMessageId = new AtomicLong(0); // Sending - private boolean sentFirstBatch = false; + private Future queueProcessingFuture; private CompletableFuture sendFuture; + private Queue messageQueue; + private Deque> sendQueue; + private boolean sentFirstBatch = false; + private Cache exceptions; // Don't annoy console users twice about using / private final Set warnedSlashUsageUserIds = new HashSet<>(); @@ -221,6 +224,17 @@ public class SingleConsoleHandler { mostRecentMessageId.set(0); } + int exceptionMinutes = config.appender.exceptions.filterOutDuplicatesMinutes; + if (exceptionMinutes != 0) { + Caffeine builder = discordSRV.caffeineBuilder(); + if (exceptionMinutes > 0) { + builder = builder.expireAfterWrite(Duration.ofMinutes(exceptionMinutes)); + } + this.exceptions = builder.build(); + } else { + this.exceptions = null; + } + timeQueueProcess(); } @@ -289,7 +303,7 @@ public class SingleConsoleHandler { sendQueue.pollLast(); } - logger.warning("Skipping " + remove + " log messages because the send queue is backed up"); + logger.warning("Skipping " + remove + " console messages because the send queue is backed up"); } if (!shutdown && !discordSRV.isReady()) { @@ -331,7 +345,21 @@ public class SingleConsoleHandler { continue; } - List messages = formatEntry(entry, outputMode, config.appender.diffExceptions); + String throwable = ExceptionUtils.getStackTrace(entry.throwable()); + if (!throwable.isEmpty() && this.exceptions != null) { + int hashCode = throwable.hashCode(); + if (this.exceptions.getIfPresent(hashCode) == null) { + this.exceptions.put(hashCode, true); + } else { + if (config.appender.exceptions.alsoBlockMessages) { + continue; + } + + throwable = ""; + } + } + + List messages = formatEntry(entry, throwable, outputMode, config.appender.diffExceptions); if (messages.size() == 1) { LogMessage message = new LogMessage(entry, messages.get(0)); currentBuffer.add(message); @@ -391,7 +419,7 @@ public class SingleConsoleHandler { sendQueue.offer(Pair.of(sendableMessage, lastEdit)); } - private List formatEntry(LogEntry entry, ConsoleConfig.OutputMode outputMode, boolean diffExceptions) { + private List formatEntry(LogEntry entry, String throwable, ConsoleConfig.OutputMode outputMode, boolean diffExceptions) { int blockLength = outputMode.blockLength(); int maximumPart = MESSAGE_MAX_LENGTH - blockLength - "\n".length(); @@ -431,9 +459,6 @@ public class SingleConsoleHandler { ) ); - Throwable thrown = entry.throwable(); - String throwable = thrown != null ? ExceptionUtils.getStackTrace(thrown) : StringUtils.EMPTY; - if (outputMode == ConsoleConfig.OutputMode.DIFF) { String diff = getLogLevelDiffCharacter(entry.level()); if (!message.isEmpty()) { @@ -463,7 +488,7 @@ public class SingleConsoleHandler { // Handle log entry being longer than a message int totalLength = blockLength + throwable.length() + message.length(); if (totalLength > MESSAGE_MAX_LENGTH) { - String remainingPart = chopOnNewlines(message, blockLength, maximumPart, formatted); + String remainingPart = chopOnNewlines(message + throwable, blockLength, maximumPart, formatted); formatted.add(remainingPart); } else { formatted.add(message + throwable);