Added more context to errors (#1450)

* Improved Extension errors
* Removed scary reflective operation exception
* Additional context to SQL Exceptions
* Added error context to Listeners
* Added error context to most error logging places
* Ignore cyclomatic complexity of DBOpException

Adds context to almost all error logging situations, except those where it is unknown, or to commands that are being refactored on another branch.

Close #1245
This commit is contained in:
Risto Lahtela 2020-05-15 12:20:29 +03:00 committed by GitHub
parent 9cd0bcd369
commit 93dc1bbb9e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
61 changed files with 398 additions and 125 deletions

View File

@ -116,4 +116,15 @@ public final class Request {
public Request omitFirstInPath() {
return new Request(method, path.omitFirst(), query, user, headers);
}
@Override
public String toString() {
return "Request{" +
"method='" + method + '\'' +
", path=" + path +
", query=" + query +
", user=" + user +
", headers=" + headers +
'}';
}
}

View File

@ -119,4 +119,11 @@ public final class URIPath {
}
return count;
}
@Override
public String toString() {
return "URIPath{" +
"path='" + path + '\'' +
'}';
}
}

View File

@ -94,4 +94,11 @@ public final class URIQuery {
}
return builder.toString();
}
@Override
public String toString() {
return "URIQuery{" +
"byKey=" + byKey +
'}';
}
}

View File

@ -59,4 +59,13 @@ public final class WebUser {
public String getUsername() {
return username;
}
@Override
public String toString() {
return "WebUser{" +
"playerName='" + playerName + '\'' +
", username='" + username + '\'' +
", permissions=" + permissions +
'}';
}
}

View File

@ -18,6 +18,7 @@ package com.djrapitops.plan.addons.placeholderapi;
import com.djrapitops.plan.PlanSystem;
import com.djrapitops.plan.placeholder.PlanPlaceholders;
import com.djrapitops.plan.utilities.logging.ErrorContext;
import com.djrapitops.plan.utilities.logging.ErrorLogger;
import com.djrapitops.plan.version.VersionChecker;
import com.djrapitops.plugin.logging.L;
@ -92,7 +93,7 @@ public class PlanPlaceholderExtension extends PlaceholderExpansion {
return value;
} catch (Exception e) {
errorLogger.log(L.WARN, getClass(), e);
errorLogger.log(L.WARN, e, ErrorContext.builder().whatToDo("Report this").related("Placeholder Request", params).build());
return null;
}
}

View File

@ -19,6 +19,7 @@ package com.djrapitops.plan.gathering.listeners.bukkit;
import com.djrapitops.plan.gathering.afk.AFKTracker;
import com.djrapitops.plan.settings.Permissions;
import com.djrapitops.plan.settings.config.PlanConfig;
import com.djrapitops.plan.utilities.logging.ErrorContext;
import com.djrapitops.plan.utilities.logging.ErrorLogger;
import com.djrapitops.plugin.logging.L;
import org.bukkit.entity.Player;
@ -79,7 +80,7 @@ public class BukkitAFKListener implements Listener {
AFK_TRACKER.performedAction(uuid, time);
} catch (Exception e) {
errorLogger.log(L.ERROR, this.getClass(), e);
errorLogger.log(L.ERROR, e, ErrorContext.builder().related(event).build());
}
}

View File

@ -21,6 +21,7 @@ import com.djrapitops.plan.gathering.cache.NicknameCache;
import com.djrapitops.plan.identification.ServerInfo;
import com.djrapitops.plan.storage.database.DBSystem;
import com.djrapitops.plan.storage.database.transactions.events.NicknameStoreTransaction;
import com.djrapitops.plan.utilities.logging.ErrorContext;
import com.djrapitops.plan.utilities.logging.ErrorLogger;
import com.djrapitops.plugin.logging.L;
import org.bukkit.entity.Player;
@ -66,7 +67,7 @@ public class ChatListener implements Listener {
try {
actOnChatEvent(event);
} catch (Exception e) {
errorLogger.log(L.ERROR, this.getClass(), e);
errorLogger.log(L.ERROR, e, ErrorContext.builder().related(event).build());
}
}

View File

@ -23,6 +23,7 @@ import com.djrapitops.plan.gathering.domain.Session;
import com.djrapitops.plan.processing.Processing;
import com.djrapitops.plan.processing.processors.player.MobKillProcessor;
import com.djrapitops.plan.processing.processors.player.PlayerKillProcessor;
import com.djrapitops.plan.utilities.logging.ErrorContext;
import com.djrapitops.plan.utilities.logging.ErrorLogger;
import com.djrapitops.plugin.logging.L;
import org.bukkit.Material;
@ -80,7 +81,7 @@ public class DeathEventListener implements Listener {
UUID uuid = dead instanceof Player ? dead.getUniqueId() : null;
handleKill(time, uuid, killerEntity);
} catch (Exception e) {
errorLogger.log(L.ERROR, this.getClass(), e);
errorLogger.log(L.ERROR, e, ErrorContext.builder().related(event, dead).build());
}
}

View File

@ -22,6 +22,7 @@ import com.djrapitops.plan.identification.ServerInfo;
import com.djrapitops.plan.settings.config.WorldAliasSettings;
import com.djrapitops.plan.storage.database.DBSystem;
import com.djrapitops.plan.storage.database.transactions.events.WorldNameStoreTransaction;
import com.djrapitops.plan.utilities.logging.ErrorContext;
import com.djrapitops.plan.utilities.logging.ErrorLogger;
import com.djrapitops.plugin.logging.L;
import org.bukkit.entity.Player;
@ -67,7 +68,7 @@ public class GameModeChangeListener implements Listener {
try {
actOnEvent(event);
} catch (Exception e) {
errorLogger.log(L.ERROR, this.getClass(), e);
errorLogger.log(L.ERROR, e, ErrorContext.builder().related(event, event.getPlayer().getGameMode() + "->" + event.getNewGameMode()).build());
}
}

View File

@ -36,6 +36,7 @@ import com.djrapitops.plan.settings.config.paths.ExportSettings;
import com.djrapitops.plan.storage.database.DBSystem;
import com.djrapitops.plan.storage.database.Database;
import com.djrapitops.plan.storage.database.transactions.events.*;
import com.djrapitops.plan.utilities.logging.ErrorContext;
import com.djrapitops.plan.utilities.logging.ErrorLogger;
import com.djrapitops.plugin.logging.L;
import org.bukkit.entity.Player;
@ -107,7 +108,7 @@ public class PlayerOnlineListener implements Listener {
dbSystem.getDatabase().executeTransaction(new BanStatusTransaction(playerUUID, () -> banned));
dbSystem.getDatabase().executeTransaction(new OperatorStatusTransaction(playerUUID, operator));
} catch (Exception e) {
errorLogger.log(L.ERROR, this.getClass(), e);
errorLogger.log(L.ERROR, e, ErrorContext.builder().related(event, event.getResult()).build());
}
}
@ -132,7 +133,7 @@ public class PlayerOnlineListener implements Listener {
dbSystem.getDatabase().executeTransaction(new KickStoreTransaction(uuid));
} catch (Exception e) {
errorLogger.log(L.ERROR, this.getClass(), e);
errorLogger.log(L.ERROR, e, ErrorContext.builder().related(event).build());
}
}
@ -141,7 +142,7 @@ public class PlayerOnlineListener implements Listener {
try {
actOnJoinEvent(event);
} catch (Exception e) {
errorLogger.log(L.ERROR, this.getClass(), e);
errorLogger.log(L.ERROR, e, ErrorContext.builder().related(event).build());
}
}
@ -205,7 +206,7 @@ public class PlayerOnlineListener implements Listener {
try {
actOnQuitEvent(event);
} catch (Exception e) {
errorLogger.log(L.ERROR, this.getClass(), e);
errorLogger.log(L.ERROR, e, ErrorContext.builder().related(event).build());
}
}

View File

@ -22,6 +22,7 @@ import com.djrapitops.plan.identification.ServerInfo;
import com.djrapitops.plan.settings.config.WorldAliasSettings;
import com.djrapitops.plan.storage.database.DBSystem;
import com.djrapitops.plan.storage.database.transactions.events.WorldNameStoreTransaction;
import com.djrapitops.plan.utilities.logging.ErrorContext;
import com.djrapitops.plan.utilities.logging.ErrorLogger;
import com.djrapitops.plugin.logging.L;
import org.bukkit.entity.Player;
@ -59,7 +60,7 @@ public class WorldChangeListener implements Listener {
try {
actOnEvent(event);
} catch (Exception e) {
errorLogger.log(L.ERROR, this.getClass(), e);
errorLogger.log(L.ERROR, e, ErrorContext.builder().related(event).build());
}
}

View File

