More improvements to console appending

This commit is contained in:
Vankka 2023-09-16 16:02:47 +03:00
parent a2f0896260
commit 9e7f043c4e
No known key found for this signature in database
GPG Key ID: 6E50CB7A29B96AD0
9 changed files with 139 additions and 59 deletions

View File

@ -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<InputStream, String> 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}

View File

@ -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<InputStream, String> attachments;
protected SendableDiscordMessageImpl(
@ -62,6 +63,7 @@ public class SendableDiscordMessageImpl implements SendableDiscordMessage {
String webhookUsername,
String webhookAvatarUrl,
boolean suppressedNotifications,
boolean suppressedEmbeds,
Map<InputStream, String> 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<InputStream, String> 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<InputStream, String> 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

View File

@ -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;

View File

@ -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;

View File

@ -40,7 +40,7 @@ public class ConsoleModule extends AbstractModule<DiscordSRV> implements LogAppe
List<ConsoleConfig> configs = discordSRV.config().console;
for (ConsoleConfig config : configs) {
handlers.add(new SingleConsoleHandler(discordSRV, config));
handlers.add(new SingleConsoleHandler(discordSRV, logger(), config));
}
}

View File

@ -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<LogEntry> queue = new LinkedBlockingQueue<>();
private Future<?> queueProcessingFuture;
// Editing
private final List<LogMessage> 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<LogMessage> 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<LogMessage> 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<String> 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<String> 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<LogMessage> 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<String> formatEntry(LogEntry entry, ConsoleConfig.OutputMode outputMode) {
private List<String> 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 " ";
}
}

View File

@ -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);

View File

@ -89,7 +89,11 @@ public abstract class AbstractDiscordGuildMessageChannel<T extends GuildMessageC
.setAvatarUrl(message.getWebhookAvatarUrl())
);
} else {
createRequest = CompletableFuture.completedFuture(((R) channel.sendMessage(createData)));
createRequest = CompletableFuture.completedFuture(
((R) channel.sendMessage(createData)
// JDA doesn't properly grab this from MessageCreateData
.setSuppressEmbeds(createData.isSuppressEmbeds()))
);
}
return createRequest

View File

@ -73,6 +73,7 @@ public final class SendableDiscordMessageUtil {
.setContent(message.getContent())
.setEmbeds(embeds)
.setAllowedMentions(allowedTypes)
.setSuppressEmbeds(message.isSuppressedEmbeds())
.mentionUsers(allowedUsers.stream().mapToLong(l -> l).toArray())
.mentionRoles(allowedRoles.stream().mapToLong(l -> l).toArray())
.setFiles(uploads);