Skip to content

Commit

Permalink
Merge pull request #1857 from newrelic/log-caused-by
Browse files Browse the repository at this point in the history
Add "caused by"  section(s) when forwarding logs
  • Loading branch information
jtduffy authored Apr 17, 2024
2 parents d3dea38 + ddfb068 commit c07478f
Show file tree
Hide file tree
Showing 10 changed files with 313 additions and 217 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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<String> 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) {
Expand All @@ -49,4 +48,16 @@ public static String getErrorClass(Throwable throwable) {
}
return throwable.getClass().getName();
}

private static Collection<String> stackTracesToStrings(StackTraceElement[] stackTraces) {
if (stackTraces == null || stackTraces.length == 0) {
return Collections.emptyList();
}
List<String> lines = new ArrayList<>(stackTraces.length);
for (StackTraceElement e : stackTraces) {
lines.add(" at " + e.toString());
}

return lines;
}
}
Original file line number Diff line number Diff line change
@@ -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"));
}
}
Original file line number Diff line number Diff line change
@@ -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;

Expand All @@ -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<String> 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<String> stackTracesToStrings(StackTraceElement[] stackTraces) {
if (stackTraces == null || stackTraces.length == 0) {
return Collections.emptyList();
}
List<String> lines = new ArrayList<>(stackTraces.length);
for (StackTraceElement e : stackTraces) {
lines.add(" at " + e.toString());
}

return lines;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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"));
}
}
Original file line number Diff line number Diff line change
@@ -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;

Expand All @@ -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<String> 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<String> stackTracesToStrings(StackTraceElement[] stackTraces) {
if (stackTraces == null || stackTraces.length == 0) {
return Collections.emptyList();
}
List<String> lines = new ArrayList<>(stackTraces.length);
for (StackTraceElement e : stackTraces) {
lines.add(" at " + e.toString());
}

return lines;
}
}
Loading

0 comments on commit c07478f

Please sign in to comment.