@ -89,8 +89,7 @@ public class BukkitPingCounter extends AbsRunnable implements Listener {
} catch (NoSuchMethodException | IllegalAccessException | NoSuchFieldException reflectiveEx) {
Logger.getGlobal().log(
Level.WARNING,
"Plan: Reflective exception in static initializer of PingCountTimer",
reflectiveEx
"Plan: Could not register Ping counter due to " + reflectiveEx
);
} catch (IllegalArgumentException e) {
Logger.getGlobal().log(

View File

@ -34,6 +34,7 @@ import com.djrapitops.plan.storage.database.DBSystem;
import com.djrapitops.plan.storage.database.Database;
import com.djrapitops.plan.storage.database.transactions.events.GeoInfoStoreTransaction;
import com.djrapitops.plan.storage.database.transactions.events.PlayerRegisterTransaction;
import com.djrapitops.plan.utilities.logging.ErrorContext;
import com.djrapitops.plan.utilities.logging.ErrorLogger;
import com.djrapitops.plugin.logging.L;
import net.md_5.bungee.api.connection.ProxiedPlayer;
@ -92,7 +93,7 @@ public class PlayerOnlineListener implements Listener {
try {
actOnLogin(event);
} catch (Exception e) {
errorLogger.log(L.WARN, this.getClass(), e);
errorLogger.log(L.ERROR, e, ErrorContext.builder().related(event).build());
}
}
@ -142,7 +143,7 @@ public class PlayerOnlineListener implements Listener {
try {
actOnLogout(event);
} catch (Exception e) {
errorLogger.log(L.WARN, this.getClass(), e);
errorLogger.log(L.ERROR, e, ErrorContext.builder().related(event).build());
}
}
@ -179,7 +180,7 @@ public class PlayerOnlineListener implements Listener {
try {
actOnServerSwitch(event);
} catch (Exception e) {
errorLogger.log(L.WARN, this.getClass(), e);
errorLogger.log(L.ERROR, e, ErrorContext.builder().related(event).build());
}
}

View File

@ -37,6 +37,7 @@ import com.djrapitops.plan.settings.SettingsSvc;
import com.djrapitops.plan.settings.locale.LocaleSystem;
import com.djrapitops.plan.storage.database.DBSystem;
import com.djrapitops.plan.storage.file.PlanFiles;
import com.djrapitops.plan.utilities.logging.ErrorContext;
import com.djrapitops.plan.utilities.logging.ErrorLogger;
import com.djrapitops.plan.version.VersionChecker;
import com.djrapitops.plugin.benchmarking.Benchmark;
@ -222,7 +223,7 @@ public class PlanSystem implements SubSystem {
system.disable();
}
} catch (Exception e) {
errorLogger.log(L.WARN, this.getClass(), e);
errorLogger.log(L.WARN, e, ErrorContext.builder().related("Disabling PlanSystem: " + system).build());
}
}
}

View File

@ -73,7 +73,7 @@ public class ExportScheduler {
private void schedulePlayersPageExport() {
long period = TimeAmount.toTicks(config.get(ExportSettings.EXPORT_PERIOD), TimeUnit.MILLISECONDS);
taskSystem.registerTask("Players page export",
new ExportTask(exporter, Exporter::exportPlayersPage, logger, errorLogger)
new ExportTask(exporter, Exporter::exportPlayersPage, errorLogger)
).runTaskTimerAsynchronously(0L, period);
}
@ -89,7 +89,7 @@ public class ExportScheduler {
Optional<Server> proxy = servers.stream().filter(Server::isProxy).findFirst();
proxy.ifPresent(mainServer -> taskSystem.registerTask("Network export",
new ExportTask(exporter, exporter -> exporter.exportServerPage(mainServer), logger, errorLogger))
new ExportTask(exporter, exporter -> exporter.exportServerPage(mainServer), errorLogger))
.runTaskTimerAsynchronously(0L, period)
);
@ -99,7 +99,7 @@ public class ExportScheduler {
new ExportTask(exporter, same -> {
same.exportServerPage(server);
same.exportServerJSON(server);
}, logger, errorLogger))
}, errorLogger))
.runTaskTimerAsynchronously(offset * offsetMultiplier, period);
offsetMultiplier++;
}

View File

@ -19,27 +19,24 @@ package com.djrapitops.plan.delivery.export;
import com.djrapitops.plan.exceptions.ExportException;
import com.djrapitops.plan.exceptions.database.DBOpException;
import com.djrapitops.plan.utilities.java.ThrowingConsumer;
import com.djrapitops.plan.utilities.logging.ErrorContext;
import com.djrapitops.plan.utilities.logging.ErrorLogger;
import com.djrapitops.plugin.logging.L;
import com.djrapitops.plugin.logging.console.PluginLogger;
import com.djrapitops.plugin.task.AbsRunnable;
public class ExportTask extends AbsRunnable {
private final Exporter exporter;
private final ThrowingConsumer<Exporter, ExportException> exportAction;
private final PluginLogger logger;
private final ErrorLogger errorLogger;
public ExportTask(
Exporter exporter,
ThrowingConsumer<Exporter, ExportException> exportAction,
PluginLogger logger,
ErrorLogger errorLogger
) {
this.exporter = exporter;
this.exportAction = exportAction;
this.logger = logger;
this.errorLogger = errorLogger;
}
@ -48,22 +45,26 @@ public class ExportTask extends AbsRunnable {
try {
exportAction.accept(exporter);
} catch (ExportException e) {
errorLogger.log(L.WARN, this.getClass(), e);
errorLogger.log(L.WARN, e, ErrorContext.builder().related("Export task run").build());
} catch (DBOpException dbException) {
handleDBException(dbException);
} catch (Exception | NoClassDefFoundError | NoSuchMethodError | NoSuchFieldError e) {
logger.error("Export Task Disabled due to error, reload Plan to re-enable.");
errorLogger.log(L.ERROR, this.getClass(), e);
errorLogger.log(L.ERROR, e, ErrorContext.builder()
.whatToDo("Export Task Disabled due to error - reload Plan to re-enable.")
.related("Export task run").build());
cancel();
}
}
private void handleDBException(DBOpException dbException) {
logger.error("Export Task Disabled due to database error, reload Plan to re-enable.");
if (dbException.getMessage().contains("closed")) {
logger.warn("(Error was caused by database closing, so this error can possibly be ignored.)");
errorLogger.log(L.ERROR, dbException, ErrorContext.builder()
.whatToDo("Export Task Disabled due to error - database is closing, so this error can be ignored.).")
.related("Export task run").build());
} else {
errorLogger.log(L.ERROR, this.getClass(), dbException);
errorLogger.log(L.ERROR, dbException, ErrorContext.builder()
.whatToDo("Export Task Disabled due to error - reload Plan to re-enable.")
.related("Export task run").build());
}
cancel();
}

View File

@ -31,6 +31,7 @@ import com.djrapitops.plan.identification.ServerInfo;
import com.djrapitops.plan.identification.properties.ServerProperties;
import com.djrapitops.plan.storage.database.Database;
import com.djrapitops.plan.storage.file.ResourceCache;
import com.djrapitops.plan.utilities.logging.ErrorContext;
import com.djrapitops.plan.utilities.logging.ErrorLogger;
import com.djrapitops.plan.version.VersionChecker;
import com.djrapitops.plugin.benchmarking.Benchmark;
@ -135,7 +136,7 @@ public class DebugPage implements Page {
}
content.append("</pre>");
} catch (Exception e) {
errorLogger.log(L.WARN, this.getClass(), e);
errorLogger.log(L.WARN, e, ErrorContext.builder().related("/debug page access, resource cache").build());
}
}
@ -151,7 +152,7 @@ public class DebugPage implements Page {
}
content.append("</pre>");
} catch (Exception e) {
errorLogger.log(L.WARN, this.getClass(), e);
errorLogger.log(L.WARN, e, ErrorContext.builder().related("/debug page access, JSON cache").build());
}
}
@ -172,7 +173,7 @@ public class DebugPage implements Page {
}
content.append("</pre>");
} catch (Exception e) {
errorLogger.log(L.WARN, this.getClass(), e);
errorLogger.log(L.WARN, e, ErrorContext.builder().related("/debug page access, Session cache").build());
}
}

View File

@ -23,6 +23,7 @@ import com.djrapitops.plan.settings.locale.Locale;
import com.djrapitops.plan.settings.locale.lang.PluginLang;
import com.djrapitops.plan.storage.file.PlanFiles;
import com.djrapitops.plan.storage.file.Resource;
import com.djrapitops.plan.utilities.logging.ErrorContext;
import com.djrapitops.plan.utilities.logging.ErrorLogger;
import com.djrapitops.plugin.logging.L;
import com.djrapitops.plugin.logging.console.PluginLogger;
@ -145,7 +146,9 @@ public class ResourceSvc implements ResourceService {
return getOrWriteCustomized(fileName, source);
}
} catch (IOException e) {
errorLogger.log(L.WARN, getClass(), e.getCause());
errorLogger.log(L.WARN, e, ErrorContext.builder()
.whatToDo("Report this or provide " + fileName + " in " + files.getCustomizationDirectory())
.related("Fetching resource", "Of: " + pluginName, fileName).build());
}
// Return original by default
return source.get();

View File

