Skip to content

Commit a685ce8

Browse files
authored
feature/configurable-logging-causes (#3663)
- Kan nå konfigurere eller slå av lengde før stack trace trunkeres. Default til 480 hvis ikke satt (som før, men legger nå på " (truncated)..." i tillegg). - Kan nå legge til causes på stack trace. Default til false hvis ikke satt (som før). - Kan nå filtrere på elementer i stack trace. Default til alle hvis ikke satt (som før).
1 parent 2bc7711 commit a685ce8

File tree

2 files changed

+158
-48
lines changed

2 files changed

+158
-48
lines changed

libs/reactive-core/src/main/java/no/nav/testnav/libs/reactivecore/logging/TestnavLogbackEncoder.java

+79-24
Original file line numberDiff line numberDiff line change
@@ -3,58 +3,113 @@
33
import ch.qos.logback.classic.spi.ILoggingEvent;
44
import ch.qos.logback.classic.spi.ThrowableProxy;
55
import com.fasterxml.jackson.core.JsonFactory;
6+
import lombok.Setter;
67
import lombok.SneakyThrows;
78
import lombok.extern.slf4j.Slf4j;
89
import net.logstash.logback.encoder.LogstashEncoder;
10+
import org.springframework.util.StringUtils;
911

1012
import java.io.ByteArrayOutputStream;
11-
import java.io.PrintWriter;
1213
import java.io.StringWriter;
1314
import java.util.regex.Pattern;
1415

16+
import static java.util.Objects.isNull;
1517
import static java.util.Objects.nonNull;
16-
18+
import static org.springframework.util.StringUtils.truncate;
19+
20+
/**
21+
* Config:
22+
* <ul>
23+
* <li>{@code maxStackTraceLength}: Default 480, set to <1 to disable truncation of stack trace altogether.</li>
24+
* <li>{@code addCauses}: If enabled, adds the cause(s) to the stack trace (with stack traces for each cause) Truncated according to above, filtered according to below.</li>
25+
* <li>{@code stackTraceIncludePrefix}: If set, only include stack trace elements that starts with the given prefix, e.g. "no.nav.testnav".</li>
26+
* </ul>
27+
* Copy of {@code no.nav.testnav.libs.servletcore.logging.TestnavLogbackEncoder}.
28+
*
29+
* @see StringUtils#truncate(CharSequence, int)
30+
*/
1731
@Slf4j
32+
@SuppressWarnings("java:S110")
1833
public class TestnavLogbackEncoder extends LogstashEncoder {
1934

2035
// matches exactly 11 digits (\\d{11}) that are not immediately preceded ((?<!\\d)) or followed ((?!\\d)) by another digit.
2136
private final Pattern pattern = Pattern.compile("(?<!\\d)\\d{11}(?!\\d)");
2237

38+
@Setter
39+
private int maxStackTraceLength = 480;
40+
41+
@Setter
42+
private boolean addCauses = false;
43+
44+
@Setter
45+
private String stackTraceIncludePrefix = null;
46+
2347
@SneakyThrows
2448
@Override
2549
public byte[] encode(ILoggingEvent event) {
26-
var outputStream = new ByteArrayOutputStream();
2750

28-
var generator = new JsonFactory().createGenerator(outputStream);
29-
30-
generator.writeStartObject();
51+
var outputStream = new ByteArrayOutputStream();
52+
try (var generator = new JsonFactory().createGenerator(outputStream)) {
53+
54+
generator.writeStartObject();
55+
generator.writeStringField("@timestamp", new java.text.SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSSSSSSSXXX")
56+
.format(new java.util.Date(event.getTimeStamp())));
57+
generator.writeStringField("message", formatMessage(event.getFormattedMessage()));
58+
generator.writeStringField("logger_name", event.getLoggerName());
59+
generator.writeStringField("thread_name", event.getThreadName());
60+
generator.writeStringField("level", event.getLevel().toString());
61+
generator.writeStringField("stack_trace", getStackTrace(event));
62+
generator.writeEndObject();
63+
64+
generator.flush();
65+
outputStream.write('\n');
66+
}
3167

32-
generator.writeStringField("@timestamp", new java.text.SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSSSSSSSXXX")
33-
.format(new java.util.Date(event.getTimeStamp())));
34-
generator.writeStringField("message", formatMessage(event.getFormattedMessage()));
35-
generator.writeStringField("logger_name", event.getLoggerName());
36-
generator.writeStringField("thread_name", event.getThreadName());
37-
generator.writeStringField("level", event.getLevel().toString());
68+
return outputStream.toByteArray();
69+
}
3870

71+
private String getStackTrace(ILoggingEvent event) {
3972
if (nonNull(event.getThrowableProxy())) {
4073
var exception = (ThrowableProxy) event.getThrowableProxy();
4174
if (nonNull(exception.getThrowable())) {
42-
var sw = new StringWriter();
43-
var pw = new PrintWriter(sw);
44-
for (StackTraceElement element : exception.getThrowable().getStackTrace()) {
45-
pw.println("\tat " + element);
46-
}
47-
var stackTrace = sw.toString().substring(0, 480); //Limit the stack trace to 480 characters
48-
generator.writeStringField("stack_trace", stackTrace);
75+
var writer = new StringWriter();
76+
appendStackTraceElements(exception.getThrowable().getStackTrace(), writer);
77+
appendStackTraceCauses(exception, writer);
78+
return maxStackTraceLength > 0 ?
79+
truncate(writer.toString(), maxStackTraceLength) :
80+
writer.toString();
4981
}
5082
}
83+
return null;
84+
}
5185

52-
generator.writeEndObject();
53-
54-
generator.flush();
55-
outputStream.write('\n');
86+
private void appendStackTraceElements(StackTraceElement[] elements, StringWriter writer) {
87+
for (StackTraceElement element : elements) {
88+
if (isNull(stackTraceIncludePrefix) || stackTraceIncludePrefix.isEmpty() || element.toString().startsWith(stackTraceIncludePrefix)) {
89+
writer
90+
.append("\tat ")
91+
.append(element.toString())
92+
.append("\n");
93+
}
94+
}
95+
}
5696

57-
return outputStream.toByteArray();
97+
private void appendStackTraceCauses(ThrowableProxy exception, StringWriter writer) {
98+
if (addCauses) {
99+
var cause = exception;
100+
while (!isNull(cause.getCause())) {
101+
cause = (ThrowableProxy) cause.getCause();
102+
writer
103+
.append("caused by ")
104+
.append(cause.getClassName())
105+
.append(": ")
106+
.append(cause.getMessage())
107+
.append("\n");
108+
if (!isNull(cause.getThrowable())) {
109+
appendStackTraceElements(cause.getThrowable().getStackTrace(), writer);
110+
}
111+
}
112+
}
58113
}
59114

60115
private String formatMessage(String message) {

libs/servlet-core/src/main/java/no/nav/testnav/libs/servletcore/logging/TestnavLogbackEncoder.java

+79-24
Original file line numberDiff line numberDiff line change
@@ -3,58 +3,113 @@
33
import ch.qos.logback.classic.spi.ILoggingEvent;
44
import ch.qos.logback.classic.spi.ThrowableProxy;
55
import com.fasterxml.jackson.core.JsonFactory;
6+
import lombok.Setter;
67
import lombok.SneakyThrows;
78
import lombok.extern.slf4j.Slf4j;
89
import net.logstash.logback.encoder.LogstashEncoder;
10+
import org.springframework.util.StringUtils;
911

1012
import java.io.ByteArrayOutputStream;
11-
import java.io.PrintWriter;
1213
import java.io.StringWriter;
1314
import java.util.regex.Pattern;
1415

16+
import static java.util.Objects.isNull;
1517
import static java.util.Objects.nonNull;
16-
18+
import static org.springframework.util.StringUtils.truncate;
19+
20+
/**
21+
* Config:
22+
* <ul>
23+
* <li>{@code maxStackTraceLength}: Default 480, set to <1 to disable truncation of stack trace altogether.</li>
24+
* <li>{@code addCauses}: If enabled, adds the cause(s) to the stack trace (with stack traces for each cause) Truncated according to above, filtered according to below.</li>
25+
* <li>{@code stackTraceIncludePrefix}: If set, only include stack trace elements that starts with the given prefix, e.g. "no.nav.testnav".</li>
26+
* </ul>
27+
* Copy of {@code no.nav.testnav.libs.servletcore.logging.TestnavLogbackEncoder}.
28+
*
29+
* @see StringUtils#truncate(CharSequence, int)
30+
*/
1731
@Slf4j
32+
@SuppressWarnings("java:S110")
1833
public class TestnavLogbackEncoder extends LogstashEncoder {
1934

2035
// matches exactly 11 digits (\\d{11}) that are not immediately preceded ((?<!\\d)) or followed ((?!\\d)) by another digit.
2136
private final Pattern pattern = Pattern.compile("(?<!\\d)\\d{11}(?!\\d)");
2237

38+
@Setter
39+
private int maxStackTraceLength = 480;
40+
41+
@Setter
42+
private boolean addCauses = false;
43+
44+
@Setter
45+
private String stackTraceIncludePrefix = null;
46+
2347
@SneakyThrows
2448
@Override
2549
public byte[] encode(ILoggingEvent event) {
26-
var outputStream = new ByteArrayOutputStream();
2750

28-
var generator = new JsonFactory().createGenerator(outputStream);
29-
30-
generator.writeStartObject();
51+
var outputStream = new ByteArrayOutputStream();
52+
try (var generator = new JsonFactory().createGenerator(outputStream)) {
53+
54+
generator.writeStartObject();
55+
generator.writeStringField("@timestamp", new java.text.SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSSSSSSSXXX")
56+
.format(new java.util.Date(event.getTimeStamp())));
57+
generator.writeStringField("message", formatMessage(event.getFormattedMessage()));
58+
generator.writeStringField("logger_name", event.getLoggerName());
59+
generator.writeStringField("thread_name", event.getThreadName());
60+
generator.writeStringField("level", event.getLevel().toString());
61+
generator.writeStringField("stack_trace", getStackTrace(event));
62+
generator.writeEndObject();
63+
64+
generator.flush();
65+
outputStream.write('\n');
66+
}
3167

32-
generator.writeStringField("@timestamp", new java.text.SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSSSSSSSXXX")
33-
.format(new java.util.Date(event.getTimeStamp())));
34-
generator.writeStringField("message", formatMessage(event.getFormattedMessage()));
35-
generator.writeStringField("logger_name", event.getLoggerName());
36-
generator.writeStringField("thread_name", event.getThreadName());
37-
generator.writeStringField("level", event.getLevel().toString());
68+
return outputStream.toByteArray();
69+
}
3870

71+
private String getStackTrace(ILoggingEvent event) {
3972
if (nonNull(event.getThrowableProxy())) {
4073
var exception = (ThrowableProxy) event.getThrowableProxy();
4174
if (nonNull(exception.getThrowable())) {
42-
var sw = new StringWriter();
43-
var pw = new PrintWriter(sw);
44-
for (StackTraceElement element : exception.getThrowable().getStackTrace()) {
45-
pw.println("\tat " + element);
46-
}
47-
var stackTrace = sw.toString().substring(0, 480); //Limit the stack trace to 480 characters
48-
generator.writeStringField("stack_trace", stackTrace);
75+
var writer = new StringWriter();
76+
appendStackTraceElements(exception.getThrowable().getStackTrace(), writer);
77+
appendStackTraceCauses(exception, writer);
78+
return maxStackTraceLength > 0 ?
79+
truncate(writer.toString(), maxStackTraceLength) :
80+
writer.toString();
4981
}
5082
}
83+
return null;
84+
}
5185

52-
generator.writeEndObject();
53-
54-
generator.flush();
55-
outputStream.write('\n');
86+
private void appendStackTraceElements(StackTraceElement[] elements, StringWriter writer) {
87+
for (StackTraceElement element : elements) {
88+
if (isNull(stackTraceIncludePrefix) || stackTraceIncludePrefix.isEmpty() || element.toString().startsWith(stackTraceIncludePrefix)) {
89+
writer
90+
.append("\tat ")
91+
.append(element.toString())
92+
.append("\n");
93+
}
94+
}
95+
}
5696

57-
return outputStream.toByteArray();
97+
private void appendStackTraceCauses(ThrowableProxy exception, StringWriter writer) {
98+
if (addCauses) {
99+
var cause = exception;
100+
while (!isNull(cause.getCause())) {
101+
cause = (ThrowableProxy) cause.getCause();
102+
writer
103+
.append("caused by ")
104+
.append(cause.getClassName())
105+
.append(": ")
106+
.append(cause.getMessage())
107+
.append("\n");
108+
if (!isNull(cause.getThrowable())) {
109+
appendStackTraceElements(cause.getThrowable().getStackTrace(), writer);
110+
}
111+
}
112+
}
58113
}
59114

60115
private String formatMessage(String message) {

0 commit comments

Comments
 (0)