Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make it possible to pretty print throwables as array of strings. #1043

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1568,6 +1568,18 @@ For example:
See the [net.logstash.logback.decorate](/src/main/java/net/logstash/logback/decorate) package
and sub-packages for other decorators.

If you prefer pretty printing for easier interactive viewing of (error) logs, you
may also prefer to output throwable as json array of strings where each string
is a stacktrace line:

```xml
<encoder class="net.logstash.logback.encoder.LogstashEncoder">
<jsonGeneratorDecorator class="net.logstash.logback.decorate.PrettyPrintingJsonGeneratorDecorator">
<indentArraysWithNewLine>true</indentArraysWithNewLine>
<outputThrowableAsArray>true</outputThrowableAsArray>
</encoder>
```

### Registering Jackson Modules

By default, Jackson modules are dynamically registered via
Expand Down Expand Up @@ -2766,6 +2778,7 @@ The provider name is the xml element name to use when configuring. Each provider
<ul>
<li><tt>fieldName</tt> - Output field name (<tt>stack_trace</tt>)</li>
<li><tt>throwableConverter</tt> - The <tt>ThrowableHandlingConverter</tt> to use to format the stacktrace (<tt>stack_trace</tt>)</li>
<li><tt>outputThrowableAsArray</tt> - Output throwable as json array of strings where each string is a stacktrace line</li>
</ul>
</td>
</tr>
Expand Down
8 changes: 8 additions & 0 deletions src/main/java/net/logstash/logback/LogstashFormatter.java
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,14 @@ public void setThrowableConverter(ThrowableHandlingConverter throwableConverter)
this.stackTraceProvider.setThrowableConverter(throwableConverter);
}

public boolean isOutputThrowableAsArray() {
return this.stackTraceProvider.isOutputThrowableAsArray();
}

public void setOutputThrowableAsArray(boolean outputAsArray) {
this.stackTraceProvider.setOutputThrowableAsArray(outputAsArray);
}

public String getVersion() {
return this.versionProvider.getVersion();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@ public class StackTraceJsonProvider extends AbstractFieldJsonProvider<ILoggingEv

public static final String FIELD_STACK_TRACE = "stack_trace";

/**
* If true, stacktrace will be output as a json array of strings split by newlines
* If else, stacktrace will be output as a json string
*/
private boolean outputThrowableAsArray = false;

/**
* Used to format throwables as Strings.
*
Expand All @@ -47,6 +53,14 @@ public StackTraceJsonProvider() {
setFieldName(FIELD_STACK_TRACE);
}

public boolean isOutputThrowableAsArray() {
return outputThrowableAsArray;
}

public void setOutputThrowableAsArray(boolean outputThrowableAsArray) {
this.outputThrowableAsArray = outputThrowableAsArray;
}

@Override
public void start() {
this.throwableConverter.start();
Expand All @@ -62,8 +76,15 @@ public void stop() {
@Override
public void writeTo(JsonGenerator generator, ILoggingEvent event) throws IOException {
IThrowableProxy throwableProxy = event.getThrowableProxy();
if (throwableProxy != null) {
JsonWritingUtils.writeStringField(generator, getFieldName(), throwableConverter.convert(event));
if (throwableProxy == null) {
return;
}
String stacktrace = throwableConverter.convert(event);
if (outputThrowableAsArray) {
String[] lines = stacktrace.split("\n");
JsonWritingUtils.writeStringArrayField(generator, getFieldName(), lines);
} else {
JsonWritingUtils.writeStringField(generator, getFieldName(), stacktrace);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import ch.qos.logback.core.CoreConstants;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.util.DefaultIndenter;
import com.fasterxml.jackson.core.util.DefaultPrettyPrinter;

/**
Expand Down Expand Up @@ -62,4 +63,12 @@ public void setSpacesInObjectEntries(boolean spacesInObjectEntries) {
prettyPrinter = prettyPrinter.withoutSpacesInObjectEntries();
}
}

public void setIndentArraysWithNewLine(boolean indentArraysWithNewLine) {
if (indentArraysWithNewLine) {
prettyPrinter.indentArraysWith(DefaultIndenter.SYSTEM_LINEFEED_INSTANCE);
} else {
prettyPrinter.indentArraysWith(null);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,14 @@ public void setThrowableConverter(ThrowableHandlingConverter throwableConverter)
getFormatter().setThrowableConverter(throwableConverter);
}

public boolean isOutputThrowableAsArray() {
return getFormatter().isOutputThrowableAsArray();
}

public void setOutputThrowableAsArray(boolean outputAsArray) {
this.getFormatter().setOutputThrowableAsArray(outputAsArray);
}

public String getTimeZone() {
return getFormatter().getTimeZone();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
*/
package net.logstash.logback.composite.loggingevent;

import static org.mockito.Mockito.anyString;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

Expand All @@ -26,6 +28,7 @@
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.classic.spi.IThrowableProxy;
import com.fasterxml.jackson.core.JsonGenerator;
import org.assertj.core.util.Throwables;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
Expand Down Expand Up @@ -90,4 +93,21 @@ public void testFieldNames() throws IOException {
verify(generator).writeStringField("newFieldName", "stack");
}

@Test
public void testOutputAsArray() throws IOException {
String stacktrace = Throwables.getStackTrace(new RuntimeException("testing exception handling"));
when(converter.convert(event)).thenReturn(stacktrace);

provider.setOutputThrowableAsArray(true);

when(event.getThrowableProxy()).thenReturn(ThrowableProxy);

provider.writeTo(generator, event);

verify(generator).writeArrayFieldStart("stack_trace");
verify(generator).writeString("java.lang.RuntimeException: testing exception handling");
verify(generator, atLeastOnce()).writeString(anyString());
verify(generator).writeEndArray();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

import java.io.IOException;
import java.io.StringWriter;
import java.util.Arrays;
import java.util.Collections;

import com.fasterxml.jackson.core.JsonGenerator;
Expand Down Expand Up @@ -94,4 +95,29 @@ void noSpacesInObjectEntries() throws IOException {
writer.flush();
assertThat(writer.toString()).isEqualTo("{\n \"key1\":\"value1\"\n}{\n \"key2\":\"value2\"\n}");
}

@Test
void arrayElementsOnNewLine() throws IOException {
PrettyPrintingJsonGeneratorDecorator decorator = new PrettyPrintingJsonGeneratorDecorator();
decorator.setIndentArraysWithNewLine(true);

StringWriter writer = new StringWriter();
ObjectMapper objectMapper = new ObjectMapper();
JsonGenerator generator = decorator.decorate(objectMapper.createGenerator(writer));

generator.writeObject(Collections.singletonMap("key1", Arrays.asList("RuntimeException: foobar",
"\tat com.example.Foobar")));
generator.writeObject(Collections.singletonMap("key2", "value2"));

generator.flush();
writer.flush();
assertThat(writer.toString()).isEqualTo("{\n" +
" \"key1\" : [\n" +
" \"RuntimeException: foobar\",\n" +
" \"\\tat com.example.Foobar\"\n" +
" ]\n" +
"}{\n" +
" \"key2\" : \"value2\"\n" +
"}");
}
}