From 9e7f043c4ed5096483da2e2bd18c3dbe0cac08c5 Mon Sep 17 00:00:00 2001 From: Vankka Date: Sat, 16 Sep 2023 16:02:47 +0300 Subject: [PATCH] More improvements to console appending --- .../message/SendableDiscordMessage.java | 19 +++ .../impl/SendableDiscordMessageImpl.java | 22 ++- .../discordsrv/bukkit/BukkitDiscordSRV.java | 1 - .../common/config/main/ConsoleConfig.java | 6 + .../common/console/ConsoleModule.java | 2 +- .../common/console/SingleConsoleHandler.java | 136 +++++++++++------- .../console/message/ConsoleMessage.java | 5 + .../AbstractDiscordGuildMessageChannel.java | 6 +- .../util/SendableDiscordMessageUtil.java | 1 + 9 files changed, 139 insertions(+), 59 deletions(-) diff --git a/api/src/main/java/com/discordsrv/api/discord/entity/message/SendableDiscordMessage.java b/api/src/main/java/com/discordsrv/api/discord/entity/message/SendableDiscordMessage.java index 6fcacec9..44b1a692 100644 --- a/api/src/main/java/com/discordsrv/api/discord/entity/message/SendableDiscordMessage.java +++ b/api/src/main/java/com/discordsrv/api/discord/entity/message/SendableDiscordMessage.java @@ -115,6 +115,12 @@ public interface SendableDiscordMessage { */ boolean isSuppressedNotifications(); + /** + * If embeds for this message are suppressed. + * @return if embeds for this message are suppressed + */ + boolean isSuppressedEmbeds(); + Map getAttachments(); @SuppressWarnings("UnusedReturnValue") // API @@ -275,6 +281,19 @@ public interface SendableDiscordMessage { */ boolean isSuppressedNotifications(); + /** + * Sets if this message's embeds will be suppressed. + * @param suppressedEmbeds if embeds should be suppressed + * @return this builder, useful for chaining + */ + Builder setSuppressedEmbeds(boolean suppressedEmbeds); + + /** + * Checks if this builder has embeds suppressed. + * @return {@code true} if embeds should be suppressed for this message + */ + boolean isSuppressedEmbeds(); + /** * Builds a {@link SendableDiscordMessage} from this builder. * @return the new {@link SendableDiscordMessage} diff --git a/api/src/main/java/com/discordsrv/api/discord/entity/message/impl/SendableDiscordMessageImpl.java b/api/src/main/java/com/discordsrv/api/discord/entity/message/impl/SendableDiscordMessageImpl.java index 2be7b968..451fd604 100644 --- a/api/src/main/java/com/discordsrv/api/discord/entity/message/impl/SendableDiscordMessageImpl.java +++ b/api/src/main/java/com/discordsrv/api/discord/entity/message/impl/SendableDiscordMessageImpl.java @@ -52,6 +52,7 @@ public class SendableDiscordMessageImpl implements SendableDiscordMessage { private final String webhookUsername; private final String webhookAvatarUrl; private final boolean suppressedNotifications; + private final boolean suppressedEmbeds; private final Map attachments; protected SendableDiscordMessageImpl( @@ -62,6 +63,7 @@ public class SendableDiscordMessageImpl implements SendableDiscordMessage { String webhookUsername, String webhookAvatarUrl, boolean suppressedNotifications, + boolean suppressedEmbeds, Map attachments ) { this.content = content; @@ -71,6 +73,7 @@ public class SendableDiscordMessageImpl implements SendableDiscordMessage { this.webhookUsername = webhookUsername; this.webhookAvatarUrl = webhookAvatarUrl; this.suppressedNotifications = suppressedNotifications; + this.suppressedEmbeds = suppressedEmbeds; this.attachments = Collections.unmodifiableMap(attachments); } @@ -110,6 +113,11 @@ public class SendableDiscordMessageImpl implements SendableDiscordMessage { return suppressedNotifications; } + @Override + public boolean isSuppressedEmbeds() { + return suppressedEmbeds; + } + @Override public Map getAttachments() { return attachments; @@ -124,6 +132,7 @@ public class SendableDiscordMessageImpl implements SendableDiscordMessage { private String webhookUsername; private String webhookAvatarUrl; private boolean suppressedNotifications; + private boolean suppressedEmbeds; private final Map attachments = new LinkedHashMap<>(); @Override @@ -241,6 +250,17 @@ public class SendableDiscordMessageImpl implements SendableDiscordMessage { return this; } + @Override + public boolean isSuppressedEmbeds() { + return suppressedEmbeds; + } + + @Override + public Builder setSuppressedEmbeds(boolean suppressedEmbeds) { + this.suppressedEmbeds = suppressedEmbeds; + return this; + } + @Override public boolean isSuppressedNotifications() { return suppressedNotifications; @@ -248,7 +268,7 @@ public class SendableDiscordMessageImpl implements SendableDiscordMessage { @Override public @NotNull SendableDiscordMessage build() { - return new SendableDiscordMessageImpl(content, embeds, actionRows, allowedMentions, webhookUsername, webhookAvatarUrl, suppressedNotifications, attachments); + return new SendableDiscordMessageImpl(content, embeds, actionRows, allowedMentions, webhookUsername, webhookAvatarUrl, suppressedNotifications, suppressedEmbeds, attachments); } @Override diff --git a/bukkit/src/main/java/com/discordsrv/bukkit/BukkitDiscordSRV.java b/bukkit/src/main/java/com/discordsrv/bukkit/BukkitDiscordSRV.java index 9ea7dd5f..192fc968 100644 --- a/bukkit/src/main/java/com/discordsrv/bukkit/BukkitDiscordSRV.java +++ b/bukkit/src/main/java/com/discordsrv/bukkit/BukkitDiscordSRV.java @@ -50,7 +50,6 @@ import com.discordsrv.common.debug.data.OnlineMode; import com.discordsrv.common.messageforwarding.game.minecrafttodiscord.MinecraftToDiscordChatModule; import com.discordsrv.common.plugin.PluginManager; import net.kyori.adventure.platform.bukkit.BukkitAudiences; -import org.bukkit.ChatColor; import org.bukkit.Server; import org.bukkit.plugin.ServicePriority; import org.bukkit.plugin.java.JavaPlugin; 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 86aa6f02..04983095 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 @@ -25,6 +25,12 @@ public class ConsoleConfig { + "- plain_content: Plain text") public OutputMode outputMode = OutputMode.ANSI; + @Comment("In \"diff\" mode, should exception lines have the prefix character as well") + public boolean diffExceptions = true; + + @Comment("If urls should have embeds disabled") + public boolean disableLinkEmbeds = true; + @Comment("Avoids sending new messages by editing the most recent message until it reaches it's maximum length") public boolean useEditing = true; diff --git a/common/src/main/java/com/discordsrv/common/console/ConsoleModule.java b/common/src/main/java/com/discordsrv/common/console/ConsoleModule.java index fbaf9afd..4cd627eb 100644 --- a/common/src/main/java/com/discordsrv/common/console/ConsoleModule.java +++ b/common/src/main/java/com/discordsrv/common/console/ConsoleModule.java @@ -40,7 +40,7 @@ public class ConsoleModule extends AbstractModule implements LogAppe List configs = discordSRV.config().console; for (ConsoleConfig config : configs) { - handlers.add(new SingleConsoleHandler(discordSRV, config)); + handlers.add(new SingleConsoleHandler(discordSRV, logger(), config)); } } diff --git a/common/src/main/java/com/discordsrv/common/console/SingleConsoleHandler.java b/common/src/main/java/com/discordsrv/common/console/SingleConsoleHandler.java index 585de382..1bf318b1 100644 --- a/common/src/main/java/com/discordsrv/common/console/SingleConsoleHandler.java +++ b/common/src/main/java/com/discordsrv/common/console/SingleConsoleHandler.java @@ -9,6 +9,7 @@ import com.discordsrv.common.console.entry.LogEntry; import com.discordsrv.common.console.entry.LogMessage; import com.discordsrv.common.console.message.ConsoleMessage; import com.discordsrv.common.logging.LogLevel; +import com.discordsrv.common.logging.Logger; import net.dv8tion.jda.api.entities.Message; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.exception.ExceptionUtils; @@ -16,7 +17,7 @@ import org.apache.commons.lang3.exception.ExceptionUtils; import java.util.ArrayList; import java.util.List; import java.util.Queue; -import java.util.concurrent.ExecutionException; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.Future; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; @@ -26,9 +27,10 @@ public class SingleConsoleHandler { private static final int MESSAGE_MAX_LENGTH = Message.MAX_CONTENT_LENGTH; private final DiscordSRV discordSRV; + private final Logger logger; private final ConsoleConfig config; - private final Future queueProcessingFuture; private final Queue queue = new LinkedBlockingQueue<>(); + private Future queueProcessingFuture; // Editing private final List messageCache; @@ -36,14 +38,15 @@ public class SingleConsoleHandler { // Preventing concurrent sends private final Object sendLock = new Object(); - private Future sendFuture; + private CompletableFuture sendFuture; - public SingleConsoleHandler(DiscordSRV discordSRV, ConsoleConfig config) { + public SingleConsoleHandler(DiscordSRV discordSRV, Logger logger, ConsoleConfig config) { this.discordSRV = discordSRV; + this.logger = logger; this.config = config; this.messageCache = config.appender.useEditing ? new ArrayList<>() : null; - this.queueProcessingFuture = discordSRV.scheduler().runAtFixedRate(this::processQueue, 2, TimeUnit.SECONDS); + timeQueueProcess(); } public void queue(LogEntry entry) { @@ -57,43 +60,55 @@ public class SingleConsoleHandler { mostRecentMessageId = null; } + private void timeQueueProcess() { + this.queueProcessingFuture = discordSRV.scheduler().runLater(this::processQueue, 2, TimeUnit.SECONDS); + } + private void processQueue() { - if (sendFuture != null && !sendFuture.isDone()) { - // Previous send still in progress. - return; - } + try { + ConsoleConfig.Appender appenderConfig = config.appender; + ConsoleConfig.OutputMode outputMode = appenderConfig.outputMode; - ConsoleConfig.Appender appenderConfig = config.appender; - ConsoleConfig.OutputMode outputMode = appenderConfig.outputMode; + Queue currentBuffer = new LinkedBlockingQueue<>(); + LogEntry entry; + while ((entry = queue.poll()) != null) { + String level = entry.level().name(); + if (appenderConfig.levels.levels.contains(level) == appenderConfig.levels.blacklist) { + // Ignored level + continue; + } - Queue currentBuffer = new LinkedBlockingQueue<>(); - LogEntry entry; - while ((entry = queue.poll()) != null) { - String level = entry.level().name(); - if (appenderConfig.levels.levels.contains(level) == appenderConfig.levels.blacklist) { - // Ignored level - continue; - } + String loggerName = entry.loggerName(); + if (StringUtils.isEmpty(loggerName)) loggerName = "NONE"; + if (appenderConfig.loggers.loggers.contains(loggerName) == appenderConfig.loggers.blacklist) { + // Ignored logger + continue; + } - String loggerName = entry.loggerName(); - if (StringUtils.isEmpty(loggerName)) loggerName = "NONE"; - if (appenderConfig.loggers.loggers.contains(loggerName) == appenderConfig.loggers.blacklist) { - // Ignored logger - continue; - } - - List messages = formatEntry(entry, outputMode); - if (messages.size() == 1) { - LogMessage message = new LogMessage(entry, messages.get(0)); - currentBuffer.add(message); - } else { - clearBuffer(currentBuffer, outputMode); - for (String message : messages) { - send(message, true, outputMode); + List messages = formatEntry(entry, outputMode, config.appender.diffExceptions); + if (messages.size() == 1) { + LogMessage message = new LogMessage(entry, messages.get(0)); + currentBuffer.add(message); + } else { + clearBuffer(currentBuffer, outputMode); + for (String message : messages) { + send(message, true, outputMode); + } } } + clearBuffer(currentBuffer, outputMode); + } catch (Exception ex) { + logger.error("Failed to process console lines", ex); + } + + if (sendFuture != null) { + sendFuture.whenComplete((__, ___) -> { + sendFuture = null; + timeQueueProcess(); + }); + } else { + timeQueueProcess(); } - clearBuffer(currentBuffer, outputMode); } private void clearBuffer(Queue currentBuffer, ConsoleConfig.OutputMode outputMode) { @@ -136,19 +151,17 @@ public class SingleConsoleHandler { SendableDiscordMessage sendableMessage = SendableDiscordMessage.builder() .setContent(outputMode.prefix() + message + outputMode.suffix()) .setSuppressedNotifications(config.appender.silentMessages) + .setSuppressedEmbeds(config.appender.disableLinkEmbeds) .build(); synchronized (sendLock) { - if (sendFuture != null && !sendFuture.isDone()) { - try { - sendFuture.get(); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } catch (ExecutionException ignored) {} - } + CompletableFuture future = sendFuture != null ? sendFuture : CompletableFuture.completedFuture(null); - sendFuture = discordSRV.discordAPI() - .findOrCreateDestinations(config.channel.asDestination(), true, true, true) + sendFuture = future + .thenCompose(__ -> + discordSRV.discordAPI() + .findOrCreateDestinations(config.channel.asDestination(), true, true, true) + ) .thenApply(channels -> { if (channels.isEmpty()) { throw new IllegalStateException("No channel"); @@ -173,20 +186,21 @@ public class SingleConsoleHandler { } } - private List formatEntry(LogEntry entry, ConsoleConfig.OutputMode outputMode) { + private List formatEntry(LogEntry entry, ConsoleConfig.OutputMode outputMode, boolean diffExceptions) { int blockLength = outputMode.blockLength(); int maximumPart = MESSAGE_MAX_LENGTH - blockLength - "\n".length(); String parsedMessage; + ConsoleMessage consoleMessage = new ConsoleMessage(discordSRV, entry.message()); switch (outputMode) { case ANSI: - parsedMessage = new ConsoleMessage(discordSRV, entry.message()).asAnsi(); + parsedMessage = consoleMessage.asAnsi(); break; case MARKDOWN: - parsedMessage = new ConsoleMessage(discordSRV, entry.message()).asMarkdown(); + parsedMessage = consoleMessage.asMarkdown(); break; default: - parsedMessage = entry.message(); + parsedMessage = consoleMessage.asPlain(); break; } @@ -194,14 +208,26 @@ public class SingleConsoleHandler { config.appender.lineFormat, entry, new SinglePlaceholder("message", parsedMessage) - ) + "\n"; + ); + + Throwable thrown = entry.throwable(); + String throwable = thrown != null ? ExceptionUtils.getStackTrace(thrown) : StringUtils.EMPTY; if (outputMode == ConsoleConfig.OutputMode.DIFF) { - message = getLogLevelDiffCharacter(entry.level()) + message; - // TODO: also format throwable? + String diff = getLogLevelDiffCharacter(entry.level()); + if (!message.isEmpty()) { + message = diff + message.replace("\n", "\n" + diff); + } + + String exceptionCharacter = diffExceptions ? diff : ""; + if (!throwable.isEmpty()) { + throwable = exceptionCharacter + throwable.replace("\n", "\n" + exceptionCharacter); + } } - String throwable = ExceptionUtils.getMessage(entry.throwable()); + if (!message.isEmpty()) { + message += "\n"; + } if (!throwable.isEmpty()) { throwable += "\n"; } @@ -257,10 +283,10 @@ public class SingleConsoleHandler { private String getLogLevelDiffCharacter(LogLevel level) { if (level == LogLevel.StandardLogLevel.WARNING) { - return "+"; + return "+ "; } else if (level == LogLevel.StandardLogLevel.ERROR) { - return "-"; + return "- "; } - return " "; + return " "; } } diff --git a/common/src/main/java/com/discordsrv/common/console/message/ConsoleMessage.java b/common/src/main/java/com/discordsrv/common/console/message/ConsoleMessage.java index b2f59d9e..07de873b 100644 --- a/common/src/main/java/com/discordsrv/common/console/message/ConsoleMessage.java +++ b/common/src/main/java/com/discordsrv/common/console/message/ConsoleMessage.java @@ -58,6 +58,11 @@ public class ConsoleMessage { return discordSRV.componentFactory().ansiSerializer().serialize(component); } + public String asPlain() { + Component component = builder.build(); + return discordSRV.componentFactory().plainSerializer().serialize(component); + } + private void parse(String input) { Matcher matcher = PATTERN.matcher(input); diff --git a/common/src/main/java/com/discordsrv/common/discord/api/entity/channel/AbstractDiscordGuildMessageChannel.java b/common/src/main/java/com/discordsrv/common/discord/api/entity/channel/AbstractDiscordGuildMessageChannel.java index bb8fc49a..34e94ac7 100644 --- a/common/src/main/java/com/discordsrv/common/discord/api/entity/channel/AbstractDiscordGuildMessageChannel.java +++ b/common/src/main/java/com/discordsrv/common/discord/api/entity/channel/AbstractDiscordGuildMessageChannel.java @@ -89,7 +89,11 @@ public abstract class AbstractDiscordGuildMessageChannel l).toArray()) .mentionRoles(allowedRoles.stream().mapToLong(l -> l).toArray()) .setFiles(uploads);