com.sedmelluq.discord.lavaplayer.tools.ExceptionTools Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of lavaplayer Show documentation
Show all versions of lavaplayer Show documentation
A Lavaplayer fork maintained by Lavalink
package com.sedmelluq.discord.lavaplayer.tools;
import com.sedmelluq.discord.lavaplayer.tools.FriendlyException.Severity;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
/**
* Contains common helper methods for dealing with exceptions.
*/
public class ExceptionTools {
private static final Logger log = LoggerFactory.getLogger(ExceptionTools.class);
private static volatile ErrorDebugInfoHandler debugInfoHandler = new DefaultErrorDebugInfoHandler();
/**
* Sometimes it is necessary to catch Throwable instances for logging or reporting purposes. However, unless for
* specific known cases, Error instances should not be blocked from propagating, so rethrow them.
*
* @param throwable The Throwable to check, it is rethrown if it is an Error
*/
public static void rethrowErrors(Throwable throwable) {
if (throwable instanceof Error) {
throw (Error) throwable;
}
}
/**
* If the exception is not a FriendlyException, wrap with a FriendlyException with the given message
*
* @param message Message of the new FriendlyException if needed
* @param severity Severity of the new FriendlyException
* @param throwable The exception to potentially wrap
* @return Original or wrapped exception
*/
public static FriendlyException wrapUnfriendlyExceptions(String message, Severity severity, Throwable throwable) {
if (throwable instanceof FriendlyException) {
return (FriendlyException) throwable;
} else {
return new FriendlyException(message, severity, throwable);
}
}
/**
* If the exception is not a FriendlyException, wrap with a RuntimeException
*
* @param throwable The exception to potentially wrap
* @return Original or wrapped exception
*/
public static RuntimeException wrapUnfriendlyExceptions(Throwable throwable) {
if (throwable instanceof FriendlyException) {
return (FriendlyException) throwable;
} else {
return new RuntimeException(throwable);
}
}
public static RuntimeException toRuntimeException(Exception e) {
if (e instanceof RuntimeException) {
return (RuntimeException) e;
} else {
return new RuntimeException(e);
}
}
/**
* Finds the first exception which is an instance of the specified class from the throwable cause chain.
*
* @param throwable Throwable to scan.
* @param klass The throwable class to scan for.
* @param The throwable class to scan for.
* @return The first exception in the cause chain (including itself) which is an instance of the specified class.
*/
public static T findDeepException(Throwable throwable, Class klass) {
while (throwable != null) {
if (klass.isAssignableFrom(throwable.getClass())) {
return (T) throwable;
}
throwable = throwable.getCause();
}
return null;
}
/**
* Makes sure thread is set to interrupted state when the throwable is an InterruptedException
*
* @param throwable Throwable to check
*/
public static void keepInterrupted(Throwable throwable) {
if (throwable instanceof InterruptedException) {
Thread.currentThread().interrupt();
}
}
/**
* Log a FriendlyException appropriately according to its severity.
*
* @param log Logger instance to log it to
* @param exception The exception itself
* @param context An object that is included in the log
*/
public static void log(Logger log, FriendlyException exception, Object context) {
switch (exception.severity) {
case COMMON:
log.debug("Common failure for {}: {}", context, exception.getMessage());
break;
case SUSPICIOUS:
log.warn("Suspicious exception for {}", context, exception);
break;
case FAULT:
default:
log.error("Error in {}", context, exception);
break;
}
}
public static void setDebugInfoHandler(ErrorDebugInfoHandler debugInfoHandler) {
ExceptionTools.debugInfoHandler = debugInfoHandler;
}
public static RuntimeException throwWithDebugInfo(
Logger log,
Throwable cause,
String message,
String name,
String value
) {
ErrorDebugInfo debugInfo = new ErrorDebugInfo(log, UUID.randomUUID().toString(), cause, message, name, value);
debugInfoHandler.handle(debugInfo);
return new RuntimeException(message + " EID: " + debugInfo.errorId + ", " + name + " ", cause);
}
/**
* Encode an exception to an output stream
*
* @param output Data output
* @param exception Exception to encode
* @throws IOException On IO error
*/
public static void encodeException(DataOutput output, FriendlyException exception) throws IOException {
List causes = new ArrayList<>();
Throwable next = exception.getCause();
while (next != null) {
causes.add(next);
next = next.getCause();
}
for (int i = causes.size() - 1; i >= 0; i--) {
Throwable cause = causes.get(i);
output.writeBoolean(true);
String message;
if (cause instanceof DecodedException) {
output.writeUTF(((DecodedException) cause).className);
message = ((DecodedException) cause).originalMessage;
} else {
output.writeUTF(cause.getClass().getName());
message = cause.getMessage();
}
output.writeBoolean(message != null);
if (message != null) {
output.writeUTF(message);
}
encodeStackTrace(output, cause);
}
output.writeBoolean(false);
output.writeUTF(exception.getMessage());
output.writeInt(exception.severity.ordinal());
encodeStackTrace(output, exception);
}
/**
* Closes the specified closeable object. In case that throws an error, logs the error with WARN level, but does not
* rethrow.
*
* @param closeable Object to close.
*/
public static void closeWithWarnings(AutoCloseable closeable) {
try {
closeable.close();
} catch (Exception e) {
log.warn("Failed to close.", e);
}
}
private static void encodeStackTrace(DataOutput output, Throwable throwable) throws IOException {
StackTraceElement[] trace = throwable.getStackTrace();
output.writeInt(trace.length);
for (StackTraceElement element : trace) {
output.writeUTF(element.getClassName());
output.writeUTF(element.getMethodName());
String fileName = element.getFileName();
output.writeBoolean(fileName != null);
if (fileName != null) {
output.writeUTF(fileName);
}
output.writeInt(element.getLineNumber());
}
}
/**
* Decode an exception from an input stream
*
* @param input Data input
* @return Decoded exception
* @throws IOException On IO error
*/
public static FriendlyException decodeException(DataInput input) throws IOException {
DecodedException cause = null;
while (input.readBoolean()) {
cause = new DecodedException(input.readUTF(), input.readBoolean() ? input.readUTF() : null, cause);
cause.setStackTrace(decodeStackTrace(input));
}
FriendlyException exception = new FriendlyException(input.readUTF(), Severity.class.getEnumConstants()[input.readInt()], cause);
exception.setStackTrace(decodeStackTrace(input));
return exception;
}
private static StackTraceElement[] decodeStackTrace(DataInput input) throws IOException {
StackTraceElement[] trace = new StackTraceElement[input.readInt()];
for (int i = 0; i < trace.length; i++) {
trace[i] = new StackTraceElement(input.readUTF(), input.readUTF(), input.readBoolean() ? input.readUTF() : null, input.readInt());
}
return trace;
}
public static class ErrorDebugInfo {
public final Logger log;
public final String errorId;
public final Throwable cause;
public final String message;
public final String name;
public final String value;
public ErrorDebugInfo(
Logger log,
String errorId,
Throwable cause,
String message,
String name,
String value
) {
this.log = log;
this.errorId = errorId;
this.cause = cause;
this.message = message;
this.name = name;
this.value = value;
}
}
public interface ErrorDebugInfoHandler {
void handle(ErrorDebugInfo payload);
}
public static class DefaultErrorDebugInfoHandler implements ErrorDebugInfoHandler {
@Override
public void handle(ErrorDebugInfo debugInfo) {
log.warn("{} EID: {}, {}: {}", debugInfo.message, debugInfo.errorId, debugInfo.name, debugInfo.value);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy