Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions application/config.json.template
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@
"wsh"
],
"logInfoChannelWebhook": "<put_your_webhook_here>",
"logErrorChannelWebhook": "<put_your_webhook_here>"
"openaiApiKey": "<check pins in #tjbot_discussion for the key>"
"logErrorChannelWebhook": "<put_your_webhook_here>",
"openaiApiKey": "<check pins in #tjbot_discussion for the key>",
"sourceCodeBaseUrl": "<https://github.com/<your_account_here>/<your_repo_here>/blob/master/application/src/main/java/>"
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ public final class Config {
private final String logInfoChannelWebhook;
private final String logErrorChannelWebhook;
private final String openaiApiKey;
private final String sourceCodeBaseUrl;

@SuppressWarnings("ConstructorWithTooManyParameters")
@JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
Expand Down Expand Up @@ -72,7 +73,8 @@ private Config(@JsonProperty(value = "token", required = true) String token,
required = true) String logInfoChannelWebhook,
@JsonProperty(value = "logErrorChannelWebhook",
required = true) String logErrorChannelWebhook,
@JsonProperty(value = "openaiApiKey", required = true) String openaiApiKey) {
@JsonProperty(value = "openaiApiKey", required = true) String openaiApiKey,
@JsonProperty(value = "sourceCodeBaseUrl", required = true) String sourceCodeBaseUrl) {
this.token = Objects.requireNonNull(token);
this.gistApiKey = Objects.requireNonNull(gistApiKey);
this.databasePath = Objects.requireNonNull(databasePath);
Expand All @@ -96,6 +98,7 @@ private Config(@JsonProperty(value = "token", required = true) String token,
this.logInfoChannelWebhook = Objects.requireNonNull(logInfoChannelWebhook);
this.logErrorChannelWebhook = Objects.requireNonNull(logErrorChannelWebhook);
this.openaiApiKey = Objects.requireNonNull(openaiApiKey);
this.sourceCodeBaseUrl = Objects.requireNonNull(sourceCodeBaseUrl);
}

/**
Expand Down Expand Up @@ -316,4 +319,15 @@ public String getLogErrorChannelWebhook() {
public String getOpenaiApiKey() {
return openaiApiKey;
}

/**
* The base URL of the source code of this bot. E.g.
* {@code getSourceCodeBaseUrl() + "/org/togetherjava/tjbot/config/Config.java"} would point to
* this file.
*
* @return the base url of the source code of this bot
*/
public String getSourceCodeBaseUrl() {
return sourceCodeBaseUrl;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ final class DiscordLogAppender extends AbstractAppender {
private final DiscordLogForwarder logForwarder;

private DiscordLogAppender(String name, Filter filter, StringLayout layout,
boolean ignoreExceptions, URI webhook) {
boolean ignoreExceptions, URI webhook, String sourceCodeBaseUrl) {
super(name, filter, layout, ignoreExceptions, NO_PROPERTIES);

logForwarder = new DiscordLogForwarder(webhook);
logForwarder = new DiscordLogForwarder(webhook, sourceCodeBaseUrl);
}

@Override
Expand All @@ -43,11 +43,19 @@ static final class DiscordLogAppenderBuilder
@Required
private URI webhook;

@Required
private String sourceCodeBaseUrl;

public DiscordLogAppenderBuilder setWebhook(URI webhook) {
this.webhook = webhook;
return asBuilder();
}

public DiscordLogAppenderBuilder setSourceCodeBaseUrl(String sourceCodeBaseUrl) {
this.sourceCodeBaseUrl = sourceCodeBaseUrl;
return asBuilder();
}

@Override
public DiscordLogAppender build() {
Layout<? extends Serializable> layout = getOrCreateLayout();
Expand All @@ -58,7 +66,7 @@ public DiscordLogAppender build() {
String name = Objects.requireNonNull(getName());

return new DiscordLogAppender(name, getFilter(), (StringLayout) layout,
isIgnoreExceptions(), webhook);
isIgnoreExceptions(), webhook, sourceCodeBaseUrl);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.core.LogEvent;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand Down Expand Up @@ -70,16 +71,23 @@ final class DiscordLogForwarder {
0xDFDF00, Level.ERROR, 0xBF2200, Level.FATAL, 0xFF8484);

private final WebhookClient webhookClient;
private final String sourceCodeBaseUrl;
/**
* Internal buffer of logs that still have to be forwarded to Discord. Actions are synchronized
* using {@link #pendingLogsLock} to ensure thread safety.
*/
private final Queue<LogMessage> pendingLogs = new PriorityQueue<>();
private final Object pendingLogsLock = new Object();

DiscordLogForwarder(URI webhook) {
DiscordLogForwarder(URI webhook, String sourceCodeBaseUrl) {
webhookClient = WebhookClient.withUrl(webhook.toString());

if (!sourceCodeBaseUrl.endsWith("/")) {
this.sourceCodeBaseUrl = sourceCodeBaseUrl + "/";
} else {
this.sourceCodeBaseUrl = sourceCodeBaseUrl;
}

SERVICE.scheduleWithFixedDelay(this::processPendingLogs, 5, 5, TimeUnit.SECONDS);
}

Expand Down Expand Up @@ -110,7 +118,7 @@ void forwardLogEvent(LogEvent event) {
""");
}

LogMessage log = LogMessage.ofEvent(event);
LogMessage log = LogMessage.ofEvent(event, sourceCodeBaseUrl);

synchronized (pendingLogsLock) {
pendingLogs.add(log);
Expand Down Expand Up @@ -160,22 +168,24 @@ private List<LogMessage> validateBatch(List<LogMessage> logBatch) {
private record LogMessage(WebhookEmbed embed,
Instant timestamp) implements Comparable<LogMessage> {

private static LogMessage ofEvent(LogEvent event) {
private static final String BASE_PACKAGE = "org.togetherjava.tjbot.";

private static LogMessage ofEvent(LogEvent event, String sourceCodeBaseUrl) {
String authorName = event.getLoggerName();
String authorUrl = linkToSource(event.getSource(), sourceCodeBaseUrl).orElse(null);
String title = event.getLevel().name();
int colorDecimal = Objects.requireNonNull(LEVEL_TO_AMBIENT_COLOR.get(event.getLevel()));
String description =
MessageUtils.abbreviate(describeLogEvent(event), MAX_EMBED_DESCRIPTION);
Instant timestamp = Instant.ofEpochMilli(event.getInstant().getEpochMillisecond());

WebhookEmbed embed = new WebhookEmbedBuilder()
.setAuthor(new WebhookEmbed.EmbedAuthor(authorName, null, null))
.setAuthor(new WebhookEmbed.EmbedAuthor(authorName, null, authorUrl))
.setTitle(new WebhookEmbed.EmbedTitle(title, null))
.setDescription(description)
.setColor(colorDecimal)
.setTimestamp(timestamp)
.build();

return new LogMessage(embed, timestamp);
}

Expand All @@ -193,6 +203,21 @@ private static String describeLogEvent(LogEvent event) {
return logMessage + "\n" + exceptionWriter.toString().replace("\t", "> ");
}

private static Optional<String> linkToSource(@Nullable StackTraceElement sourceElement,
String sourceCodeBaseUrl) {
if (sourceElement == null) {
return Optional.empty();
}

String source = sourceElement.getClassName();
if (!source.startsWith(BASE_PACKAGE)) {
return Optional.empty();
}

String link = "%s%s.java".formatted(sourceCodeBaseUrl, source.replace('.', '/'));
return Optional.of(link);
}

private LogMessage shortened() {
String shortDescription = MessageUtils.abbreviate(
Objects.requireNonNull(embed.getDescription()), MAX_EMBED_DESCRIPTION_SHORT);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,11 @@ public static void startDiscordLogging(Config botConfig) {
private static void addAppenders(Configuration logConfig, Config botConfig) {
parseWebhookUri(botConfig.getLogInfoChannelWebhook())
.ifPresent(webhookUri -> addDiscordLogAppender("DiscordInfo", createInfoRangeFilter(),
webhookUri, logConfig));
webhookUri, botConfig.getSourceCodeBaseUrl(), logConfig));

parseWebhookUri(botConfig.getLogErrorChannelWebhook())
.ifPresent(webhookUri -> addDiscordLogAppender("DiscordError", createErrorRangeFilter(),
webhookUri, logConfig));
webhookUri, botConfig.getSourceCodeBaseUrl(), logConfig));
}

private static Optional<URI> parseWebhookUri(String webhookUri) {
Expand All @@ -71,7 +71,7 @@ private static Optional<URI> parseWebhookUri(String webhookUri) {
// to the config.
@SuppressWarnings("squid:S4792")
private static void addDiscordLogAppender(String name, Filter filter, URI webhookUri,
Configuration logConfig) {
String sourceCodeBaseUrl, Configuration logConfig) {
// NOTE The whole setup is done programmatically in order to allow the webhooks
// to be read from the config file
Filter[] filters = {filter, createDenyMarkerFilter(LogMarkers.NO_DISCORD.getName()),
Expand All @@ -80,6 +80,7 @@ private static void addDiscordLogAppender(String name, Filter filter, URI webhoo
Appender appender = DiscordLogAppender.newBuilder()
.setName(name)
.setWebhook(webhookUri)
.setSourceCodeBaseUrl(sourceCodeBaseUrl)
.setFilter(CompositeFilter.createFilters(filters))
.build();

Expand Down