Skip to content

Commit 83b2543

Browse files
committed
Add includeOrigin flag
depends on elastic/ecs#563 closes #3
1 parent 910bea9 commit 83b2543

File tree

14 files changed

+105
-21
lines changed

14 files changed

+105
-21
lines changed

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,9 @@ We recommend using this library to log into a JSON log file and let Filebeat sen
8181
|[`@timestamp`](https://www.elastic.co/guide/en/ecs/current/ecs-base.html) | [`LogEvent#getTimeMillis()`](https://logging.apache.org/log4j/log4j-2.3/log4j-core/apidocs/org/apache/logging/log4j/core/LogEvent.html#getTimeMillis()) |
8282
|[`log.level`](https://www.elastic.co/guide/en/ecs/current/ecs-log.html) | [`LogEvent#getLevel()`](https://logging.apache.org/log4j/log4j-2.3/log4j-core/apidocs/org/apache/logging/log4j/core/LogEvent.html#getLevel()) |
8383
|[`log.logger`](https://www.elastic.co/guide/en/ecs/current/ecs-log.html)|[`LogEvent#getLoggerName()`](https://logging.apache.org/log4j/log4j-2.3/log4j-core/apidocs/org/apache/logging/log4j/core/LogEvent.html#getLoggerName())|
84+
|[`log.origin.file`](https://www.elastic.co/guide/en/ecs/current/ecs-log.html)|[StackTraceElement#getFileName()](https://docs.oracle.com/javase/6/docs/api/java/lang/StackTraceElement.html#getFileName())|
85+
|[`log.origin.function`](https://www.elastic.co/guide/en/ecs/current/ecs-log.html)|[`[StackTraceElement#getMethodName()]()`](https://docs.oracle.com/javase/6/docs/api/java/lang/StackTraceElement.html#getMethodName())|
86+
|[`log.origin.line`](https://www.elastic.co/guide/en/ecs/current/ecs-log.html)|[`[StackTraceElement#getLineNumber()]()`](https://docs.oracle.com/javase/6/docs/api/java/lang/StackTraceElement.html#getLineNumber())|
8487
|[`message`](https://www.elastic.co/guide/en/ecs/current/ecs-base.html)|[`LogEvent#getMessage()`](https://logging.apache.org/log4j/log4j-2.3/log4j-core/apidocs/org/apache/logging/log4j/core/LogEvent.html#getMessage())|
8588
|[`error.code`](https://www.elastic.co/guide/en/ecs/current/ecs-error.html)|[`Throwable#getClass()`](https://docs.oracle.com/javase/7/docs/api/java/lang/Object.html#getClass())|
8689
|[`error.message`](https://www.elastic.co/guide/en/ecs/current/ecs-error.html)|[`Throwable#getStackTrace()`](https://docs.oracle.com/javase/7/docs/api/java/lang/Throwable.html#getMessage())|

ecs-logging-core/src/main/java/co/elastic/logging/EcsJsonSerializer.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,25 @@ public static void serializeTagEnd(StringBuilder builder) {
119119
builder.append("],");
120120
}
121121

122+
public static void serializeOrigin(StringBuilder builder, StackTraceElement stackTraceElement) {
123+
if (stackTraceElement != null) {
124+
serializeOrigin(builder, stackTraceElement.getFileName(), stackTraceElement.getMethodName(), stackTraceElement.getLineNumber());
125+
}
126+
}
127+
128+
public static void serializeOrigin(StringBuilder builder, String fileName, String methodName, int lineNumber) {
129+
builder.append("\"log.origin\":{");
130+
builder.append("\"file\":\"");
131+
JsonUtils.quoteAsString(fileName, builder);
132+
builder.append("\",");
133+
builder.append("\"function\":\"");
134+
JsonUtils.quoteAsString(methodName, builder);
135+
builder.append("\",");
136+
builder.append("\"line\":");
137+
builder.append(lineNumber);
138+
builder.append("},");
139+
}
140+
122141
public static void serializeLabels(StringBuilder builder, Map<String, ?> labels, Set<String> topLevelLabels) {
123142
if (!labels.isEmpty()) {
124143
for (Map.Entry<String, ?> entry : labels.entrySet()) {

ecs-logging-core/src/test/java/co/elastic/logging/AbstractEcsLoggingTest.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,14 @@ void testLogException() throws Exception {
9393
assertThat(stackTrace).contains("at co.elastic.logging.AbstractEcsLoggingTest.testLogException");
9494
}
9595

96+
@Test
97+
void testLogOrigin() throws Exception {
98+
debug("test");
99+
assertThat(getLastLogLine().get("log.origin").get("file").textValue()).endsWith(".java");
100+
assertThat(getLastLogLine().get("log.origin").get("function").textValue()).isEqualTo("debug");
101+
assertThat(getLastLogLine().get("log.origin").get("line").intValue()).isPositive();
102+
}
103+
96104
public abstract void putMdc(String key, String value);
97105

98106
public boolean putNdc(String message) {

log4j-ecs-layout/src/main/java/co/elastic/logging/log4j/EcsLayout.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626

2727
import co.elastic.logging.EcsJsonSerializer;
2828
import org.apache.log4j.Layout;
29+
import org.apache.log4j.spi.LocationInfo;
2930
import org.apache.log4j.spi.LoggingEvent;
3031
import org.apache.log4j.spi.ThrowableInformation;
3132

@@ -37,6 +38,7 @@ public class EcsLayout extends Layout {
3738
private boolean stackTraceAsArray = false;
3839
private String serviceName;
3940
private Set<String> topLevelLabels = new HashSet<String>(EcsJsonSerializer.DEFAULT_TOP_LEVEL_LABELS);
41+
private boolean includeOrigin;
4042

4143
@Override
4244
public String format(LoggingEvent event) {
@@ -49,6 +51,12 @@ public String format(LoggingEvent event) {
4951
EcsJsonSerializer.serializeLoggerName(builder, event.getLoggerName());
5052
EcsJsonSerializer.serializeLabels(builder, event.getProperties(), topLevelLabels);
5153
EcsJsonSerializer.serializeTag(builder, event.getNDC());
54+
if (includeOrigin) {
55+
LocationInfo locationInformation = event.getLocationInformation();
56+
if (locationInformation != null) {
57+
EcsJsonSerializer.serializeOrigin(builder, locationInformation.getFileName(), locationInformation.getMethodName(), getLineNumber(locationInformation));
58+
}
59+
}
5260
ThrowableInformation throwableInformation = event.getThrowableInformation();
5361
if (throwableInformation != null) {
5462
EcsJsonSerializer.serializeException(builder, throwableInformation.getThrowable(), stackTraceAsArray);
@@ -57,6 +65,19 @@ public String format(LoggingEvent event) {
5765
return builder.toString();
5866
}
5967

68+
private static int getLineNumber(LocationInfo locationInformation) {
69+
int lineNumber = -1;
70+
String lineNumberString = locationInformation.getLineNumber();
71+
if (!LocationInfo.NA.equals(lineNumberString)) {
72+
try {
73+
lineNumber = Integer.parseInt(lineNumberString);
74+
} catch (NumberFormatException e) {
75+
// ignore
76+
}
77+
}
78+
return lineNumber;
79+
}
80+
6081
@Override
6182
public boolean ignoresThrowable() {
6283
return false;
@@ -71,6 +92,10 @@ public void setServiceName(String serviceName) {
7192
this.serviceName = serviceName;
7293
}
7394

95+
public void setIncludeOrigin(boolean includeOrigin) {
96+
this.includeOrigin = includeOrigin;
97+
}
98+
7499
public void setStackTraceAsArray(boolean stackTraceAsArray) {
75100
this.stackTraceAsArray = stackTraceAsArray;
76101
}

log4j-ecs-layout/src/test/java/co/elastic/logging/log4j/ListAppender.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,11 @@
3131
import java.util.List;
3232

3333
class ListAppender extends AppenderSkeleton {
34-
private List<LoggingEvent> logEvents = new ArrayList<>();
34+
private List<String> logEvents = new ArrayList<>();
3535

3636
@Override
3737
protected void append(LoggingEvent event) {
38-
logEvents.add(event);
38+
logEvents.add(layout.format(event));
3939
}
4040

4141
@Override
@@ -45,10 +45,10 @@ public void close() {
4545

4646
@Override
4747
public boolean requiresLayout() {
48-
return false;
48+
return true;
4949
}
5050

51-
public List<LoggingEvent> getLogEvents() {
51+
public List<String> getLogEvents() {
5252
return logEvents;
5353
}
5454
}

log4j-ecs-layout/src/test/java/co/elastic/logging/log4j/Log4jEcsLayoutTest.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,11 @@
3131
import org.apache.log4j.MDC;
3232
import org.apache.log4j.NDC;
3333
import org.junit.jupiter.api.AfterEach;
34-
import org.junit.jupiter.api.Assertions;
3534
import org.junit.jupiter.api.Assumptions;
3635
import org.junit.jupiter.api.BeforeEach;
3736

3837
import java.io.IOException;
3938

40-
import static org.assertj.core.api.Assertions.assertThat;
41-
4239
class Log4jEcsLayoutTest extends AbstractEcsLoggingTest {
4340

4441
private Logger logger;
@@ -54,6 +51,8 @@ void setUp() {
5451
ecsLayout = new EcsLayout();
5552
ecsLayout.setServiceName("test");
5653
ecsLayout.setStackTraceAsArray(true);
54+
ecsLayout.setIncludeOrigin(true);
55+
appender.setLayout(ecsLayout);
5756
}
5857

5958
@BeforeEach
@@ -87,7 +86,7 @@ public void error(String message, Throwable t) {
8786

8887
@Override
8988
public JsonNode getLastLogLine() throws IOException {
90-
return objectMapper.readTree(ecsLayout.format(appender.getLogEvents().get(0)));
89+
return objectMapper.readTree(appender.getLogEvents().get(0));
9190
}
9291

9392
}

log4j2-ecs-layout/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ Instead of the usual `<PatternLayout/>`, use `<EcsLayout serviceName="my-app"/>`
4747
|serviceName |String | |Sets the `service.name` field so you can filter your logs by a particular service |
4848
|includeMarkers |boolean|`false`|Log [Markers](https://logging.apache.org/log4j/2.0/manual/markers.html) as `tags` |
4949
|stackTraceAsArray|boolean|`false`|Serializes the `error.stack_trace` as a JSON array where each element is in a new line to improve readability. Note that this requires a slightly more complex [Filebeat configuration](../README.md#when-stacktraceasarray-is-enabled).|
50+
|includeOrigin |boolean|`false`|If `true`, adds the `log.origin.file`, `log.origin.function` and `log.origin.line` fields. Note that you also have to set `includeLocation="true"` on your loggers and appenders if you are using the async ones. |
5051

5152
To include any custom field in the output, use following syntax:
5253

log4j2-ecs-layout/src/main/java/co/elastic/logging/log4j2/EcsLayout.java

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -80,15 +80,17 @@ public void accept(final String key, final Object value, final StringBuilder str
8080
private final KeyValuePair[] additionalFields;
8181
private final Set<String> topLevelLabels;
8282
private final boolean stackTraceAsArray;
83-
private String serviceName;
84-
private boolean includeMarkers;
83+
private final String serviceName;
84+
private final boolean includeMarkers;
85+
private final boolean includeOrigin;
8586
private final ConcurrentMap<Class<? extends MultiformatMessage>, Boolean> supportsJson = new ConcurrentHashMap<Class<? extends MultiformatMessage>, Boolean>();
8687

87-
private EcsLayout(Configuration config, String serviceName, boolean includeMarkers, KeyValuePair[] additionalFields, Collection<String> topLevelLabels, boolean stackTraceAsArray) {
88+
private EcsLayout(Configuration config, String serviceName, boolean includeMarkers, KeyValuePair[] additionalFields, Collection<String> topLevelLabels, boolean includeOrigin, boolean stackTraceAsArray) {
8889
super(config, UTF_8, null, null);
8990
this.serviceName = serviceName;
9091
this.includeMarkers = includeMarkers;
9192
this.topLevelLabels = new HashSet<String>(topLevelLabels);
93+
this.includeOrigin = includeOrigin;
9294
this.stackTraceAsArray = stackTraceAsArray;
9395
this.topLevelLabels.add("trace.id");
9496
this.topLevelLabels.add("transaction.id");
@@ -126,6 +128,9 @@ private StringBuilder toText(LogEvent event, StringBuilder builder, boolean gcFr
126128
EcsJsonSerializer.serializeLoggerName(builder, event.getLoggerName());
127129
serializeLabels(event, builder);
128130
serializeTags(event, builder);
131+
if (includeOrigin) {
132+
EcsJsonSerializer.serializeOrigin(builder, event.getSource());
133+
}
129134
EcsJsonSerializer.serializeException(builder, event.getThrown(), stackTraceAsArray);
130135
EcsJsonSerializer.serializeObjectEnd(builder);
131136
return builder;
@@ -281,6 +286,8 @@ public static class Builder extends AbstractStringLayout.Builder<EcsLayout.Build
281286
private KeyValuePair[] additionalFields;
282287
@PluginElement("TopLevelLabels")
283288
private String[] topLevelLabels;
289+
@PluginBuilderAttribute("includeOrigin")
290+
private boolean includeOrigin;
284291

285292
Builder() {
286293
super();
@@ -299,6 +306,10 @@ public boolean isIncludeMarkers() {
299306
return includeMarkers;
300307
}
301308

309+
public boolean isIncludeOrigin() {
310+
return includeOrigin;
311+
}
312+
302313
public String[] getTopLevelLabels() {
303314
return topLevelLabels;
304315
}
@@ -328,14 +339,19 @@ public EcsLayout.Builder setIncludeMarkers(final boolean includeMarkers) {
328339
return asBuilder();
329340
}
330341

342+
public EcsLayout.Builder setIncludeOrigin(final boolean includeOrigin) {
343+
this.includeOrigin = includeOrigin;
344+
return asBuilder();
345+
}
346+
331347
public EcsLayout.Builder setStackTraceAsArray(boolean stackTraceAsArray) {
332348
this.stackTraceAsArray = stackTraceAsArray;
333349
return asBuilder();
334350
}
335351

336352
@Override
337353
public EcsLayout build() {
338-
return new EcsLayout(getConfiguration(), serviceName, includeMarkers, additionalFields, topLevelLabels == null ? Collections.<String>emptyList() : Arrays.<String>asList(topLevelLabels), stackTraceAsArray);
354+
return new EcsLayout(getConfiguration(), serviceName, includeMarkers, additionalFields, topLevelLabels == null ? Collections.<String>emptyList() : Arrays.<String>asList(topLevelLabels), includeOrigin, stackTraceAsArray);
339355
}
340356

341357
public boolean isStackTraceAsArray() {

log4j2-ecs-layout/src/test/java/co/elastic/logging/log4j2/Log4j2EcsLayoutTest.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ void setUp() {
6868
.setConfiguration(ctx.getConfiguration())
6969
.setServiceName("test")
7070
.setIncludeMarkers(true)
71+
.setIncludeOrigin(true)
7172
.setStackTraceAsArray(true)
7273
.setAdditionalFields(new KeyValuePair[]{
7374
new KeyValuePair("cluster.uuid", "9fe9134b-20b0-465e-acf9-8cc09ac9053b"),

log4j2-ecs-layout/src/test/resources/log4j2-test.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
</Properties>
66
<Appenders>
77
<List name="TestAppender">
8-
<EcsLayout serviceName="test" includeMarkers="true" stackTraceAsArray="true">
8+
<EcsLayout serviceName="test" includeMarkers="true" includeOrigin="true" stackTraceAsArray="true">
99
<KeyValuePair key="cluster.uuid" value="9fe9134b-20b0-465e-acf9-8cc09ac9053b"/>
1010
<KeyValuePair key="node.id" value="${node.id}"/>
1111
<KeyValuePair key="empty" value="${empty}"/>

0 commit comments

Comments
 (0)