@ -19,6 +19,7 @@ package com.djrapitops.plan.delivery.webserver;
import com.djrapitops.plan.settings.config.PlanConfig;
import com.djrapitops.plan.settings.config.paths.PluginSettings;
import com.djrapitops.plan.settings.config.paths.WebserverSettings;
import com.djrapitops.plan.utilities.logging.ErrorContext;
import com.djrapitops.plan.utilities.logging.ErrorLogger;
import com.djrapitops.plugin.logging.L;
import com.djrapitops.plugin.logging.console.PluginLogger;
@ -73,9 +74,10 @@ public class NonProxyWebserverDisableChecker implements Runnable {
config.set(WebserverSettings.DISABLED, true);
config.save();
logger.warn("Note: Set '" + WebserverSettings.DISABLED.getPath() + "' to true");
} catch (IOException e) {
errorLogger.log(L.WARN, this.getClass(), e);
errorLogger.log(L.WARN, e, ErrorContext.builder()
.whatToDo("Set '" + WebserverSettings.DISABLED.getPath() + "' to true manually.")
.related("Disabling webserver in config setting", WebserverSettings.DISABLED.getPath()).build());
}
}
}

View File

@ -31,6 +31,7 @@ import com.djrapitops.plan.settings.config.PlanConfig;
import com.djrapitops.plan.settings.config.paths.PluginSettings;
import com.djrapitops.plan.settings.config.paths.WebserverSettings;
import com.djrapitops.plan.storage.database.DBSystem;
import com.djrapitops.plan.utilities.logging.ErrorContext;
import com.djrapitops.plan.utilities.logging.ErrorLogger;
import com.djrapitops.plugin.logging.L;
import com.djrapitops.plugin.logging.console.PluginLogger;
@ -99,7 +100,10 @@ public class RequestHandler implements HttpHandler {
} catch (Exception e) {
if (config.isTrue(PluginSettings.DEV_MODE)) {
logger.warn("THIS ERROR IS ONLY LOGGED IN DEV MODE:");
errorLogger.log(L.WARN, this.getClass(), e);
errorLogger.log(L.WARN, e, ErrorContext.builder()
.whatToDo("THIS ERROR IS ONLY LOGGED IN DEV MODE")
.related(exchange.getRequestMethod(), exchange.getRemoteAddress(), exchange.getRequestHeaders(), exchange.getResponseHeaders(), exchange.getRequestURI())
.build());
}
} finally {
exchange.close();

View File

@ -31,6 +31,7 @@ import com.djrapitops.plan.delivery.webserver.resolver.auth.*;
import com.djrapitops.plan.delivery.webserver.resolver.json.RootJSONResolver;
import com.djrapitops.plan.exceptions.WebUserAuthException;
import com.djrapitops.plan.exceptions.connection.ForbiddenException;
import com.djrapitops.plan.utilities.logging.ErrorContext;
import com.djrapitops.plan.utilities.logging.ErrorLogger;
import com.djrapitops.plugin.logging.L;
import dagger.Lazy;
@ -143,7 +144,7 @@ public class ResponseResolver {
} catch (WebUserAuthException e) {
throw e; // Pass along
} catch (Exception e) {
errorLogger.log(L.ERROR, this.getClass(), e);
errorLogger.log(L.ERROR, e, ErrorContext.builder().related(request).build());
return responseFactory.internalErrorResponse(e, request.getPath().asString());
}
}

View File

@ -23,6 +23,7 @@ import com.djrapitops.plan.settings.config.paths.WebserverSettings;
import com.djrapitops.plan.settings.locale.Locale;
import com.djrapitops.plan.settings.locale.lang.PluginLang;
import com.djrapitops.plan.storage.file.PlanFiles;
import com.djrapitops.plan.utilities.logging.ErrorContext;
import com.djrapitops.plan.utilities.logging.ErrorLogger;
import com.djrapitops.plugin.logging.L;
import com.djrapitops.plugin.logging.console.PluginLogger;
@ -145,7 +146,9 @@ public class WebServer implements SubSystem {
.namingPattern("Plan WebServer Thread-%d")
.uncaughtExceptionHandler((thread, throwable) -> {
if (config.isTrue(PluginSettings.DEV_MODE)) {
errorLogger.log(L.WARN, WebServer.class, throwable);
errorLogger.log(L.WARN, throwable, ErrorContext.builder()
.whatToDo("THIS ERROR IS ONLY LOGGED IN DEV MODE")
.build());
}
}).build()
);
@ -165,7 +168,7 @@ public class WebServer implements SubSystem {
logger.error("Webserver failed to bind port: " + failedToBind.toString());
enabled = false;
} catch (IllegalArgumentException | IllegalStateException | IOException e) {
errorLogger.log(L.ERROR, this.getClass(), e);
errorLogger.log(L.ERROR, e, ErrorContext.builder().related("Trying to enable webserver", config.get(WebserverSettings.INTERNAL_IP) + ":" + port).build());
enabled = false;
}
}
@ -183,7 +186,9 @@ public class WebServer implements SubSystem {
}
} catch (InvalidPathException e) {
logger.error("WebServer: Could not find Keystore: " + e.getMessage());
errorLogger.log(L.ERROR, this.getClass(), e);
errorLogger.log(L.ERROR, e, ErrorContext.builder()
.whatToDo(e.getMessage() + ", Fix this path to point to a valid keystore file: " + keyStorePath)
.related(keyStorePath).build());
}
char[] storepass = config.get(WebserverSettings.CERTIFICATE_STOREPASS).toCharArray();
@ -232,7 +237,7 @@ public class WebServer implements SubSystem {
logger.error(e.getMessage());
} catch (KeyManagementException | NoSuchAlgorithmException e) {
logger.error(locale.getString(PluginLang.WEB_SERVER_FAIL_SSL_CONTEXT));
errorLogger.log(L.ERROR, this.getClass(), e);
errorLogger.log(L.ERROR, e, ErrorContext.builder().related(keyStoreKind).build());
} catch (EOFException e) {
logger.error(locale.getString(PluginLang.WEB_SERVER_FAIL_EMPTY_FILE));
} catch (FileNotFoundException e) {
@ -241,11 +246,12 @@ public class WebServer implements SubSystem {
} catch (BindException e) {
throw e; // Pass to above error handler
} catch (IOException e) {
logger.error("WebServer: " + e);
errorLogger.log(L.ERROR, this.getClass(), e);
errorLogger.log(L.ERROR, e, ErrorContext.builder().related(config.get(WebserverSettings.INTERNAL_IP) + ":" + port).build());
} catch (KeyStoreException | CertificateException | UnrecoverableKeyException e) {
logger.error(locale.getString(PluginLang.WEB_SERVER_FAIL_STORE_LOAD));
errorLogger.log(L.ERROR, this.getClass(), e);
errorLogger.log(L.ERROR, e, ErrorContext.builder()
.whatToDo("Make sure the Certificate settings are correct / You can try remaking the keystore without -passin or -passout parameters.")
.related(keyStorePath).build());
}
return startSuccessful;
}

View File

@ -0,0 +1,25 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.exceptions;
import com.djrapitops.plan.utilities.logging.ErrorContext;
import java.util.Optional;
public interface ExceptionWithContext {
Optional<ErrorContext> getContext();
}

View File

