Begin work on console appending

This commit is contained in:
Vankka 2023-08-28 00:36:45 +03:00
parent 80017829e5
commit 7148944537
No known key found for this signature in database
GPG Key ID: 6E50CB7A29B96AD0
6 changed files with 325 additions and 2 deletions

View File

@ -0,0 +1,60 @@
package com.discordsrv.common.config.main;
import org.spongepowered.configurate.objectmapping.meta.Comment;
import java.util.Locale;
public class ConsoleConfig {
@Comment("The format for log lines")
public String lineFormat = "[%log_time:'ccc HH:mm:ss zzz'%] [%log_level%] [%logger_name%] %message%";
@Comment("The mode for the console output, available options are:\n"
+ "- ansi: A colored ansi code block\n"
+ "- log: A \"accesslog\" code block\n"
+ "- diff: A \"diff\" code block highlighting warnings and errors with different colors\n"
+ "- plain: Plain text code block\n"
+ "- plain_content: Plain text")
public String outputMode = "ansi";
public OutputMode getOutputMode() {
switch (outputMode.toLowerCase(Locale.ROOT)) {
default:
case "ansi": return OutputMode.ANSI;
case "log": return OutputMode.LOG;
case "diff": return OutputMode.DIFF;
case "plain": return OutputMode.PLAIN;
case "plain_content": return OutputMode.PLAIN_CONTENT;
}
}
@Comment("Avoids sending new messages by editing the most recent message until it reaches it's maximum length")
public boolean useEditing = true;
@Comment("If console messages should be silent, not causing a notification")
public boolean silentMessages = true;
public enum OutputMode {
ANSI("```ansi\n", "```"),
LOG("```accesslog\n", "```"),
DIFF("```diff\n", "```"),
PLAIN("```\n", "```"),
PLAIN_CONTENT("", "");
private final String prefix;
private final String suffix;
OutputMode(String prefix, String suffix) {
this.prefix = prefix;
this.suffix = suffix;
}
public String prefix() {
return prefix;
}
public String suffix() {
return suffix;
}
}
}

View File

@ -0,0 +1,58 @@
package com.discordsrv.common.console;
import com.discordsrv.common.DiscordSRV;
import com.discordsrv.common.config.main.ConsoleConfig;
import com.discordsrv.common.console.entry.LogEntry;
import com.discordsrv.common.logging.LogAppender;
import com.discordsrv.common.logging.LogLevel;
import com.discordsrv.common.logging.NamedLogger;
import com.discordsrv.common.logging.backend.LoggingBackend;
import com.discordsrv.common.module.type.AbstractModule;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
public class ConsoleModule extends AbstractModule<DiscordSRV> implements LogAppender {
private LoggingBackend backend;
private final List<SingleConsoleHandler> handlers = new ArrayList<>();
public ConsoleModule(DiscordSRV discordSRV) {
super(discordSRV, new NamedLogger(discordSRV, "CONSOLE"));
}
@Override
public void enable() {
backend = discordSRV.console().loggingBackend();
backend.addAppender(this);
reload();
}
@Override
public void reloadNoResult() {
handlers.add(new SingleConsoleHandler(discordSRV, new ConsoleConfig())); // TODO
}
@Override
public void disable() {
if (backend != null) {
backend.removeAppender(this);
}
}
@Override
public void append(
@Nullable String loggerName,
@NotNull LogLevel logLevel,
@Nullable String message,
@Nullable Throwable throwable
) {
LogEntry entry = new LogEntry(loggerName, logLevel, message, throwable);
for (SingleConsoleHandler handler : handlers) {
handler.queue(entry);
}
}
}

View File

