Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

INTERNAL: stacktrace all exceptions #840

Merged
merged 1 commit into from
Nov 25, 2024
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
4 changes: 4 additions & 0 deletions src/main/java/net/spy/memcached/ExceptionMessageFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@ private static boolean isBulkOperation(Operation op, Collection<Operation> ops)
}

public static String createCompositeMessage(List<Exception> exceptions) {
if (exceptions == null || exceptions.isEmpty()) {
throw new IllegalArgumentException("At least one exception must be specified");
}

StringBuilder rv = new StringBuilder();
rv.append("Multiple exceptions (");
rv.append(exceptions.size());
Expand Down
42 changes: 41 additions & 1 deletion src/main/java/net/spy/memcached/internal/CompositeException.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package net.spy.memcached.internal;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
Expand All @@ -10,13 +12,51 @@ public class CompositeException extends ExecutionException {

private static final long serialVersionUID = -599478797582490012L;
private final ArrayList<Exception> exceptions = new ArrayList<>();
private final Throwable cause;

CompositeException(List<Exception> exceptions) {
public CompositeException(List<Exception> exceptions) {
super(ExceptionMessageFactory.createCompositeMessage(exceptions));

if (exceptions.size() > 1) {
StringWriter sw = new StringWriter();
sw.write(System.lineSeparator());
try (PrintWriter pw = new PrintWriter(sw)) {
oliviarla marked this conversation as resolved.
Show resolved Hide resolved
for (Exception e : exceptions) {
e.printStackTrace(pw);
}
}

this.cause = new ExceptionOverview(sw.toString());
} else {
this.cause = exceptions.get(0);
}
this.exceptions.addAll(exceptions);
oliviarla marked this conversation as resolved.
Show resolved Hide resolved
}

public List<Exception> getExceptions() {
return exceptions;
}

public int size() {
return exceptions.size();
}

@Override
public synchronized Throwable getCause() {
uhm0311 marked this conversation as resolved.
Show resolved Hide resolved
uhm0311 marked this conversation as resolved.
Show resolved Hide resolved
return cause;
}

static final class ExceptionOverview extends RuntimeException {

private static final long serialVersionUID = -641960514509105302L;

ExceptionOverview(String message) {
super(message);
}

@Override
public synchronized Throwable fillInStackTrace() {
return this;
}
}
}
27 changes: 27 additions & 0 deletions src/test/java/net/spy/memcached/compat/log/LoggingTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@

// XXX: This really needs to get log4j configured first.

import java.util.ArrayList;
import java.util.List;

import net.spy.memcached.internal.CompositeException;
import net.spy.memcached.ops.OperationErrorType;
import net.spy.memcached.ops.OperationException;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

Expand Down Expand Up @@ -154,4 +161,24 @@ void testNoExceptionArg() throws Exception {
assertNull(t);
}

@Test
void logCompositeException() {
List<Exception> exceptions = new ArrayList<>();
exceptions.add(new OperationException(OperationErrorType.SERVER, "msg1"));
exceptions.add(new OperationException(OperationErrorType.CLIENT, "msg2"));
CompositeException exception = new CompositeException(exceptions);

logger.error("failed to get", exception);
}

@Test
void slf4jCompositeException() {
List<Exception> exceptions = new ArrayList<>();
exceptions.add(new OperationException(OperationErrorType.SERVER, "msg1"));
exceptions.add(new OperationException(OperationErrorType.CLIENT, "msg2"));
CompositeException exception = new CompositeException(exceptions);

Log4JLogger log4JLogger = new Log4JLogger(getClass().getName());
log4JLogger.error("failed to get", exception);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package net.spy.memcached.internal;

import java.util.ArrayList;
import java.util.List;

import net.spy.memcached.ops.OperationErrorType;
import net.spy.memcached.ops.OperationException;

import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertTrue;

class CompositeExceptionTest {

@Test
void printStackTraceOfAllExceptions() {
List<Exception> exceptions = new ArrayList<>();
exceptions.add(new OperationException(OperationErrorType.SERVER, "msg1"));
exceptions.add(new OperationException(OperationErrorType.CLIENT, "msg2"));
Exception e = throwError1();
exceptions.add(e);
CompositeException compositeException = new CompositeException(exceptions);
String message = compositeException.getCause().getMessage();

assertTrue(message
.contains("OperationException: SERVER: msg1"));
assertTrue(message
.contains("OperationException: CLIENT: msg2"));
assertTrue(message
.contains("OperationException: SERVER: msg3"));
assertTrue(message
.contains("at net.spy.memcached.internal.CompositeExceptionTest.throwError2"));
assertTrue(message
.contains("at net.spy.memcached.internal.CompositeExceptionTest.throwError1"));
}

private Exception throwError1() {
return throwError2();
}

private Exception throwError2() {
return new OperationException(OperationErrorType.SERVER, "msg3");
}
}