@ -16,14 +16,20 @@
*/
package com.djrapitops.plan.exceptions.database;
import com.djrapitops.plan.exceptions.ExceptionWithContext;
import com.djrapitops.plan.utilities.logging.ErrorContext;
import java.sql.SQLException;
import java.util.Optional;
/**
* Runtime exception for wrapping database errors.
*
* @author Rsl1122
*/
public class DBOpException extends IllegalStateException {
public class DBOpException extends IllegalStateException implements ExceptionWithContext {
private ErrorContext context;
public DBOpException(String message) {
super(message);
@ -33,8 +39,100 @@ public class DBOpException extends IllegalStateException {
super(message, cause);
}
public static DBOpException forCause(String sql, SQLException e) {
return new DBOpException("SQL Failed: " + sql + "; " + e.getMessage(), e);
public DBOpException(String message, Throwable cause, ErrorContext context) {
super(message, cause);
this.context = context;
}
// Checkstyle.OFF: CyclomaticComplexity
public static DBOpException forCause(String sql, SQLException e) {
ErrorContext.Builder context = ErrorContext.builder();
int errorCode = e.getErrorCode();
context.related("Error code: " + errorCode)
.related(sql);
switch (errorCode) {
// SQLite Corrupt
case 10:
case 523:
context.related("SQL Corrupted")
.whatToDo("Your SQLite has corrupted. This can happen if .db-wal or .db-shm files get replaced mid operation. Restore database.db from backup.");
break;
// Syntax error codes
case 1054: // MySQL
case 1064:
case 1146:
case 42000: // H2
case 42001:
case 42101:
case 42102:
case 42111:
case 42112:
case 42121:
case 42122:
case 42132:
context.related("SQL Grammar error")
.whatToDo("Report this, there is an SQL grammar error.");
break;
// Type mismatch
case 20: // SQLite
context.related("SQL Type mismatch")
.whatToDo("Report this, there is an SQL Type mismatch.");
break;
// Duplicate key
case 1062:
case 23001:
case 23505:
context.related("Duplicate key")
.whatToDo("Report this, duplicate key exists in SQL.");
break;
// Constraint violation
case 19: // SQLite
case 275:
case 531:
case 787:
case 1043:
case 1299:
case 1555:
case 2579:
case 1811:
case 2067:
case 2323:
case 630: // MySQL
case 839:
case 840:
case 893:
case 1169:
case 1215:
case 1216:
case 1217:
case 1364:
case 1451:
case 1557:
case 22001: // H2
case 22003:
case 22012:
case 22018:
case 22025:
case 23000:
case 23002:
case 23502:
case 23506:
case 23507:
case 23513:
context.related("Constraint Violation")
.whatToDo("Report this, there is an SQL Constraint Violation.");
break;
default:
context.related("Unknown SQL Error code");
}
return new DBOpException("SQL Failure: " + e.getMessage(), e, context.build());
}
// Checkstyle.ON: CyclomaticComplexity
@Override
public Optional<ErrorContext> getContext() {
return Optional.ofNullable(context);
}
}

View File

@ -23,12 +23,14 @@ import com.djrapitops.plan.exceptions.DataExtensionMethodCallException;
import com.djrapitops.plan.extension.implementation.CallerImplementation;
import com.djrapitops.plan.extension.implementation.ExtensionRegister;
import com.djrapitops.plan.extension.implementation.ExtensionWrapper;
import com.djrapitops.plan.extension.implementation.providers.MethodWrapper;
import com.djrapitops.plan.extension.implementation.providers.gathering.ProviderValueGatherer;
import com.djrapitops.plan.identification.ServerInfo;
import com.djrapitops.plan.processing.Processing;
import com.djrapitops.plan.settings.config.ExtensionSettings;
import com.djrapitops.plan.settings.config.PlanConfig;
import com.djrapitops.plan.storage.database.DBSystem;
import com.djrapitops.plan.utilities.logging.ErrorContext;
import com.djrapitops.plan.utilities.logging.ErrorLogger;
import com.djrapitops.plugin.logging.L;
import com.djrapitops.plugin.logging.console.PluginLogger;
@ -88,8 +90,14 @@ public class ExtensionSvc implements ExtensionService {
try {
extensionRegister.registerBuiltInExtensions(config.getExtensionSettings().getDisabled());
} catch (IllegalStateException failedToRegisterOne) {
logger.warn("One or more extensions failed to register, see suppressed exceptions (They can be disabled in Plan config).");
errorLogger.log(L.WARN, ExtensionService.class, failedToRegisterOne);
ErrorContext.Builder context = ErrorContext.builder()
.whatToDo("Report and/or disable the failed extensions. You can find the failed extensions in the error file.");
for (Throwable suppressedException : failedToRegisterOne.getSuppressed()) {
context.related(suppressedException.getMessage());
}
logger.warn("One or more extensions failed to register (They can be disabled in Plan config).");
errorLogger.log(L.WARN, failedToRegisterOne, context.build());
}
}
@ -130,7 +138,9 @@ public class ExtensionSvc implements ExtensionService {
try {
pluginsConfig.createSection(pluginName);
} catch (IOException e) {
errorLogger.log(L.ERROR, this.getClass(), e);
errorLogger.log(L.WARN, e, ErrorContext.builder()
.whatToDo("Create 'Plugins." + pluginName + ".Enabled: true' setting manually.")
.related("Section: " + pluginName).build());
logger.warn("Could not register DataExtension for " + pluginName + " due to " + e.toString());
return true;
}
@ -165,21 +175,23 @@ public class ExtensionSvc implements ExtensionService {
// Try again
updatePlayerValues(gatherer, playerUUID, playerName, event);
} catch (Exception | NoClassDefFoundError | NoSuchFieldError | NoSuchMethodError unexpectedError) {
logger.warn("Encountered unexpected error with " + gatherer.getPluginName() + " Extension: " + unexpectedError +
" (but failed safely) when updating value for '" + playerName +
"', stack trace to follow (please report this):");
errorLogger.log(L.WARN, gatherer.getClass(), unexpectedError);
ErrorContext.Builder context = ErrorContext.builder()
.whatToDo("Report and/or disable " + gatherer.getPluginName() + " extension in the Plan config.")
.related(gatherer.getPluginName())
.related(event)
.related("Player: " + playerName + " " + playerUUID);
errorLogger.log(L.WARN, unexpectedError, context.build());
}
}
private void logFailure(String playerName, DataExtensionMethodCallException methodCallFailed) {
Throwable cause = methodCallFailed.getCause();
String causeName = cause.getClass().getSimpleName();
logger.warn("Encountered " + causeName + " with " + methodCallFailed.getPluginName() + " Extension" +
" (failed safely) when updating value for '" + playerName +
"', the method was disabled temporarily (won't be called until next Plan reload)" +
", stack trace to follow (please report this):");
errorLogger.log(L.WARN, getClass(), cause);
ErrorContext.Builder context = ErrorContext.builder()
.whatToDo("Report and/or disable " + methodCallFailed.getPluginName() + " extension in the Plan config.")
.related(methodCallFailed.getPluginName())
.related("Method:" + methodCallFailed.getMethod().map(MethodWrapper::getMethodName).orElse("-"))
.related("Player: " + playerName);
errorLogger.log(L.WARN, cause, context.build());
}
public void updateServerValues(CallEvents event) {
@ -206,9 +218,12 @@ public class ExtensionSvc implements ExtensionService {
// Try again
updateServerValues(gatherer, event);
} catch (Exception | NoClassDefFoundError | NoSuchFieldError | NoSuchMethodError unexpectedError) {
logger.warn("Encountered unexpected error with " + gatherer.getPluginName() + " Extension: " + unexpectedError +
" (failed safely) when updating value for server, stack trace to follow (please report this):");
errorLogger.log(L.WARN, gatherer.getClass(), unexpectedError);
ErrorContext.Builder context = ErrorContext.builder()
.whatToDo("Report and/or disable " + gatherer.getPluginName() + " extension in the Plan config.")
.related(gatherer.getPluginName())
.related(event)
.related("Gathering for server");
errorLogger.log(L.WARN, unexpectedError, context.build());
}
}
}

View File

@ -25,6 +25,7 @@ import com.djrapitops.plan.settings.locale.lang.PluginLang;
import com.djrapitops.plan.storage.database.DBSystem;
import com.djrapitops.plan.storage.database.Database;
import com.djrapitops.plan.storage.database.transactions.events.ServerShutdownTransaction;
import com.djrapitops.plan.utilities.logging.ErrorContext;
import com.djrapitops.plan.utilities.logging.ErrorLogger;
import com.djrapitops.plugin.logging.L;
import com.djrapitops.plugin.logging.console.PluginLogger;
@ -90,7 +91,11 @@ public abstract class ServerShutdownSave {
prepareSessionsForStorage(activeSessions, System.currentTimeMillis());
return Optional.of(saveActiveSessions(activeSessions));
} catch (DBInitException e) {
errorLogger.log(L.ERROR, this.getClass(), e);
errorLogger.log(L.ERROR, e, ErrorContext.builder()
.whatToDo("Find the sessions in the error file and save them manually or ignore. Report & delete the error file after.")
.related("Shutdown save failed to init database.")
.related(activeSessions)
.build());
return Optional.empty();
} catch (IllegalStateException ignored) {
/* Database is not initialized */

View File

@ -99,7 +99,7 @@ public class NicknameCache implements SubSystem {
NicknameQueries.fetchLastSeenNicknameOfPlayer(uuid, serverInfo.getServerUUID())
).map(Nickname::getName);
} catch (DBOpException e) {
errorLogger.log(L.ERROR, this.getClass(), e);
errorLogger.log(L.ERROR, e);
}
return Optional.empty();
}

View File

@ -19,6 +19,7 @@ package com.djrapitops.plan.gathering.timed;
import com.djrapitops.plan.gathering.SystemUsage;
import com.djrapitops.plan.settings.config.PlanConfig;
import com.djrapitops.plan.settings.config.paths.DataGatheringSettings;
import com.djrapitops.plan.utilities.logging.ErrorContext;
import com.djrapitops.plan.utilities.logging.ErrorLogger;
import com.djrapitops.plugin.logging.L;
import com.djrapitops.plugin.logging.console.PluginLogger;
@ -111,7 +112,8 @@ public class SystemUsageBuffer {
buffer.freeDiskSpace = SystemUsage.getFreeDiskSpace();
} catch (SecurityException noPermission) {
if (!diskErrored) {
errorLogger.log(L.WARN, this.getClass(), noPermission);
errorLogger.log(L.WARN, noPermission, ErrorContext.builder()
.whatToDo("Resolve " + noPermission.getMessage() + " via OS or JVM permissions").build());
}
diskErrored = true;
} catch (Exception e) {

View File

@ -16,6 +16,7 @@
*/
package com.djrapitops.plan.gathering.timed;
import com.djrapitops.plan.utilities.logging.ErrorContext;
import com.djrapitops.plan.utilities.logging.ErrorLogger;
import com.djrapitops.plugin.logging.L;
import com.djrapitops.plugin.logging.console.PluginLogger;
@ -45,7 +46,7 @@ public abstract class TPSCounter extends AbsRunnable {
pulse();
} catch (Exception | NoClassDefFoundError | NoSuchMethodError | NoSuchFieldError e) {
logger.error("TPS Count Task Disabled due to error, reload Plan to re-enable.");
errorLogger.log(L.ERROR, this.getClass(), e);
errorLogger.log(L.ERROR, e, ErrorContext.builder().whatToDo("See if a restart fixes this or Report this").build());
cancel();
}
}

View File

@ -27,6 +27,7 @@ import com.djrapitops.plan.storage.database.DBSystem;
import com.djrapitops.plan.storage.database.Database;
import com.djrapitops.plan.storage.database.queries.objects.ServerQueries;
import com.djrapitops.plan.storage.database.transactions.StoreServerInformationTransaction;
import com.djrapitops.plan.utilities.logging.ErrorContext;
import com.djrapitops.plan.utilities.logging.ErrorLogger;
import com.djrapitops.plugin.logging.L;
@ -113,8 +114,11 @@ public class ServerServerInfo extends ServerInfo {
if (!foundServer.isPresent()) {
try {
server = registerServer(serverUUID);
} catch (ExecutionException | IOException e) {
errorLogger.log(L.CRITICAL, this.getClass(), e);
} catch (IOException e) {
errorLogger.log(L.CRITICAL, e, ErrorContext.builder()
.whatToDo("Grant access permissions for writing to " + serverInfoFile.getConfigFilePath()).build());
} catch (ExecutionException e) {
errorLogger.log(L.CRITICAL, e, ErrorContext.builder().build());
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}

View File

@ -92,7 +92,7 @@ public class UUIDUtility {
try {
return dbSystem.getDatabase().query(UserIdentifierQueries.fetchPlayerUUIDOf(playerName));
} catch (DBOpException e) {
errorLogger.log(L.ERROR, UUIDUtility.class, e);
errorLogger.log(L.ERROR, e);
return Optional.empty();
}
}

View File

@ -19,6 +19,7 @@ package com.djrapitops.plan.processing;
import com.djrapitops.plan.SubSystem;
import com.djrapitops.plan.settings.locale.Locale;
import com.djrapitops.plan.settings.locale.lang.PluginLang;
import com.djrapitops.plan.utilities.logging.ErrorContext;
import com.djrapitops.plan.utilities.logging.ErrorLogger;
import com.djrapitops.plugin.logging.L;
import com.djrapitops.plugin.logging.console.PluginLogger;
@ -58,7 +59,7 @@ public class Processing implements SubSystem {
new BasicThreadFactory.Builder()
.namingPattern(s)
.uncaughtExceptionHandler((thread, throwable) ->
errorLogger.log(L.WARN, Processing.class, throwable)
errorLogger.log(L.WARN, throwable, ErrorContext.builder().build())
).build());
}
@ -110,14 +111,14 @@ public class Processing implements SubSystem {
private <T> T exceptionHandlerNonCritical(T t, Throwable throwable) {
if (throwable != null) {
errorLogger.log(L.WARN, Processing.class, throwable.getCause());
errorLogger.log(L.WARN, throwable.getCause(), ErrorContext.builder().build());
}
return t;
}
private <T> T exceptionHandlerCritical(T t, Throwable throwable) {
if (throwable != null) {
errorLogger.log(L.ERROR, Processing.class, throwable.getCause());
errorLogger.log(L.ERROR, throwable.getCause(), ErrorContext.builder().build());
}
return t;
}
@ -163,7 +164,7 @@ public class Processing implements SubSystem {
try {
runnable.run();
} catch (Exception | NoClassDefFoundError | NoSuchMethodError | NoSuchFieldError e) {
errorLogger.log(L.WARN, this.getClass(), e);
errorLogger.log(L.WARN, e, ErrorContext.builder().build());
}
}
}

View File

@ -23,6 +23,7 @@ import com.djrapitops.plan.storage.database.Database;
import com.djrapitops.plan.storage.database.queries.QueryAPIExecutable;
import com.djrapitops.plan.storage.database.queries.QueryAPIQuery;
import com.djrapitops.plan.storage.database.transactions.Transaction;
import com.djrapitops.plan.utilities.logging.ErrorContext;
import com.djrapitops.plan.utilities.logging.ErrorLogger;
import com.djrapitops.plugin.logging.L;
import com.djrapitops.plugin.logging.console.PluginLogger;
@ -107,8 +108,9 @@ public class QuerySvc implements QueryService {
try {
subscriber.accept(playerUUID);
} catch (DBOpException e) {
logger.warn("User of Query API (" + subscriber.getClass().getName() + ") ran into exception, failed safely:");
errorLogger.log(L.WARN, QueryService.class, e);
errorLogger.log(L.WARN, e, ErrorContext.builder()
.whatToDo("Report to this Query API user " + subscriber.getClass().getName())
.related("Subscriber: " + subscriber.getClass().getName()).build());
}
});
}
@ -118,8 +120,9 @@ public class QuerySvc implements QueryService {
try {
function.apply();
} catch (DBOpException e) {
logger.warn("User of Query API (" + function.getClass().getName() + ") ran into exception, failed safely:");
errorLogger.log(L.WARN, QueryService.class, e);
errorLogger.log(L.WARN, e, ErrorContext.builder()
.whatToDo("Report to this Query API user " + function.getClass().getName())
.related("Subscriber: " + function.getClass().getName()).build());
}
});
}

