|
3 | 3 | import ch.qos.logback.classic.spi.ILoggingEvent;
|
4 | 4 | import ch.qos.logback.classic.spi.ThrowableProxy;
|
5 | 5 | import com.fasterxml.jackson.core.JsonFactory;
|
| 6 | +import lombok.Setter; |
6 | 7 | import lombok.SneakyThrows;
|
7 | 8 | import lombok.extern.slf4j.Slf4j;
|
8 | 9 | import net.logstash.logback.encoder.LogstashEncoder;
|
| 10 | +import org.springframework.util.StringUtils; |
9 | 11 |
|
10 | 12 | import java.io.ByteArrayOutputStream;
|
11 |
| -import java.io.PrintWriter; |
12 | 13 | import java.io.StringWriter;
|
13 | 14 | import java.util.regex.Pattern;
|
14 | 15 |
|
| 16 | +import static java.util.Objects.isNull; |
15 | 17 | 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 | + */ |
17 | 31 | @Slf4j
|
| 32 | +@SuppressWarnings("java:S110") |
18 | 33 | public class TestnavLogbackEncoder extends LogstashEncoder {
|
19 | 34 |
|
20 | 35 | // matches exactly 11 digits (\\d{11}) that are not immediately preceded ((?<!\\d)) or followed ((?!\\d)) by another digit.
|
21 | 36 | private final Pattern pattern = Pattern.compile("(?<!\\d)\\d{11}(?!\\d)");
|
22 | 37 |
|
| 38 | + @Setter |
| 39 | + private int maxStackTraceLength = 480; |
| 40 | + |
| 41 | + @Setter |
| 42 | + private boolean addCauses = false; |
| 43 | + |
| 44 | + @Setter |
| 45 | + private String stackTraceIncludePrefix = null; |
| 46 | + |
23 | 47 | @SneakyThrows
|
24 | 48 | @Override
|
25 | 49 | public byte[] encode(ILoggingEvent event) {
|
26 |
| - var outputStream = new ByteArrayOutputStream(); |
27 | 50 |
|
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 | + } |
31 | 67 |
|
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 | + } |
38 | 70 |
|
| 71 | + private String getStackTrace(ILoggingEvent event) { |
39 | 72 | if (nonNull(event.getThrowableProxy())) {
|
40 | 73 | var exception = (ThrowableProxy) event.getThrowableProxy();
|
41 | 74 | 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(); |
49 | 81 | }
|
50 | 82 | }
|
| 83 | + return null; |
| 84 | + } |
51 | 85 |
|
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 | + } |
56 | 96 |
|
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 | + } |
58 | 113 | }
|
59 | 114 |
|
60 | 115 | private String formatMessage(String message) {
|
|
0 commit comments