@ -0,0 +1,125 @@
package com.discordsrv.common.console;
import com.discordsrv.common.DiscordSRV;
import com.discordsrv.common.config.main.ConsoleConfig;
import com.discordsrv.common.console.entry.LogEntry;
import com.discordsrv.common.console.entry.LogMessage;
import com.discordsrv.common.logging.LogLevel;
import net.dv8tion.jda.api.entities.Message;
import org.apache.commons.lang3.exception.ExceptionUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.LinkedBlockingQueue;
public class SingleConsoleHandler {
private static final int MESSAGE_MAX_LENGTH = Message.MAX_CONTENT_LENGTH;
private final DiscordSRV discordSRV;
private final ConsoleConfig config;
private final Queue<LogEntry> queue = new LinkedBlockingQueue<>();
private final List<LogMessage> messageCache;
public SingleConsoleHandler(DiscordSRV discordSRV, ConsoleConfig config) {
this.discordSRV = discordSRV;
this.config = config;
this.messageCache = config.useEditing ? new ArrayList<>() : null;
}
public void queue(LogEntry entry) {
queue.offer(entry);
}
public void shutdown() {
}
private void processQueue() {
ConsoleConfig.OutputMode outputMode = config.getOutputMode();
}
private List<String> formatEntry(LogEntry entry) {
String message = entry.message();
String throwable = ExceptionUtils.getMessage(entry.throwable());
message = discordSRV.placeholderService().replacePlaceholders(config.lineFormat, entry);
ConsoleConfig.OutputMode outputMode = config.getOutputMode();
String prefix = outputMode.prefix();
String suffix = outputMode.suffix();
int blockLength = prefix.length() + suffix.length();
int maximumPart = MESSAGE_MAX_LENGTH - blockLength - "\n".length();
if (outputMode == ConsoleConfig.OutputMode.DIFF) {
message = getLogLevelDiffCharacter(entry.level()) + message;
// TODO: also format throwable?
}
message += "\n";
if (!throwable.isEmpty()) {
throwable += "\n";
}
List<String> formatted = new ArrayList<>();
// Handle message being longer than a message
message = cutToSizeIfNeeded(message, blockLength, maximumPart, formatted);
// Handle log entry being longer than a message
int totalLength = blockLength + throwable.length() + message.length();
if (totalLength > MESSAGE_MAX_LENGTH) {
StringBuilder builder = new StringBuilder(message);
for (String line : throwable.split("\n")) {
line += "\n";
// Handle a single line of a throwable being longer than a message
line = cutToSizeIfNeeded(line, blockLength, maximumPart, formatted);
if (blockLength + line.length() > MESSAGE_MAX_LENGTH) {
// Need to split here
formatted.add(builder.toString());
builder.setLength(0);
}
builder.append(line);
}
formatted.add(builder.toString());
} else {
formatted.add(message + throwable);
}
return formatted;
}
private String cutToSizeIfNeeded(
String content,
int blockLength,
int maximumPart,
List<String> formatted
) {
while (content.length() + blockLength > MESSAGE_MAX_LENGTH) {
String cutToSize = content.substring(0, maximumPart) + "\n";
if (cutToSize.endsWith("\n\n")) {
// maximumPart excludes the newline at the end of message/line
cutToSize = cutToSize.substring(0, cutToSize.length() - 1);
}
formatted.add(cutToSize);
content = content.substring(maximumPart);
}
return content;
}
private String getLogLevelDiffCharacter(LogLevel level) {
if (level == LogLevel.StandardLogLevel.WARNING) {
return "+";
} else if (level == LogLevel.StandardLogLevel.ERROR) {
return "-";
}
return " ";
}
}

View File

@ -0,0 +1,61 @@
package com.discordsrv.common.console.entry;
import com.discordsrv.api.placeholder.PlaceholderLookupResult;
import com.discordsrv.api.placeholder.annotation.Placeholder;
import com.discordsrv.api.placeholder.annotation.PlaceholderRemainder;
import com.discordsrv.common.logging.LogLevel;
import java.time.Instant;
import java.time.format.DateTimeFormatter;
import java.util.LinkedHashSet;
import java.util.Set;
public class LogEntry {
@Placeholder("logger_name")
private final String loggerName;
@Placeholder("log_level")
private final LogLevel level;
private final String message;
private final Throwable throwable;
private final Instant logTime;
public LogEntry(String loggerName, LogLevel level, String message, Throwable throwable) {
this.loggerName = loggerName;
this.level = level;
this.message = message;
this.throwable = throwable;
this.logTime = Instant.now();
}
public String loggerName() {
return loggerName;
}
public LogLevel level() {
return level;
}
public String message() {
return message;
}
public Throwable throwable() {
return throwable;
}
public Instant logTime() {
return logTime;
}
@Placeholder("log_time")
public PlaceholderLookupResult _logTimePlaceholder(@PlaceholderRemainder String format) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(format); // TODO: cache
Set<Object> extras = new LinkedHashSet<>();
extras.add(formatter);
extras.add(logTime);
return PlaceholderLookupResult.newLookup("date", extras);
}
}

View File

@ -0,0 +1,19 @@
package com.discordsrv.common.console.entry;
public class LogMessage {
private final LogEntry entry;
private String formatted;
public LogMessage(LogEntry entry) {
this.entry = entry;
}
public String getFormatted() {
return formatted;
}
public void setFormatted(String formatted) {
this.formatted = formatted;
}
}

View File

@ -149,8 +149,8 @@ public class Log4JLoggerImpl implements Logger, LoggingBackend {
public boolean removeAppender(LogAppender appender) {
if (logger instanceof org.apache.logging.log4j.core.Logger) {
org.apache.logging.log4j.core.Logger loggerImpl = (org.apache.logging.log4j.core.Logger) logger;
Appender log4jAppender = appenders.get(appender);
loggerImpl.addAppender(log4jAppender);
Appender log4jAppender = appenders.remove(appender);
loggerImpl.removeAppender(log4jAppender);
return true;
}
return false;