View File

@ -24,6 +24,7 @@ import com.djrapitops.plan.settings.config.paths.FormatSettings;
import com.djrapitops.plan.settings.config.paths.PluginSettings;
import com.djrapitops.plan.settings.theme.Theme;
import com.djrapitops.plan.storage.file.PlanFiles;
import com.djrapitops.plan.utilities.logging.ErrorContext;
import com.djrapitops.plan.utilities.logging.ErrorLogger;
import com.djrapitops.plugin.logging.L;
import com.djrapitops.plugin.logging.console.PluginLogger;
@ -86,7 +87,7 @@ public abstract class ConfigSystem implements SubSystem {
checkWrongTimeZone();
} catch (IOException e) {
errorLogger.log(L.ERROR, this.getClass(), e);
errorLogger.log(L.ERROR, e, ErrorContext.builder().whatToDo("Fix write permissions to " + config.getConfigFilePath()).build());
throw new EnableException("Failed to save default config: " + e.getMessage(), e);
}
theme.enable();
@ -134,7 +135,7 @@ public abstract class ConfigSystem implements SubSystem {
try {
config.read();
} catch (IOException e) {
errorLogger.log(L.ERROR, this.getClass(), e);
errorLogger.log(L.ERROR, e, ErrorContext.builder().whatToDo("Fix read permissions to " + config.getConfigFilePath()).build());
}
}
}

View File

@ -18,6 +18,7 @@ package com.djrapitops.plan.settings;
import com.djrapitops.plan.settings.config.ConfigNode;
import com.djrapitops.plan.settings.config.PlanConfig;
import com.djrapitops.plan.utilities.logging.ErrorContext;
import com.djrapitops.plan.utilities.logging.ErrorLogger;
import com.djrapitops.plugin.logging.L;
@ -70,7 +71,7 @@ public class SettingsSvc implements SettingsService {
try {
config.save();
} catch (IOException e) {
errorLogger.log(L.ERROR, this.getClass(), e);
errorLogger.log(L.ERROR, e, ErrorContext.builder().whatToDo("Fix write permissions to " + config.getConfigFilePath()).build());
}
}

View File

@ -98,4 +98,8 @@ public class Config extends ConfigNode {
public int hashCode() {
return Objects.hash(super.hashCode(), configFilePath);
}
public Path getConfigFilePath() {
return configFilePath;
}
}

View File

@ -27,6 +27,7 @@ import com.djrapitops.plan.settings.config.paths.DisplaySettings;
import com.djrapitops.plan.settings.locale.Locale;
import com.djrapitops.plan.settings.locale.lang.GenericLang;
import com.djrapitops.plan.settings.locale.lang.HtmlLang;
import com.djrapitops.plan.utilities.logging.ErrorContext;
import com.djrapitops.plan.utilities.logging.ErrorLogger;
import com.djrapitops.plugin.logging.L;
import com.djrapitops.plugin.utilities.Verify;
@ -93,7 +94,7 @@ public class WorldAliasSettings {
try {
aliasSect.save();
} catch (IOException e) {
errorLogger.log(L.WARN, WorldAliasSettings.class, e);
errorLogger.log(L.ERROR, e, ErrorContext.builder().whatToDo("Fix write permissions to " + config.get().getConfigFilePath()).build());
}
});
}

