diff --git a/instrumentation/apache-log4j-1/src/main/java/com/nr/agent/instrumentation/log4j1/Log4j1ExceptionUtil.java b/instrumentation/apache-log4j-1/src/main/java/com/nr/agent/instrumentation/log4j1/Log4j1ExceptionUtil.java index 2e1db9b1cc..86a84d8ded 100644 --- a/instrumentation/apache-log4j-1/src/main/java/com/nr/agent/instrumentation/log4j1/Log4j1ExceptionUtil.java +++ b/instrumentation/apache-log4j-1/src/main/java/com/nr/agent/instrumentation/log4j1/Log4j1ExceptionUtil.java @@ -7,6 +7,11 @@ package com.nr.agent.instrumentation.log4j1; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + public class Log4j1ExceptionUtil { public static final int MAX_STACK_SIZE = 300; @@ -15,25 +20,19 @@ public static String getErrorStack(Throwable throwable) { return null; } - StackTraceElement[] stack = throwable.getStackTrace(); - return getErrorStack(stack); - } - - public static String getErrorStack(StackTraceElement[] stack) { - return getErrorStack(stack, MAX_STACK_SIZE); - } - - public static String getErrorStack(StackTraceElement[] stack, Integer maxStackSize) { - if (stack == null || stack.length == 0) { - return null; + Throwable t = throwable; + List lines = new ArrayList<>(); + boolean inner = false; + while (t != null) { + if (inner) { + lines.add(" caused by: " + t.getClass().getName() + ": " + t.getMessage()); + } + lines.addAll(stackTracesToStrings(t.getStackTrace())); + t = t.equals(t.getCause()) ? null : t.getCause(); + inner = true; } - StringBuilder stackBuilder = new StringBuilder(); - int stackSizeLimit = Math.min(maxStackSize, stack.length); - for (int i = 0; i < stackSizeLimit; i++) { - stackBuilder.append(" at ").append(stack[i].toString()).append("\n"); - } - return stackBuilder.toString(); + return String.join("\n", lines.subList(0, Math.min(lines.size(), MAX_STACK_SIZE))); } public static String getErrorMessage(Throwable throwable) { @@ -49,4 +48,16 @@ public static String getErrorClass(Throwable throwable) { } return throwable.getClass().getName(); } + + private static Collection stackTracesToStrings(StackTraceElement[] stackTraces) { + if (stackTraces == null || stackTraces.length == 0) { + return Collections.emptyList(); + } + List lines = new ArrayList<>(stackTraces.length); + for (StackTraceElement e : stackTraces) { + lines.add(" at " + e.toString()); + } + + return lines; + } } \ No newline at end of file diff --git a/instrumentation/apache-log4j-1/src/test/java/com/nr/agent/instrumentation/log4j1/Log4j1ExceptionUtilTest.java b/instrumentation/apache-log4j-1/src/test/java/com/nr/agent/instrumentation/log4j1/Log4j1ExceptionUtilTest.java new file mode 100644 index 0000000000..fb35b2f3d8 --- /dev/null +++ b/instrumentation/apache-log4j-1/src/test/java/com/nr/agent/instrumentation/log4j1/Log4j1ExceptionUtilTest.java @@ -0,0 +1,47 @@ +package com.nr.agent.instrumentation.log4j1; + +import org.junit.Test; + +import static org.junit.Assert.*; + +public class Log4j1ExceptionUtilTest { + @Test + public void getErrorStack_withThrowable_generatesFullStacktrace() { + assertFalse(Log4j1ExceptionUtil.getErrorStack(createTestException()).contains("caused by: java.lang.Exception: inner exception")); + + assertTrue(Log4j1ExceptionUtil.getErrorStack(createTestExceptionWithCausedBy()).contains("caused by: java.lang.Exception: inner exception")); + } + + @Test + public void getErrorStack_withNullThrowable_returnsNull() { + assertNull(Log4j1ExceptionUtil.getErrorStack(null)); + } + + @Test + public void getErrorMessage_withThrowable_returnsErrorMessage() { + assertEquals("test exception", Log4j1ExceptionUtil.getErrorMessage(createTestException())); + } + + @Test + public void getErrorMessage_withNullThrowable_returnsNull() { + assertNull(Log4j1ExceptionUtil.getErrorMessage(null)); + } + + @Test + public void getErrorClass_withThrowable_returnsErrorMessage() { + assertEquals("java.lang.Exception", Log4j1ExceptionUtil.getErrorClass(createTestException())); + } + + @Test + public void getErrorClass_withNullThrowable_returnsNull() { + assertNull(Log4j1ExceptionUtil.getErrorClass(null)); + } + + private Exception createTestException() { + return new Exception("test exception"); + } + + private Exception createTestExceptionWithCausedBy() { + return new Exception("test exception", new Exception("inner exception")); + } +} diff --git a/instrumentation/apache-log4j-2.11/src/main/java/com/nr/agent/instrumentation/log4j2/ExceptionUtil.java b/instrumentation/apache-log4j-2.11/src/main/java/com/nr/agent/instrumentation/log4j2/ExceptionUtil.java index a0f650906a..3237c033b2 100644 --- a/instrumentation/apache-log4j-2.11/src/main/java/com/nr/agent/instrumentation/log4j2/ExceptionUtil.java +++ b/instrumentation/apache-log4j-2.11/src/main/java/com/nr/agent/instrumentation/log4j2/ExceptionUtil.java @@ -1,5 +1,10 @@ package com.nr.agent.instrumentation.log4j2; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + public class ExceptionUtil { public static final int MAX_STACK_SIZE = 300; @@ -8,42 +13,48 @@ public static boolean isThrowableNull(Throwable throwable) { } public static String getErrorStack(Throwable throwable) { - if (isThrowableNull(throwable)) { + if (throwable == null) { return null; } - StackTraceElement[] stack = throwable.getStackTrace(); - return getErrorStack(stack); - } - - public static String getErrorStack(StackTraceElement[] stack) { - return getErrorStack(stack, MAX_STACK_SIZE); - } - - public static String getErrorStack(StackTraceElement[] stack, Integer maxStackSize) { - if (stack == null || stack.length == 0) { - return null; + Throwable t = throwable; + List lines = new ArrayList<>(); + boolean inner = false; + while (t != null) { + if (inner) { + lines.add(" caused by: " + t.getClass().getName() + ": " + t.getMessage()); + } + lines.addAll(stackTracesToStrings(t.getStackTrace())); + t = t.equals(t.getCause()) ? null : t.getCause(); + inner = true; } - StringBuilder stackBuilder = new StringBuilder(); - int stackSizeLimit = Math.min(maxStackSize, stack.length); - for (int i = 0; i < stackSizeLimit; i++) { - stackBuilder.append(" at ").append(stack[i].toString()).append("\n"); - } - return stackBuilder.toString(); + return String.join("\n", lines.subList(0, Math.min(lines.size(), MAX_STACK_SIZE))); } public static String getErrorMessage(Throwable throwable) { - if (isThrowableNull(throwable)) { + if (throwable == null) { return null; } return throwable.getMessage(); } public static String getErrorClass(Throwable throwable) { - if (isThrowableNull(throwable)) { + if (throwable == null) { return null; } return throwable.getClass().getName(); } + + private static Collection stackTracesToStrings(StackTraceElement[] stackTraces) { + if (stackTraces == null || stackTraces.length == 0) { + return Collections.emptyList(); + } + List lines = new ArrayList<>(stackTraces.length); + for (StackTraceElement e : stackTraces) { + lines.add(" at " + e.toString()); + } + + return lines; + } } diff --git a/instrumentation/apache-log4j-2.11/src/test/java/com/nr/agent/instrumentation/log4j2/ExceptionUtilTest.java b/instrumentation/apache-log4j-2.11/src/test/java/com/nr/agent/instrumentation/log4j2/ExceptionUtilTest.java index 85545ba746..3484b8f7d6 100644 --- a/instrumentation/apache-log4j-2.11/src/test/java/com/nr/agent/instrumentation/log4j2/ExceptionUtilTest.java +++ b/instrumentation/apache-log4j-2.11/src/test/java/com/nr/agent/instrumentation/log4j2/ExceptionUtilTest.java @@ -9,6 +9,18 @@ public class ExceptionUtilTest { + @Test + public void getErrorStack_withThrowable_generatesFullStacktrace() { + assertFalse(ExceptionUtil.getErrorStack(createTestException()).contains("caused by: java.lang.Exception: inner exception")); + + assertTrue(ExceptionUtil.getErrorStack(createTestExceptionWithCausedBy()).contains("caused by: java.lang.Exception: inner exception")); + } + + @Test + public void getErrorStack_withNullThrowable_returnsNull() { + assertNull(ExceptionUtil.getErrorStack(null)); + } + @Test public void testIsThrowableNull() { Throwable nullThrowable = null; @@ -19,43 +31,30 @@ public void testIsThrowableNull() { } @Test - public void testGetErrorStack() { - int maxStackSize = 3; - StackTraceElement stackTraceElement1 = new StackTraceElement("Class1", "method1", "File1", 1); - StackTraceElement stackTraceElement2 = new StackTraceElement("Class2", "method2", "File2", 2); - StackTraceElement stackTraceElement3 = new StackTraceElement("Class3", "method3", "File3", 3); - StackTraceElement stackTraceElement4 = new StackTraceElement("Class4", "method4", "File4", 4); - StackTraceElement stackTraceElement5 = new StackTraceElement("Class5", "method5", "File5", 5); - StackTraceElement[] stack = new StackTraceElement[] { stackTraceElement1, stackTraceElement2, stackTraceElement3, stackTraceElement4, - stackTraceElement5 }; - String errorStack = ExceptionUtil.getErrorStack(stack, maxStackSize); - - // Processed stack should be limited to only the first three lines - assertTrue(errorStack.contains(stackTraceElement1.toString())); - assertTrue(errorStack.contains(stackTraceElement2.toString())); - assertTrue(errorStack.contains(stackTraceElement3.toString())); - // Processed stack should omit the last two lines - assertFalse(errorStack.contains(stackTraceElement4.toString())); - assertFalse(errorStack.contains(stackTraceElement5.toString())); + public void getErrorMessage_withThrowable_returnsErrorMessage() { + assertEquals("test exception", ExceptionUtil.getErrorMessage(createTestException())); } @Test - public void testGetErrorMessage() { - String expectedMessage = "Hi"; - Throwable nullThrowable = null; - Throwable nonNullThrowable = new Throwable(expectedMessage); + public void getErrorMessage_withNullThrowable_returnsNull() { + assertNull(ExceptionUtil.getErrorMessage(null)); + } - assertNull(ExceptionUtil.getErrorMessage(nullThrowable)); - assertEquals(expectedMessage, ExceptionUtil.getErrorMessage(nonNullThrowable)); + @Test + public void getErrorClass_withThrowable_returnsErrorMessage() { + assertEquals("java.lang.Exception", ExceptionUtil.getErrorClass(createTestException())); } @Test - public void testGetErrorClass() { - String expectedExceptionClass = "java.lang.RuntimeException"; - Throwable nullThrowable = null; - RuntimeException runtimeException = new RuntimeException("Hi"); + public void getErrorClass_withNullThrowable_returnsNull() { + assertNull(ExceptionUtil.getErrorClass(null)); + } + + private Exception createTestException() { + return new Exception("test exception"); + } - assertNull(ExceptionUtil.getErrorClass(nullThrowable)); - assertEquals(expectedExceptionClass, ExceptionUtil.getErrorClass(runtimeException)); + private Exception createTestExceptionWithCausedBy() { + return new Exception("test exception", new Exception("inner exception")); } } diff --git a/instrumentation/apache-log4j-2.6/src/main/java/com/nr/agent/instrumentation/log4j2/ExceptionUtil.java b/instrumentation/apache-log4j-2.6/src/main/java/com/nr/agent/instrumentation/log4j2/ExceptionUtil.java index a0f650906a..3237c033b2 100644 --- a/instrumentation/apache-log4j-2.6/src/main/java/com/nr/agent/instrumentation/log4j2/ExceptionUtil.java +++ b/instrumentation/apache-log4j-2.6/src/main/java/com/nr/agent/instrumentation/log4j2/ExceptionUtil.java @@ -1,5 +1,10 @@ package com.nr.agent.instrumentation.log4j2; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + public class ExceptionUtil { public static final int MAX_STACK_SIZE = 300; @@ -8,42 +13,48 @@ public static boolean isThrowableNull(Throwable throwable) { } public static String getErrorStack(Throwable throwable) { - if (isThrowableNull(throwable)) { + if (throwable == null) { return null; } - StackTraceElement[] stack = throwable.getStackTrace(); - return getErrorStack(stack); - } - - public static String getErrorStack(StackTraceElement[] stack) { - return getErrorStack(stack, MAX_STACK_SIZE); - } - - public static String getErrorStack(StackTraceElement[] stack, Integer maxStackSize) { - if (stack == null || stack.length == 0) { - return null; + Throwable t = throwable; + List lines = new ArrayList<>(); + boolean inner = false; + while (t != null) { + if (inner) { + lines.add(" caused by: " + t.getClass().getName() + ": " + t.getMessage()); + } + lines.addAll(stackTracesToStrings(t.getStackTrace())); + t = t.equals(t.getCause()) ? null : t.getCause(); + inner = true; } - StringBuilder stackBuilder = new StringBuilder(); - int stackSizeLimit = Math.min(maxStackSize, stack.length); - for (int i = 0; i < stackSizeLimit; i++) { - stackBuilder.append(" at ").append(stack[i].toString()).append("\n"); - } - return stackBuilder.toString(); + return String.join("\n", lines.subList(0, Math.min(lines.size(), MAX_STACK_SIZE))); } public static String getErrorMessage(Throwable throwable) { - if (isThrowableNull(throwable)) { + if (throwable == null) { return null; } return throwable.getMessage(); } public static String getErrorClass(Throwable throwable) { - if (isThrowableNull(throwable)) { + if (throwable == null) { return null; } return throwable.getClass().getName(); } + + private static Collection stackTracesToStrings(StackTraceElement[] stackTraces) { + if (stackTraces == null || stackTraces.length == 0) { + return Collections.emptyList(); + } + List lines = new ArrayList<>(stackTraces.length); + for (StackTraceElement e : stackTraces) { + lines.add(" at " + e.toString()); + } + + return lines; + } } diff --git a/instrumentation/apache-log4j-2.6/src/test/java/com/nr/agent/instrumentation/log4j2/ExceptionUtilTest.java b/instrumentation/apache-log4j-2.6/src/test/java/com/nr/agent/instrumentation/log4j2/ExceptionUtilTest.java index 85545ba746..ed2ce85a0b 100644 --- a/instrumentation/apache-log4j-2.6/src/test/java/com/nr/agent/instrumentation/log4j2/ExceptionUtilTest.java +++ b/instrumentation/apache-log4j-2.6/src/test/java/com/nr/agent/instrumentation/log4j2/ExceptionUtilTest.java @@ -8,6 +8,17 @@ import static org.junit.Assert.assertTrue; public class ExceptionUtilTest { + @Test + public void getErrorStack_withThrowable_generatesFullStacktrace() { + assertFalse(ExceptionUtil.getErrorStack(createTestException()).contains("caused by: java.lang.Exception: inner exception")); + + assertTrue(ExceptionUtil.getErrorStack(createTestExceptionWithCausedBy()).contains("caused by: java.lang.Exception: inner exception")); + } + + @Test + public void getErrorStack_withNullThrowable_returnsNull() { + assertNull(ExceptionUtil.getErrorStack(null)); + } @Test public void testIsThrowableNull() { @@ -19,43 +30,30 @@ public void testIsThrowableNull() { } @Test - public void testGetErrorStack() { - int maxStackSize = 3; - StackTraceElement stackTraceElement1 = new StackTraceElement("Class1", "method1", "File1", 1); - StackTraceElement stackTraceElement2 = new StackTraceElement("Class2", "method2", "File2", 2); - StackTraceElement stackTraceElement3 = new StackTraceElement("Class3", "method3", "File3", 3); - StackTraceElement stackTraceElement4 = new StackTraceElement("Class4", "method4", "File4", 4); - StackTraceElement stackTraceElement5 = new StackTraceElement("Class5", "method5", "File5", 5); - StackTraceElement[] stack = new StackTraceElement[] { stackTraceElement1, stackTraceElement2, stackTraceElement3, stackTraceElement4, - stackTraceElement5 }; - String errorStack = ExceptionUtil.getErrorStack(stack, maxStackSize); - - // Processed stack should be limited to only the first three lines - assertTrue(errorStack.contains(stackTraceElement1.toString())); - assertTrue(errorStack.contains(stackTraceElement2.toString())); - assertTrue(errorStack.contains(stackTraceElement3.toString())); - // Processed stack should omit the last two lines - assertFalse(errorStack.contains(stackTraceElement4.toString())); - assertFalse(errorStack.contains(stackTraceElement5.toString())); + public void getErrorMessage_withThrowable_returnsErrorMessage() { + assertEquals("test exception", ExceptionUtil.getErrorMessage(createTestException())); } @Test - public void testGetErrorMessage() { - String expectedMessage = "Hi"; - Throwable nullThrowable = null; - Throwable nonNullThrowable = new Throwable(expectedMessage); + public void getErrorMessage_withNullThrowable_returnsNull() { + assertNull(ExceptionUtil.getErrorMessage(null)); + } - assertNull(ExceptionUtil.getErrorMessage(nullThrowable)); - assertEquals(expectedMessage, ExceptionUtil.getErrorMessage(nonNullThrowable)); + @Test + public void getErrorClass_withThrowable_returnsErrorMessage() { + assertEquals("java.lang.Exception", ExceptionUtil.getErrorClass(createTestException())); } @Test - public void testGetErrorClass() { - String expectedExceptionClass = "java.lang.RuntimeException"; - Throwable nullThrowable = null; - RuntimeException runtimeException = new RuntimeException("Hi"); + public void getErrorClass_withNullThrowable_returnsNull() { + assertNull(ExceptionUtil.getErrorClass(null)); + } + + private Exception createTestException() { + return new Exception("test exception"); + } - assertNull(ExceptionUtil.getErrorClass(nullThrowable)); - assertEquals(expectedExceptionClass, ExceptionUtil.getErrorClass(runtimeException)); + private Exception createTestExceptionWithCausedBy() { + return new Exception("test exception", new Exception("inner exception")); } } diff --git a/instrumentation/java.logging-jdk8/src/main/java/com/nr/instrumentation/jul/ExceptionUtil.java b/instrumentation/java.logging-jdk8/src/main/java/com/nr/instrumentation/jul/ExceptionUtil.java index 148114c80c..d0d4905c9e 100644 --- a/instrumentation/java.logging-jdk8/src/main/java/com/nr/instrumentation/jul/ExceptionUtil.java +++ b/instrumentation/java.logging-jdk8/src/main/java/com/nr/instrumentation/jul/ExceptionUtil.java @@ -7,6 +7,11 @@ package com.nr.instrumentation.jul; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + public class ExceptionUtil { public static final int MAX_STACK_SIZE = 300; @@ -15,42 +20,48 @@ public static boolean isThrowableNull(Throwable throwable) { } public static String getErrorStack(Throwable throwable) { - if (isThrowableNull(throwable)) { + if (throwable == null) { return null; } - StackTraceElement[] stack = throwable.getStackTrace(); - return getErrorStack(stack); - } - - public static String getErrorStack(StackTraceElement[] stack) { - return getErrorStack(stack, MAX_STACK_SIZE); - } - - public static String getErrorStack(StackTraceElement[] stack, Integer maxStackSize) { - if (stack == null || stack.length == 0) { - return null; + Throwable t = throwable; + List lines = new ArrayList<>(); + boolean inner = false; + while (t != null) { + if (inner) { + lines.add(" caused by: " + t.getClass().getName() + ": " + t.getMessage()); + } + lines.addAll(stackTracesToStrings(t.getStackTrace())); + t = t.equals(t.getCause()) ? null : t.getCause(); + inner = true; } - StringBuilder stackBuilder = new StringBuilder(); - int stackSizeLimit = Math.min(maxStackSize, stack.length); - for (int i = 0; i < stackSizeLimit; i++) { - stackBuilder.append(" at ").append(stack[i].toString()).append("\n"); - } - return stackBuilder.toString(); + return String.join("\n", lines.subList(0, Math.min(lines.size(), MAX_STACK_SIZE))); } public static String getErrorMessage(Throwable throwable) { - if (isThrowableNull(throwable)) { + if (throwable == null) { return null; } return throwable.getMessage(); } public static String getErrorClass(Throwable throwable) { - if (isThrowableNull(throwable)) { + if (throwable == null) { return null; } return throwable.getClass().getName(); } + + private static Collection stackTracesToStrings(StackTraceElement[] stackTraces) { + if (stackTraces == null || stackTraces.length == 0) { + return Collections.emptyList(); + } + List lines = new ArrayList<>(stackTraces.length); + for (StackTraceElement e : stackTraces) { + lines.add(" at " + e.toString()); + } + + return lines; + } } diff --git a/instrumentation/java.logging-jdk8/src/test/java/com/nr/instrumentation/jul/ExceptionUtilTest.java b/instrumentation/java.logging-jdk8/src/test/java/com/nr/instrumentation/jul/ExceptionUtilTest.java index 322c20e3bc..7937929374 100644 --- a/instrumentation/java.logging-jdk8/src/test/java/com/nr/instrumentation/jul/ExceptionUtilTest.java +++ b/instrumentation/java.logging-jdk8/src/test/java/com/nr/instrumentation/jul/ExceptionUtilTest.java @@ -15,6 +15,17 @@ import static org.junit.Assert.assertTrue; public class ExceptionUtilTest { + @Test + public void getErrorStack_withThrowable_generatesFullStacktrace() { + assertFalse(ExceptionUtil.getErrorStack(createTestException()).contains("caused by: java.lang.Exception: inner exception")); + + assertTrue(ExceptionUtil.getErrorStack(createTestExceptionWithCausedBy()).contains("caused by: java.lang.Exception: inner exception")); + } + + @Test + public void getErrorStack_withNullThrowable_returnsNull() { + assertNull(ExceptionUtil.getErrorStack(null)); + } @Test public void testIsThrowableNull() { @@ -26,43 +37,30 @@ public void testIsThrowableNull() { } @Test - public void testGetErrorStack() { - int maxStackSize = 3; - StackTraceElement stackTraceElement1 = new StackTraceElement("Class1", "method1", "File1", 1); - StackTraceElement stackTraceElement2 = new StackTraceElement("Class2", "method2", "File2", 2); - StackTraceElement stackTraceElement3 = new StackTraceElement("Class3", "method3", "File3", 3); - StackTraceElement stackTraceElement4 = new StackTraceElement("Class4", "method4", "File4", 4); - StackTraceElement stackTraceElement5 = new StackTraceElement("Class5", "method5", "File5", 5); - StackTraceElement[] stack = new StackTraceElement[] { stackTraceElement1, stackTraceElement2, stackTraceElement3, stackTraceElement4, - stackTraceElement5 }; - String errorStack = ExceptionUtil.getErrorStack(stack, maxStackSize); - - // Processed stack should be limited to only the first three lines - assertTrue(errorStack.contains(stackTraceElement1.toString())); - assertTrue(errorStack.contains(stackTraceElement2.toString())); - assertTrue(errorStack.contains(stackTraceElement3.toString())); - // Processed stack should omit the last two lines - assertFalse(errorStack.contains(stackTraceElement4.toString())); - assertFalse(errorStack.contains(stackTraceElement5.toString())); + public void getErrorMessage_withThrowable_returnsErrorMessage() { + assertEquals("test exception", ExceptionUtil.getErrorMessage(createTestException())); } @Test - public void testGetErrorMessage() { - String expectedMessage = "Hi"; - Throwable nullThrowable = null; - Throwable nonNullThrowable = new Throwable(expectedMessage); + public void getErrorMessage_withNullThrowable_returnsNull() { + assertNull(ExceptionUtil.getErrorMessage(null)); + } - assertNull(ExceptionUtil.getErrorMessage(nullThrowable)); - assertEquals(expectedMessage, ExceptionUtil.getErrorMessage(nonNullThrowable)); + @Test + public void getErrorClass_withThrowable_returnsErrorMessage() { + assertEquals("java.lang.Exception", ExceptionUtil.getErrorClass(createTestException())); } @Test - public void testGetErrorClass() { - String expectedExceptionClass = "java.lang.RuntimeException"; - Throwable nullThrowable = null; - RuntimeException runtimeException = new RuntimeException("Hi"); + public void getErrorClass_withNullThrowable_returnsNull() { + assertNull(ExceptionUtil.getErrorClass(null)); + } + + private Exception createTestException() { + return new Exception("test exception"); + } - assertNull(ExceptionUtil.getErrorClass(nullThrowable)); - assertEquals(expectedExceptionClass, ExceptionUtil.getErrorClass(runtimeException)); + private Exception createTestExceptionWithCausedBy() { + return new Exception("test exception", new Exception("inner exception")); } } diff --git a/instrumentation/logback-classic-1.2/src/main/java/com/nr/agent/instrumentation/logbackclassic12/ExceptionUtil.java b/instrumentation/logback-classic-1.2/src/main/java/com/nr/agent/instrumentation/logbackclassic12/ExceptionUtil.java index 1de3e84418..6add4f9542 100644 --- a/instrumentation/logback-classic-1.2/src/main/java/com/nr/agent/instrumentation/logbackclassic12/ExceptionUtil.java +++ b/instrumentation/logback-classic-1.2/src/main/java/com/nr/agent/instrumentation/logbackclassic12/ExceptionUtil.java @@ -1,5 +1,10 @@ package com.nr.agent.instrumentation.logbackclassic12; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + public class ExceptionUtil { public static final int MAX_STACK_SIZE = 300; @@ -8,42 +13,48 @@ public static boolean isThrowableNull(Throwable throwable) { } public static String getErrorStack(Throwable throwable) { - if (isThrowableNull(throwable)) { + if (throwable == null) { return null; } - StackTraceElement[] stack = throwable.getStackTrace(); - return getErrorStack(stack); - } - - public static String getErrorStack(StackTraceElement[] stack) { - return getErrorStack(stack, MAX_STACK_SIZE); - } - - public static String getErrorStack(StackTraceElement[] stack, Integer maxStackSize) { - if (stack == null || stack.length == 0) { - return null; + Throwable t = throwable; + List lines = new ArrayList<>(); + boolean inner = false; + while (t != null) { + if (inner) { + lines.add(" caused by: " + t.getClass().getName() + ": " + t.getMessage()); + } + lines.addAll(stackTracesToStrings(t.getStackTrace())); + t = t.equals(t.getCause()) ? null : t.getCause(); + inner = true; } - StringBuilder stackBuilder = new StringBuilder(); - int stackSizeLimit = Math.min(maxStackSize, stack.length); - for (int i = 0; i < stackSizeLimit; i++) { - stackBuilder.append(" at ").append(stack[i].toString()).append("\n"); - } - return stackBuilder.toString(); + return String.join("\n", lines.subList(0, Math.min(lines.size(), MAX_STACK_SIZE))); } public static String getErrorMessage(Throwable throwable) { - if (isThrowableNull(throwable)) { + if (throwable == null) { return null; } return throwable.getMessage(); } public static String getErrorClass(Throwable throwable) { - if (isThrowableNull(throwable)) { + if (throwable == null) { return null; } return throwable.getClass().getName(); } + + private static Collection stackTracesToStrings(StackTraceElement[] stackTraces) { + if (stackTraces == null || stackTraces.length == 0) { + return Collections.emptyList(); + } + List lines = new ArrayList<>(stackTraces.length); + for (StackTraceElement e : stackTraces) { + lines.add(" at " + e.toString()); + } + + return lines; + } } diff --git a/instrumentation/logback-classic-1.2/src/test/java/com/nr/agent/instrumentation/logbackclassic12/ExceptionUtilTest.java b/instrumentation/logback-classic-1.2/src/test/java/com/nr/agent/instrumentation/logbackclassic12/ExceptionUtilTest.java index 29f6bad6f1..33ef05b31d 100644 --- a/instrumentation/logback-classic-1.2/src/test/java/com/nr/agent/instrumentation/logbackclassic12/ExceptionUtilTest.java +++ b/instrumentation/logback-classic-1.2/src/test/java/com/nr/agent/instrumentation/logbackclassic12/ExceptionUtilTest.java @@ -9,6 +9,18 @@ public class ExceptionUtilTest { + @Test + public void getErrorStack_withThrowable_generatesFullStacktrace() { + assertFalse(ExceptionUtil.getErrorStack(createTestException()).contains("caused by: java.lang.Exception: inner exception")); + + assertTrue(ExceptionUtil.getErrorStack(createTestExceptionWithCausedBy()).contains("caused by: java.lang.Exception: inner exception")); + } + + @Test + public void getErrorStack_withNullThrowable_returnsNull() { + assertNull(ExceptionUtil.getErrorStack(null)); + } + @Test public void testIsThrowableNull() { Throwable nullThrowable = null; @@ -19,43 +31,30 @@ public void testIsThrowableNull() { } @Test - public void testGetErrorStack() { - int maxStackSize = 3; - StackTraceElement stackTraceElement1 = new StackTraceElement("Class1", "method1", "File1", 1); - StackTraceElement stackTraceElement2 = new StackTraceElement("Class2", "method2", "File2", 2); - StackTraceElement stackTraceElement3 = new StackTraceElement("Class3", "method3", "File3", 3); - StackTraceElement stackTraceElement4 = new StackTraceElement("Class4", "method4", "File4", 4); - StackTraceElement stackTraceElement5 = new StackTraceElement("Class5", "method5", "File5", 5); - StackTraceElement[] stack = new StackTraceElement[] { stackTraceElement1, stackTraceElement2, stackTraceElement3, stackTraceElement4, - stackTraceElement5 }; - String errorStack = ExceptionUtil.getErrorStack(stack, maxStackSize); - - // Processed stack should be limited to only the first three lines - assertTrue(errorStack.contains(stackTraceElement1.toString())); - assertTrue(errorStack.contains(stackTraceElement2.toString())); - assertTrue(errorStack.contains(stackTraceElement3.toString())); - // Processed stack should omit the last two lines - assertFalse(errorStack.contains(stackTraceElement4.toString())); - assertFalse(errorStack.contains(stackTraceElement5.toString())); + public void getErrorMessage_withThrowable_returnsErrorMessage() { + assertEquals("test exception", ExceptionUtil.getErrorMessage(createTestException())); } @Test - public void testGetErrorMessage() { - String expectedMessage = "Hi"; - Throwable nullThrowable = null; - Throwable nonNullThrowable = new Throwable(expectedMessage); + public void getErrorMessage_withNullThrowable_returnsNull() { + assertNull(ExceptionUtil.getErrorMessage(null)); + } - assertNull(ExceptionUtil.getErrorMessage(nullThrowable)); - assertEquals(expectedMessage, ExceptionUtil.getErrorMessage(nonNullThrowable)); + @Test + public void getErrorClass_withThrowable_returnsErrorMessage() { + assertEquals("java.lang.Exception", ExceptionUtil.getErrorClass(createTestException())); } @Test - public void testGetErrorClass() { - String expectedExceptionClass = "java.lang.RuntimeException"; - Throwable nullThrowable = null; - RuntimeException runtimeException = new RuntimeException("Hi"); + public void getErrorClass_withNullThrowable_returnsNull() { + assertNull(ExceptionUtil.getErrorClass(null)); + } + + private Exception createTestException() { + return new Exception("test exception"); + } - assertNull(ExceptionUtil.getErrorClass(nullThrowable)); - assertEquals(expectedExceptionClass, ExceptionUtil.getErrorClass(runtimeException)); + private Exception createTestExceptionWithCausedBy() { + return new Exception("test exception", new Exception("inner exception")); } }