Skip to content

Commit 27ec094

Browse files
Add Exception Type and Message to JSON Logs
Adds the two fields exception_type and exception_message to the BaseFieldSuppliers for Log4j and Logback. This allows users to override the provided values with custom FieldSuppliers. Basic Unit Tests were added. Performance implications should be minimal for events without exceptions. If there are exceptions, the only longer check is for the message to be non-blank. Signed-off-by: Karsten Schnitter <[email protected]>
1 parent 4af10c3 commit 27ec094

File tree

4 files changed

+124
-0
lines changed

4 files changed

+124
-0
lines changed

cf-java-logging-support-log4j2/src/main/java/com/sap/hcp/cf/log4j2/layout/supppliers/BaseFieldSupplier.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import java.util.HashMap;
55
import java.util.Map;
66

7+
import org.apache.commons.lang3.StringUtils;
78
import org.apache.logging.log4j.core.LogEvent;
89

910
import com.sap.hcp.cf.log4j2.converter.api.Log4jContextFieldSupplier;
@@ -24,6 +25,13 @@ public Map<String, Object> map(LogEvent event) {
2425
if (!LogEventUtilities.isRequestLog(event) && event.getMessage() != null) {
2526
fields.put(Fields.MSG, LogEventUtilities.getFormattedMessage(event));
2627
}
28+
if (event.getThrown() != null) {
29+
Throwable throwable = event.getThrown();
30+
fields.put(Fields.EXCEPTION_TYPE, throwable.getClass().getName());
31+
if (StringUtils.isNotBlank(throwable.getMessage())) {
32+
fields.put(Fields.EXCEPTION_MESSAGE, throwable.getMessage());
33+
}
34+
}
2735
return fields;
2836
}
2937

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package com.sap.hcp.cf.log4j2.layout.suppliers;
2+
3+
import com.sap.hcp.cf.log4j2.converter.api.Log4jContextFieldSupplier;
4+
import com.sap.hcp.cf.log4j2.layout.supppliers.BaseFieldSupplier;
5+
import com.sap.hcp.cf.logging.common.Fields;
6+
import org.apache.logging.log4j.core.LogEvent;
7+
import org.apache.logging.log4j.core.time.Instant;
8+
import org.junit.Before;
9+
import org.junit.Test;
10+
import org.junit.runner.RunWith;
11+
import org.mockito.Mock;
12+
import org.mockito.Mockito;
13+
import org.mockito.runners.MockitoJUnitRunner;
14+
15+
import java.time.Clock;
16+
import java.util.Map;
17+
18+
import static org.hamcrest.MatcherAssert.assertThat;
19+
import static org.junit.Assert.*;
20+
21+
import static org.hamcrest.Matchers.hasEntry;
22+
import static org.hamcrest.MatcherAssert.*;
23+
import static org.hamcrest.Matchers.*;
24+
import static org.mockito.Mockito.mock;
25+
import static org.mockito.Mockito.when;
26+
27+
@RunWith(MockitoJUnitRunner.class)
28+
public class BaseFieldSupplierTest {
29+
30+
@Mock
31+
private LogEvent event;
32+
33+
private Log4jContextFieldSupplier baseFieldSupplier = new BaseFieldSupplier();
34+
35+
@Before
36+
public void initializeEvent() {
37+
when(event.getInstant()).thenReturn(mock(Instant.class));
38+
}
39+
40+
@Test
41+
public void addsNoExceptionFieldsWithoutException() {
42+
Map<String, Object> fields = baseFieldSupplier.map(event);
43+
44+
assertThat(fields, not(hasKey(Fields.EXCEPTION_TYPE)));
45+
assertThat(fields, not(hasKey(Fields.EXCEPTION_MESSAGE)));
46+
}
47+
48+
@Test
49+
public void mapsException() {
50+
Exception exception = new RuntimeException("exception message");
51+
when(event.getThrown()).thenReturn(exception);
52+
53+
Map<String, Object> fields = baseFieldSupplier.map(event);
54+
55+
assertThat(fields, hasEntry(Fields.EXCEPTION_TYPE, RuntimeException.class.getName()));
56+
assertThat(fields, hasEntry(Fields.EXCEPTION_MESSAGE, "exception message"));
57+
}
58+
}