View File

@ -18,6 +18,7 @@ package com.djrapitops.plan.settings.config.changes;
import com.djrapitops.plan.settings.config.Config;
import com.djrapitops.plan.settings.config.paths.FormatSettings;
import com.djrapitops.plan.utilities.logging.ErrorContext;
import com.djrapitops.plan.utilities.logging.ErrorLogger;
import com.djrapitops.plugin.logging.L;
import com.djrapitops.plugin.logging.console.PluginLogger;
@ -152,7 +153,9 @@ public class ConfigUpdater {
logger.info("Config: " + change.getAppliedMessage());
}
} catch (Exception e) {
errorLogger.log(L.WARN, this.getClass(), new IllegalStateException("Failed to apply config update: '" + change.getAppliedMessage() + "'", e));
errorLogger.log(L.ERROR, e, ErrorContext.builder()
.whatToDo("Fix write permissions to " + config.getConfigFilePath() + " or Report this")
.related("Attempt to change: " + change.getAppliedMessage()).build());
}
}
}

View File

@ -22,6 +22,7 @@ import com.djrapitops.plan.settings.config.PlanConfig;
import com.djrapitops.plan.settings.config.paths.PluginSettings;
import com.djrapitops.plan.settings.locale.lang.*;
import com.djrapitops.plan.storage.file.PlanFiles;
import com.djrapitops.plan.utilities.logging.ErrorContext;
import com.djrapitops.plan.utilities.logging.ErrorLogger;
import com.djrapitops.plugin.logging.L;
import com.djrapitops.plugin.logging.console.PluginLogger;
@ -112,8 +113,7 @@ public class LocaleSystem implements SubSystem {
}
new LocaleFileWriter(writing).writeToFile(localeFile);
} catch (IOException | IllegalStateException e) {
logger.error("Failed to write new Locale file at " + localeFile.getAbsolutePath());
errorLogger.log(L.WARN, this.getClass(), e);
errorLogger.log(L.ERROR, e, ErrorContext.builder().whatToDo("Fix write permissions to " + localeFile.getAbsolutePath()).build());
}
resetWriteConfigSetting();
}
@ -123,8 +123,7 @@ public class LocaleSystem implements SubSystem {
config.set(PluginSettings.WRITE_NEW_LOCALE, false);
config.save();
} catch (IOException | IllegalStateException e) {
logger.error("Failed set WriteNewLocaleFileOnEnable back to false");
errorLogger.log(L.WARN, this.getClass(), e);
errorLogger.log(L.ERROR, e, ErrorContext.builder().whatToDo("Fix write permissions to " + config.getConfigFilePath()).build());
}
}

View File

@ -16,6 +16,7 @@
*/
package com.djrapitops.plan.settings.upkeep;
import com.djrapitops.plan.utilities.logging.ErrorContext;
import com.djrapitops.plan.utilities.logging.ErrorLogger;
import com.djrapitops.plugin.logging.L;
import com.djrapitops.plugin.utilities.Verify;
@ -75,7 +76,7 @@ public class FileWatcher extends Thread {
watchedPath.register(watcher, ENTRY_MODIFY);
runLoop(watcher);
} catch (IOException e) {
errorLogger.log(L.ERROR, this.getClass(), e);
errorLogger.log(L.ERROR, e, ErrorContext.builder().build());
interrupt();
} catch (InterruptedException e) {
interrupt();

View File

@ -23,6 +23,7 @@ import com.djrapitops.plan.settings.config.PlanConfig;
import com.djrapitops.plan.settings.config.paths.DatabaseSettings;
import com.djrapitops.plan.settings.locale.Locale;
import com.djrapitops.plan.settings.locale.lang.PluginLang;
import com.djrapitops.plan.utilities.logging.ErrorContext;
import com.djrapitops.plan.utilities.logging.ErrorLogger;
import com.djrapitops.plugin.logging.L;
import com.djrapitops.plugin.logging.console.PluginLogger;
@ -76,7 +77,7 @@ public class MySQLDB extends SQLDB {
try {
Class.forName("com.mysql.cj.jdbc.Driver");
} catch (ClassNotFoundException e) {
errorLogger.log(L.CRITICAL, this.getClass(), e);
errorLogger.log(L.CRITICAL, e, ErrorContext.builder().whatToDo("Install MySQL Driver to the server").build());
}
}
@ -164,7 +165,7 @@ public class MySQLDB extends SQLDB {
connection.close();
}
} catch (SQLException e) {
errorLogger.log(L.ERROR, this.getClass(), e);
errorLogger.log(L.CRITICAL, e, ErrorContext.builder().related("Closing connection").build());
}
}

View File

@ -31,6 +31,7 @@ import com.djrapitops.plan.storage.database.transactions.init.CreateTablesTransa
import com.djrapitops.plan.storage.database.transactions.init.OperationCriticalTransaction;
import com.djrapitops.plan.storage.database.transactions.patches.*;
import com.djrapitops.plan.utilities.java.ThrowableUtils;
import com.djrapitops.plan.utilities.logging.ErrorContext;
import com.djrapitops.plan.utilities.logging.ErrorLogger;
import com.djrapitops.plugin.api.TimeAmount;
import com.djrapitops.plugin.logging.L;
@ -92,7 +93,9 @@ public abstract class SQLDB extends AbstractDatabase {
.namingPattern(nameFormat)
.uncaughtExceptionHandler((thread, throwable) -> {
if (devMode) {
errorLogger.log(L.WARN, getClass(), throwable);
errorLogger.log(L.WARN, throwable, ErrorContext.builder()
.whatToDo("THIS ERROR IS ONLY LOGGED IN DEV MODE")
.build());
}
}).build());
};
@ -248,10 +251,10 @@ public abstract class SQLDB extends AbstractDatabase {
}
transaction.executeTransaction(this);
return CompletableFuture.completedFuture(null);
}, getTransactionExecutor()).handle(errorHandler(origin));
}, getTransactionExecutor()).handle(errorHandler(transaction, origin));
}
private BiFunction<CompletableFuture<Object>, Throwable, CompletableFuture<Object>> errorHandler(Exception origin) {
private BiFunction<CompletableFuture<Object>, Throwable, CompletableFuture<Object>> errorHandler(Transaction transaction, Exception origin) {
return (obj, throwable) -> {
if (throwable == null) {
return CompletableFuture.completedFuture(null);
@ -261,7 +264,8 @@ public abstract class SQLDB extends AbstractDatabase {
}
ThrowableUtils.appendEntryPointToCause(throwable, origin);
errorLogger.log(L.ERROR, getClass(), throwable);
errorLogger.log(L.ERROR, throwable, ErrorContext.builder()
.related("Transaction: " + transaction.getClass()).build());
return CompletableFuture.completedFuture(null);
};
}

View File

@ -25,6 +25,7 @@ import com.djrapitops.plan.storage.file.PlanFiles;
import com.djrapitops.plan.storage.upkeep.DBKeepAliveTask;
import com.djrapitops.plan.utilities.MiscUtils;
import com.djrapitops.plan.utilities.java.ThrowableUtils;
import com.djrapitops.plan.utilities.logging.ErrorContext;
import com.djrapitops.plan.utilities.logging.ErrorLogger;
import com.djrapitops.plugin.logging.L;
import com.djrapitops.plugin.logging.console.PluginLogger;
@ -80,8 +81,8 @@ public class SQLiteDB extends SQLDB {
try {
Class.forName("org.sqlite.JDBC");
} catch (ClassNotFoundException e) {
errorLogger.log(L.CRITICAL, this.getClass(), e);
return null; // Should never happen.
errorLogger.log(L.CRITICAL, e, ErrorContext.builder().whatToDo("Install SQLite Driver to the server").build());
return null;
}
String dbFilePath = dbFile.getAbsolutePath();

View File

@ -115,7 +115,7 @@ public class DBCleanTask extends AbsRunnable {
}
}
} catch (DBOpException e) {
errorLogger.log(L.ERROR, this.getClass(), e);
errorLogger.log(L.ERROR, e);
cancel();
}
}

View File

