Skip to content

Commit 23e4825

Browse files
authored
Merge pull request #82 from newrelic/capture-full-trace
Capture full stack traces; add config support for unlimited trace size
2 parents ce673eb + d5d1644 commit 23e4825

File tree

12 files changed

+143
-337
lines changed

12 files changed

+143
-337
lines changed

README.md

+5
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,17 @@ MDC data will be added to log events with a `context.` prefix to distinguish it
3434
collisions with New Relic specific context keys.
3535

3636
### Exception Stack Trace Size
37+
**Note:** The log extensions now have the ability to capture the entire stack trace - specifically any `caused by` sections.
38+
Prior versions of the extension only reported the trace for the thrown exception and ignored any nested `caused by` `Throwables`.
39+
3740
You can configure the logging extension to control the max stack trace size for exceptions added to log events.
3841

3942
Default max stack trace size is `300`. It is recommended that you do not exceed this value or data could be dropped or truncated as well as
4043
lead to higher log event ingest costs. Max stack trace size can be configured by environment variable (`NEW_RELIC_LOG_EXTENSION_MAX_STACK_SIZE=integer`)
4144
or system property (`-Dnewrelic.log_extension.max_stack_size=integer`).
4245

46+
Explicitly setting this property to `0` will not impose a maximum size constraint on the stack trace size.
47+
4348
## Support
4449

4550
Should you need assistance with New Relic products, you are in good hands with several diagnostic tools and support channels.

core/build.gradle.kts

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ configure<JavaPluginConvention> {
1212
}
1313

1414
dependencies {
15+
implementation("org.apache.commons:commons-lang3:3.13.0")
1516
testImplementation("org.junit.jupiter:junit-jupiter:5.6.2")
1617
// Allows for easy testing of values based on Environment Variables and System Properties
1718
testImplementation("com.github.stefanbirkner:system-lambda:1.2.1")

core/src/main/java/com/newrelic/logging/core/ExceptionUtil.java

+23-13
Original file line numberDiff line numberDiff line change
@@ -5,32 +5,42 @@
55

66
package com.newrelic.logging.core;
77

8+
import org.apache.commons.lang3.exception.ExceptionUtils;
9+
10+
import java.util.Arrays;
11+
812
import static com.newrelic.logging.core.LogExtensionConfig.getMaxStackSize;
913

1014
public class ExceptionUtil {
1115
public static final int MAX_STACK_SIZE_DEFAULT = 300;
12-
public static String getErrorStack(Throwable throwable) {
16+
17+
public static String getFullStackTrace(Throwable throwable) {
1318
if (throwable == null) {
1419
return null;
1520
}
1621

17-
StackTraceElement[] stack = throwable.getStackTrace();
18-
return getErrorStack(stack);
19-
}
20-
21-
public static String getErrorStack(StackTraceElement[] stack) {
22-
return getErrorStack(stack, getMaxStackSize());
22+
return getStackTraceStringFromFramesArray(ExceptionUtils.getStackFrames(throwable));
2323
}
2424

25-
public static String getErrorStack(StackTraceElement[] stack, Integer maxStackSize) {
26-
if (stack == null || stack.length == 0) {
25+
public static String transformLogbackStackTraceString(String trace) {
26+
if (trace == null || trace.length() == 0) {
2727
return null;
2828
}
2929

30-
StringBuilder stackBuilder = new StringBuilder();
31-
for(int i = 0; i < Math.min(maxStackSize, stack.length); i++) {
32-
stackBuilder.append(" at ").append(stack[i].toString()).append("\n");
30+
return getStackTraceStringFromFramesArray(trace.split("\n"));
31+
}
32+
33+
private static String getStackTraceStringFromFramesArray(String[] frames) {
34+
int maxStackSize = getMaxStackSize();
35+
36+
//We need to truncate the stacktrace based on the desired max stacktrace size, as well as remove the first line
37+
//of the returned trace (the "message"), since this is already captured in the error.message attribute.
38+
if (frames.length > maxStackSize) {
39+
frames = Arrays.copyOfRange(frames, 1, maxStackSize + 1);
40+
} else {
41+
frames = Arrays.copyOfRange(frames, 1, frames.length);
3342
}
34-
return stackBuilder.toString();
43+
44+
return String.join("\n", frames).replace("\tat", " at") + "\n";
3545
}
3646
}

core/src/main/java/com/newrelic/logging/core/LogExtensionConfig.java

+8-4
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ public class LogExtensionConfig {
1616
public static final boolean ADD_MDC_DEFAULT = false;
1717

1818
/**
19-
* Get an int representing the max stack size for errors that should be added to logs
19+
* Get an int representing the max stack size for errors that should be added to logs. Explicitly setting the
20+
* value to 0 will not impose a maximum size constraint on the stack trace size.
2021
* <p>
2122
* Precedence: Env var > Sys prop > Default
2223
*
@@ -25,14 +26,17 @@ public class LogExtensionConfig {
2526
public static int getMaxStackSize() {
2627
String envVar = System.getenv(MAX_STACK_SIZE_ENV_VAR);
2728
String sysProp = System.getProperty(MAX_STACK_SIZE_SYS_PROP);
29+
int parsedValue;
2830

2931
if (isInteger(envVar)) {
30-
return Integer.parseInt(envVar);
32+
parsedValue = Integer.parseInt(envVar);
3133
} else if (isInteger(sysProp)) {
32-
return Integer.parseInt(sysProp);
34+
parsedValue = Integer.parseInt(sysProp);
3335
} else {
34-
return ExceptionUtil.MAX_STACK_SIZE_DEFAULT;
36+
parsedValue = ExceptionUtil.MAX_STACK_SIZE_DEFAULT;
3537
}
38+
39+
return (parsedValue == 0 ? Integer.MAX_VALUE : parsedValue);
3640
}
3741

3842
/**

core/src/test/java/com/newrelic/logging/core/LogExtensionConfigTest.java

+12
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,18 @@
2020

2121
class LogExtensionConfigTest {
2222

23+
@Test
24+
void getMaxStackSize_withValueOfZeroAndSetAsSysProp_returnsMaxInt() {
25+
System.setProperty(MAX_STACK_SIZE_SYS_PROP, String.valueOf(0));
26+
assertEquals(Integer.MAX_VALUE, LogExtensionConfig.getMaxStackSize());
27+
System.clearProperty(MAX_STACK_SIZE_SYS_PROP);
28+
}
29+
30+
@Test
31+
void getMaxStackSize_withValueOfZeroAndSetAsEnvVar_returnsMaxInt() throws Exception {
32+
assertEquals(Integer.MAX_VALUE, withEnvironmentVariable(MAX_STACK_SIZE_ENV_VAR, String.valueOf(0)).execute(LogExtensionConfig::getMaxStackSize));
33+
}
34+
2335
@Test
2436
void testGetMaxStackSizeDefault() {
2537
int actualSize = LogExtensionConfig.getMaxStackSize();

0 commit comments

Comments
 (0)