cf-java-logging-support-logback/src/main/java/com/sap/hcp/cf/logback/encoder/BaseFieldSupplier.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,14 @@
44
import java.util.HashMap;
55
import java.util.Map;
66

7+
import ch.qos.logback.classic.spi.ThrowableProxy;
78
import com.sap.hcp.cf.logback.converter.api.LogbackContextFieldSupplier;
89
import com.sap.hcp.cf.logging.common.Defaults;
910
import com.sap.hcp.cf.logging.common.Fields;
1011
import com.sap.hcp.cf.logging.common.Markers;
1112

1213
import ch.qos.logback.classic.spi.ILoggingEvent;
14+
import org.apache.commons.lang3.StringUtils;
1315

1416
public class BaseFieldSupplier implements LogbackContextFieldSupplier {
1517

@@ -25,6 +27,13 @@ public Map<String, Object> map(ILoggingEvent event) {
2527
if (!isRequestLog(event)) {
2628
fields.put(Fields.MSG, event.getFormattedMessage());
2729
}
30+
if (event.getThrowableProxy() != null && event.getThrowableProxy() instanceof ThrowableProxy) {
31+
Throwable throwable = ((ThrowableProxy) event.getThrowableProxy()).getThrowable();
32+
fields.put(Fields.EXCEPTION_TYPE, throwable.getClass().getName());
33+
if (StringUtils.isNotBlank(throwable.getMessage())) {
34+
fields.put(Fields.EXCEPTION_MESSAGE, throwable.getMessage());
35+
}
36+
}
2837
return fields;
2938
}
3039

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package com.sap.hcp.cf.logback.encoder;
2+
3+
import ch.qos.logback.classic.spi.ILoggingEvent;
4+
import ch.qos.logback.classic.spi.ThrowableProxy;
5+
import com.sap.hcp.cf.logback.converter.api.LogbackContextFieldSupplier;
6+
import com.sap.hcp.cf.logging.common.Fields;
7+
import org.apache.commons.math3.stat.inference.GTest;
8+
import org.hamcrest.MatcherAssert;
9+
import org.hamcrest.Matchers;
10+
import org.junit.Test;
11+
import org.junit.runner.RunWith;
12+
import org.mockito.Mock;
13+
import org.mockito.Mockito;
14+
import org.mockito.runners.MockitoJUnitRunner;
15+
16+
import java.util.Map;
17+
18+
import static org.hamcrest.Matchers.hasEntry;
19+
import static org.hamcrest.MatcherAssert.*;
20+
import static org.hamcrest.Matchers.*;
21+
import static org.mockito.Mockito.when;
22+
23+
@RunWith(MockitoJUnitRunner.class)
24+
public class BaseFieldSupplierTest {
25+
26+
@Mock
27+
private ILoggingEvent event;
28+
29+
private LogbackContextFieldSupplier baseFieldSupplier = new BaseFieldSupplier();
30+
31+
@Test
32+
public void addsNoExceptionFieldsWithoutException() {
33+
Map<String, Object> fields = baseFieldSupplier.map(event);
34+
35+
assertThat(fields, not(hasKey(Fields.EXCEPTION_TYPE)));
36+
assertThat(fields, not(hasKey(Fields.EXCEPTION_MESSAGE)));
37+
}
38+
39+
@Test
40+
public void mapsException() {
41+
Exception exception = new RuntimeException("exception message");
42+
when(event.getThrowableProxy()).thenReturn(new ThrowableProxy(exception));
43+
44+
Map<String, Object> fields = baseFieldSupplier.map(event);
45+
46+
assertThat(fields, hasEntry(Fields.EXCEPTION_TYPE, RuntimeException.class.getName()));
47+
assertThat(fields, hasEntry(Fields.EXCEPTION_MESSAGE, "exception message"));
48+
}
49+
}

0 commit comments

Comments
 (0)