@ -17,6 +17,7 @@
package com.djrapitops.plan.storage.upkeep;
import com.djrapitops.plan.utilities.MiscUtils;
import com.djrapitops.plan.utilities.logging.ErrorContext;
import com.djrapitops.plan.utilities.logging.ErrorLogger;
import com.djrapitops.plugin.logging.L;
import com.djrapitops.plugin.logging.console.PluginLogger;
@ -59,7 +60,8 @@ public class DBKeepAliveTask extends AbsRunnable {
try {
connection = iReconnect.reconnect();
} catch (SQLException reconnectionError) {
errorLogger.log(L.ERROR, this.getClass(), reconnectionError);
errorLogger.log(L.ERROR, reconnectionError, ErrorContext.builder()
.whatToDo("Reload Plan and Report this if the issue persists").build());
logger.error("SQL connection maintaining task had to be closed due to exception.");
this.cancel();
}

View File

@ -42,12 +42,17 @@ public class ErrorContext {
public Collection<String> toLines() {
List<String> lines = new ArrayList<>();
getWhatToDo().ifPresent(lines::add);
for (Object o : related) {
lines.add(Objects.toString(o));
}
return lines;
}
public void merge(ErrorContext context) {
this.related.addAll(context.related);
}
public static class Builder {
private final ErrorContext context;

View File

@ -18,6 +18,7 @@ package com.djrapitops.plan.utilities.logging;
import com.djrapitops.plan.PlanPlugin;
import com.djrapitops.plan.delivery.formatting.Formatters;
import com.djrapitops.plan.exceptions.ExceptionWithContext;
import com.djrapitops.plan.identification.properties.ServerProperties;
import com.djrapitops.plan.storage.file.PlanFiles;
import com.djrapitops.plan.utilities.java.Lists;
@ -76,11 +77,16 @@ public class ErrorLogger implements ErrorHandler {
this.formatters = formatters;
}
public <T extends ExceptionWithContext> void log(L level, T throwable) {
log(level, (Throwable) throwable, throwable.getContext().orElse(ErrorContext.builder().related("Missing Context").build()));
}
public void log(L level, Throwable throwable, ErrorContext context) {
String errorName = throwable.getClass().getSimpleName();
String hash = hash(throwable);
Path logsDir = files.getLogsDirectory();
Path errorLog = logsDir.resolve(errorName + "-" + hash + ".txt");
mergeAdditionalContext(throwable, context);
if (Files.exists(errorLog)) {
logExisting(errorLog, throwable, context);
} else {
@ -93,6 +99,16 @@ public class ErrorLogger implements ErrorHandler {
}
}
public void mergeAdditionalContext(Throwable throwable, ErrorContext context) {
Throwable cause = throwable.getCause();
while (cause != null) {
if (cause instanceof ExceptionWithContext) {
((ExceptionWithContext) cause).getContext().ifPresent(context::merge);
}
cause = cause.getCause();
}
}
private void logExisting(Path errorLog, Throwable throwable, ErrorContext context) {
// Read existing
List<String> lines;
@ -232,6 +248,13 @@ public class ErrorLogger implements ErrorHandler {
for (StackTraceElement element : e.getStackTrace()) {
trace.add(" " + element);
}
Throwable[] suppressed = e.getSuppressed();
if (suppressed.length > 0) {
for (Throwable suppressedThrowable : suppressed) {
trace.add(" Suppressed:");
buildReadableStacktrace(suppressedThrowable).stream().map(line -> " " + line).forEach(trace::add);
}
}
Throwable cause = e.getCause();
if (cause != null) {
trace.add("Caused by:");

View File

@ -24,12 +24,12 @@ import com.djrapitops.plan.delivery.domain.keys.PlayerKeys;
import com.djrapitops.plan.gathering.cache.SessionCache;
import com.djrapitops.plan.placeholder.PlanPlaceholders;
import com.djrapitops.plan.storage.database.queries.containers.ContainerFetchQueries;
import com.djrapitops.plan.utilities.logging.ErrorContext;
import com.djrapitops.plan.utilities.logging.ErrorLogger;
import com.djrapitops.plugin.logging.L;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.util.ArrayList;
import java.util.UUID;
/**
@ -60,9 +60,9 @@ public class NukkitPlaceholderRegistrar {
placeholders.getPlaceholders().forEach((name, loader) ->
api.visitorSensitivePlaceholder(name, (player, params) -> {
try {
return loader.apply(getPlayer(player), new ArrayList<>(params.getAll().values()));
return loader.apply(getPlayer(player), params.get());
} catch (Exception e) {
errorLogger.log(L.WARN, getClass(), e);
errorLogger.log(L.WARN, e, ErrorContext.builder().related("Registering PlaceholderAPI").build());
return null;
}
}
@ -71,9 +71,9 @@ public class NukkitPlaceholderRegistrar {
placeholders.getStaticPlaceholders().forEach((name, loader) ->
api.staticPlaceholder(name, params -> {
try {
return loader.apply(new ArrayList<>(params.getAll().values()));
return loader.apply(params.get());
} catch (Exception e) {
errorLogger.log(L.WARN, getClass(), e);
errorLogger.log(L.WARN, e, ErrorContext.builder().related("Registering PlaceholderAPI").build());
return null;
}
}

View File

@ -26,6 +26,7 @@ import com.djrapitops.plan.gathering.cache.NicknameCache;
import com.djrapitops.plan.identification.ServerInfo;
import com.djrapitops.plan.storage.database.DBSystem;
import com.djrapitops.plan.storage.database.transactions.events.NicknameStoreTransaction;
import com.djrapitops.plan.utilities.logging.ErrorContext;
import com.djrapitops.plan.utilities.logging.ErrorLogger;
import com.djrapitops.plugin.logging.L;
@ -66,7 +67,7 @@ public class ChatListener implements Listener {
try {
actOnChatEvent(event);
} catch (Exception e) {
errorLogger.log(L.ERROR, this.getClass(), e);
errorLogger.log(L.ERROR, e, ErrorContext.builder().related(event).build());
}
}

View File

@ -35,6 +35,7 @@ import com.djrapitops.plan.gathering.domain.Session;
import com.djrapitops.plan.processing.Processing;
import com.djrapitops.plan.processing.processors.player.MobKillProcessor;
import com.djrapitops.plan.processing.processors.player.PlayerKillProcessor;
import com.djrapitops.plan.utilities.logging.ErrorContext;
import com.djrapitops.plan.utilities.logging.ErrorLogger;
import com.djrapitops.plugin.logging.L;
@ -78,7 +79,7 @@ public class DeathEventListener implements Listener {
UUID uuid = dead.getUniqueId();
handleKill(time, uuid, killerEntity);
} catch (Exception e) {
errorLogger.log(L.ERROR, this.getClass(), e);
errorLogger.log(L.ERROR, e, ErrorContext.builder().related(event, dead).build());
}
}
@ -98,7 +99,7 @@ public class DeathEventListener implements Listener {
handleKill(time, /* Not a player */ null, killerEntity);
} catch (Exception e) {
errorLogger.log(L.ERROR, this.getClass(), e);
errorLogger.log(L.ERROR, e, ErrorContext.builder().related(event, dead).build());
}
}

View File

@ -28,6 +28,7 @@ import com.djrapitops.plan.identification.ServerInfo;
import com.djrapitops.plan.settings.config.WorldAliasSettings;
import com.djrapitops.plan.storage.database.DBSystem;
import com.djrapitops.plan.storage.database.transactions.events.WorldNameStoreTransaction;
import com.djrapitops.plan.utilities.logging.ErrorContext;
import com.djrapitops.plan.utilities.logging.ErrorLogger;
import com.djrapitops.plugin.logging.L;
@ -68,7 +69,7 @@ public class GameModeChangeListener implements Listener {
try {
actOnEvent(event);
} catch (Exception e) {
errorLogger.log(L.ERROR, this.getClass(), e);
errorLogger.log(L.ERROR, e, ErrorContext.builder().related(event, event.getPlayer().getGamemode() + "->" + event.getNewGamemode()).build());
}
}

View File

@ -24,6 +24,7 @@ import cn.nukkit.event.player.*;
import com.djrapitops.plan.gathering.afk.AFKTracker;
import com.djrapitops.plan.settings.Permissions;
import com.djrapitops.plan.settings.config.PlanConfig;
import com.djrapitops.plan.utilities.logging.ErrorContext;
import com.djrapitops.plan.utilities.logging.ErrorLogger;
import com.djrapitops.plugin.logging.L;
@ -79,7 +80,7 @@ public class NukkitAFKListener implements Listener {
AFK_TRACKER.performedAction(uuid, time);
} catch (Exception e) {
errorLogger.log(L.ERROR, this.getClass(), e);
errorLogger.log(L.ERROR, e, ErrorContext.builder().related(event).build());
}
}

View File

@ -45,6 +45,7 @@ import com.djrapitops.plan.settings.config.paths.ExportSettings;
import com.djrapitops.plan.storage.database.DBSystem;
import com.djrapitops.plan.storage.database.Database;
import com.djrapitops.plan.storage.database.transactions.events.*;
import com.djrapitops.plan.utilities.logging.ErrorContext;
import com.djrapitops.plan.utilities.logging.ErrorLogger;
import com.djrapitops.plugin.logging.L;
@ -105,7 +106,7 @@ public class PlayerOnlineListener implements Listener {
boolean operator = event.getPlayer().isOp();
dbSystem.getDatabase().executeTransaction(new OperatorStatusTransaction(playerUUID, operator));
} catch (Exception e) {
errorLogger.log(L.ERROR, this.getClass(), e);
errorLogger.log(L.ERROR, e, ErrorContext.builder().related(event).build());
}
}
@ -130,7 +131,7 @@ public class PlayerOnlineListener implements Listener {
dbSystem.getDatabase().executeTransaction(new KickStoreTransaction(uuid));
} catch (Exception e) {
errorLogger.log(L.ERROR, this.getClass(), e);
errorLogger.log(L.ERROR, e, ErrorContext.builder().related(event).build());
}
}
@ -139,7 +140,7 @@ public class PlayerOnlineListener implements Listener {
try {
actOnJoinEvent(event);
} catch (Exception e) {
errorLogger.log(L.ERROR, this.getClass(), e);
errorLogger.log(L.ERROR, e, ErrorContext.builder().related(event).build());
}
}
@ -204,7 +205,7 @@ public class PlayerOnlineListener implements Listener {
try {
actOnQuitEvent(event);
} catch (Exception e) {
errorLogger.log(L.ERROR, this.getClass(), e);
errorLogger.log(L.ERROR, e, ErrorContext.builder().related(event).build());
}
}

View File

@ -28,6 +28,7 @@ import com.djrapitops.plan.identification.ServerInfo;
import com.djrapitops.plan.settings.config.WorldAliasSettings;
import com.djrapitops.plan.storage.database.DBSystem;
import com.djrapitops.plan.storage.database.transactions.events.WorldNameStoreTransaction;
import com.djrapitops.plan.utilities.logging.ErrorContext;
import com.djrapitops.plan.utilities.logging.ErrorLogger;
import com.djrapitops.plugin.logging.L;
@ -61,7 +62,7 @@ public class WorldChangeListener implements Listener {
try {
actOnEvent(event);
} catch (Exception e) {
errorLogger.log(L.ERROR, this.getClass(), e);
errorLogger.log(L.ERROR, e, ErrorContext.builder().related(event).build());
}
}
}

View File

@ -36,6 +36,7 @@ import com.djrapitops.plan.settings.config.paths.ExportSettings;
import com.djrapitops.plan.storage.database.DBSystem;
import com.djrapitops.plan.storage.database.Database;
import com.djrapitops.plan.storage.database.transactions.events.*;
import com.djrapitops.plan.utilities.logging.ErrorContext;
import com.djrapitops.plan.utilities.logging.ErrorLogger;
import com.djrapitops.plugin.logging.L;
import org.spongepowered.api.Sponge;
@ -105,7 +106,7 @@ public class PlayerOnlineListener {
try {
actOnLoginEvent(event);
} catch (Exception e) {
errorLogger.log(L.ERROR, this.getClass(), e);
errorLogger.log(L.ERROR, e, ErrorContext.builder().related(event).build());
}
}
@ -125,7 +126,7 @@ public class PlayerOnlineListener {
}
dbSystem.getDatabase().executeTransaction(new KickStoreTransaction(playerUUID));
} catch (Exception e) {
errorLogger.log(L.ERROR, this.getClass(), e);
errorLogger.log(L.ERROR, e, ErrorContext.builder().related(event).build());
}
}
@ -143,7 +144,7 @@ public class PlayerOnlineListener {
try {
actOnJoinEvent(event);
} catch (Exception e) {
errorLogger.log(L.ERROR, this.getClass(), e);
errorLogger.log(L.ERROR, e, ErrorContext.builder().related(event).build());
}
}
@ -208,7 +209,7 @@ public class PlayerOnlineListener {
try {
actOnQuitEvent(event);
} catch (Exception e) {
errorLogger.log(L.ERROR, this.getClass(), e);
errorLogger.log(L.ERROR, e, ErrorContext.builder().related(event).build());
}
}

View File

@ -19,6 +19,7 @@ package com.djrapitops.plan.gathering.listeners.sponge;
import com.djrapitops.plan.gathering.afk.AFKTracker;
import com.djrapitops.plan.settings.Permissions;
import com.djrapitops.plan.settings.config.PlanConfig;
import com.djrapitops.plan.utilities.logging.ErrorContext;
import com.djrapitops.plan.utilities.logging.ErrorLogger;
import com.djrapitops.plugin.logging.L;
import org.spongepowered.api.entity.living.player.Player;
@ -71,7 +72,7 @@ public class SpongeAFKListener {
try {
performedAction(event.getTargetEntity());
} catch (Exception e) {
errorLogger.log(L.ERROR, this.getClass(), e);
errorLogger.log(L.ERROR, e, ErrorContext.builder().related(event).build());
}
}

View File

@ -21,6 +21,7 @@ import com.djrapitops.plan.gathering.cache.NicknameCache;
import com.djrapitops.plan.identification.ServerInfo;
import com.djrapitops.plan.storage.database.DBSystem;
import com.djrapitops.plan.storage.database.transactions.events.NicknameStoreTransaction;
import com.djrapitops.plan.utilities.logging.ErrorContext;
import com.djrapitops.plan.utilities.logging.ErrorLogger;
import com.djrapitops.plugin.logging.L;
import org.spongepowered.api.entity.living.player.Player;
@ -66,7 +67,7 @@ public class SpongeChatListener {
try {
actOnChatEvent(player);
} catch (Exception e) {
errorLogger.log(L.ERROR, this.getClass(), e);
errorLogger.log(L.ERROR, e, ErrorContext.builder().related(event).build());
}
}

View File

@ -23,6 +23,7 @@ import com.djrapitops.plan.gathering.domain.Session;
import com.djrapitops.plan.processing.Processing;
import com.djrapitops.plan.processing.processors.player.MobKillProcessor;
import com.djrapitops.plan.processing.processors.player.PlayerKillProcessor;
import com.djrapitops.plan.utilities.logging.ErrorContext;
import com.djrapitops.plan.utilities.logging.ErrorLogger;
import com.djrapitops.plugin.logging.L;
import org.spongepowered.api.data.key.Keys;
@ -81,7 +82,7 @@ public class SpongeDeathListener {
handleKill(time, dead, killerEntity);
}
} catch (Exception e) {
errorLogger.log(L.ERROR, this.getClass(), e);
errorLogger.log(L.ERROR, e, ErrorContext.builder().related(event, dead).build());
}
}

View File

@ -22,6 +22,7 @@ import com.djrapitops.plan.identification.ServerInfo;
import com.djrapitops.plan.settings.config.WorldAliasSettings;
import com.djrapitops.plan.storage.database.DBSystem;
import com.djrapitops.plan.storage.database.transactions.events.WorldNameStoreTransaction;
import com.djrapitops.plan.utilities.logging.ErrorContext;
import com.djrapitops.plan.utilities.logging.ErrorLogger;
import com.djrapitops.plugin.logging.L;
import org.spongepowered.api.entity.living.player.Player;
@ -67,7 +68,7 @@ public class SpongeGMChangeListener {
try {
actOnGMChangeEvent(event);
} catch (Exception e) {
errorLogger.log(L.ERROR, this.getClass(), e);
errorLogger.log(L.ERROR, e, ErrorContext.builder().related(event, event.getGameMode()).build());
}
}

View File

@ -22,6 +22,7 @@ import com.djrapitops.plan.identification.ServerInfo;
import com.djrapitops.plan.settings.config.WorldAliasSettings;
import com.djrapitops.plan.storage.database.DBSystem;
import com.djrapitops.plan.storage.database.transactions.events.WorldNameStoreTransaction;
import com.djrapitops.plan.utilities.logging.ErrorContext;
import com.djrapitops.plan.utilities.logging.ErrorLogger;
import com.djrapitops.plugin.logging.L;
import org.spongepowered.api.data.key.Keys;
@ -70,7 +71,7 @@ public class SpongeWorldChangeListener {
try {
actOnEvent(event, player);
} catch (Exception e) {
errorLogger.log(L.ERROR, this.getClass(), e);
errorLogger.log(L.ERROR, e, ErrorContext.builder().related(event).build());
}
}

View File

@ -34,6 +34,7 @@ import com.djrapitops.plan.storage.database.DBSystem;
import com.djrapitops.plan.storage.database.Database;
import com.djrapitops.plan.storage.database.transactions.events.GeoInfoStoreTransaction;
import com.djrapitops.plan.storage.database.transactions.events.PlayerRegisterTransaction;
import com.djrapitops.plan.utilities.logging.ErrorContext;
import com.djrapitops.plan.utilities.logging.ErrorLogger;
import com.djrapitops.plugin.logging.L;
import com.velocitypowered.api.event.PostOrder;
@ -96,7 +97,7 @@ public class PlayerOnlineListener {
try {
actOnLogin(event);
} catch (Exception e) {
errorLogger.log(L.WARN, this.getClass(), e);
errorLogger.log(L.ERROR, e, ErrorContext.builder().related(event).build());
}
}
@ -147,7 +148,7 @@ public class PlayerOnlineListener {
try {
actOnLogout(event);
} catch (Exception e) {
errorLogger.log(L.WARN, this.getClass(), e);
errorLogger.log(L.ERROR, e, ErrorContext.builder().related(event).build());
}
}
@ -185,7 +186,7 @@ public class PlayerOnlineListener {
try {
actOnServerSwitch(event);
} catch (Exception e) {
errorLogger.log(L.WARN, this.getClass(), e);
errorLogger.log(L.ERROR, e, ErrorContext.builder().related(event).build());
}
}