Skip to content

Commit 70d2f53

Browse files
authored
Support for log4j 2.6 (#100)
1 parent 2ccc261 commit 70d2f53

File tree

12 files changed

+459
-142
lines changed

12 files changed

+459
-142
lines changed

log4j2-ecs-layout/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# Log4j2 ECS Layout
22

3+
The minimum required log4j2 version is 2.7.
4+
35
## Step 1: add dependency
46

57
Latest version: [![Maven Central](https://img.shields.io/maven-central/v/co.elastic.logging/log4j2-ecs-layout.svg)](https://search.maven.org/search?q=g:co.elastic.logging%20AND%20a:log4j2-ecs-layout)

log4j2-ecs-layout/pom.xml

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
<properties>
1111
<parent.base.dir>${project.basedir}/..</parent.base.dir>
12+
<version.log4j>2.14.0</version.log4j>
1213
</properties>
1314

1415
<artifactId>log4j2-ecs-layout</artifactId>
@@ -30,12 +31,25 @@
3031
<configuration>
3132
<proc>only</proc>
3233
<annotationProcessors>
33-
<annotationProcessor>org.apache.logging.log4j.core.config.plugins.processor.PluginProcessor</annotationProcessor>
34+
<annotationProcessor>org.apache.logging.log4j.core.config.plugins.processor.PluginProcessor
35+
</annotationProcessor>
3436
</annotationProcessors>
3537
</configuration>
3638
</execution>
3739
</executions>
3840
</plugin>
41+
<plugin>
42+
<groupId>org.apache.maven.plugins</groupId>
43+
<artifactId>maven-jar-plugin</artifactId>
44+
<version>3.1.2</version>
45+
<executions>
46+
<execution>
47+
<goals>
48+
<goal>test-jar</goal>
49+
</goals>
50+
</execution>
51+
</executions>
52+
</plugin>
3953
</plugins>
4054
</build>
4155

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

Lines changed: 72 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,15 @@
2828
import co.elastic.logging.EcsJsonSerializer;
2929
import co.elastic.logging.JsonUtils;
3030
import org.apache.logging.log4j.Marker;
31+
import org.apache.logging.log4j.ThreadContext;
3132
import org.apache.logging.log4j.core.Layout;
3233
import org.apache.logging.log4j.core.LogEvent;
3334
import org.apache.logging.log4j.core.config.Configuration;
3435
import org.apache.logging.log4j.core.config.Node;
3536
import org.apache.logging.log4j.core.config.plugins.Plugin;
3637
import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute;
3738
import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory;
39+
import org.apache.logging.log4j.core.config.plugins.PluginConfiguration;
3840
import org.apache.logging.log4j.core.config.plugins.PluginElement;
3941
import org.apache.logging.log4j.core.layout.AbstractStringLayout;
4042
import org.apache.logging.log4j.core.layout.ByteBufferDestination;
@@ -46,11 +48,10 @@
4648
import org.apache.logging.log4j.message.Message;
4749
import org.apache.logging.log4j.message.MultiformatMessage;
4850
import org.apache.logging.log4j.message.ObjectMessage;
49-
import org.apache.logging.log4j.util.MultiFormatStringBuilderFormattable;
5051
import org.apache.logging.log4j.util.StringBuilderFormattable;
51-
import org.apache.logging.log4j.util.TriConsumer;
5252

5353
import java.nio.charset.Charset;
54+
import java.util.Collections;
5455
import java.util.List;
5556
import java.util.concurrent.ConcurrentHashMap;
5657
import java.util.concurrent.ConcurrentMap;
@@ -59,18 +60,9 @@
5960
public class EcsLayout extends AbstractStringLayout {
6061

6162
public static final Charset UTF_8 = Charset.forName("UTF-8");
62-
static final String[] JSON_FORMAT = {"JSON"};
63-
64-
private final TriConsumer<String, Object, StringBuilder> WRITE_MDC = new TriConsumer<String, Object, StringBuilder>() {
65-
@Override
66-
public void accept(final String key, final Object value, final StringBuilder stringBuilder) {
67-
stringBuilder.append('\"');
68-
JsonUtils.quoteAsString(key, stringBuilder);
69-
stringBuilder.append("\":\"");
70-
JsonUtils.quoteAsString(EcsJsonSerializer.toNullSafeString(String.valueOf(value)), stringBuilder);
71-
stringBuilder.append("\",");
72-
}
73-
};
63+
private static final ObjectMessageJacksonSerializer JACKSON_SERIALIZER = ObjectMessageJacksonSerializer.Resolver.resolve();
64+
private static final MdcSerializer MDC_SERIALIZER = MdcSerializer.Resolver.resolve();
65+
private static final MultiFormatHandler MULTI_FORMAT_HANDLER = MultiFormatHandler.Resolver.resolve();
7466

7567
private final KeyValuePair[] additionalFields;
7668
private final PatternFormatter[][] fieldValuePatternFormatter;
@@ -80,7 +72,6 @@ public void accept(final String key, final Object value, final StringBuilder str
8072
private final boolean includeMarkers;
8173
private final boolean includeOrigin;
8274
private final ConcurrentMap<Class<? extends MultiformatMessage>, Boolean> supportsJson = new ConcurrentHashMap<Class<? extends MultiformatMessage>, Boolean>();
83-
private final ObjectMessageJacksonSerializer objectMessageJacksonSerializer = ObjectMessageJacksonSerializer.Resolver.INSTANCE.resolve();
8475

8576
private EcsLayout(Configuration config, String serviceName, String eventDataset, boolean includeMarkers, KeyValuePair[] additionalFields, boolean includeOrigin, boolean stackTraceAsArray) {
8677
super(config, UTF_8, null, null);
@@ -103,7 +94,7 @@ private EcsLayout(Configuration config, String serviceName, String eventDataset,
10394

10495
@PluginBuilderFactory
10596
public static EcsLayout.Builder newBuilder() {
106-
return new EcsLayout.Builder().asBuilder();
97+
return new EcsLayout.Builder();
10798
}
10899

109100
private static boolean valueNeedsLookup(final String value) {
@@ -148,40 +139,38 @@ private StringBuilder toText(LogEvent event, StringBuilder builder, boolean gcFr
148139

149140
private void serializeAdditionalFieldsAndMDC(LogEvent event, StringBuilder builder) {
150141
final int length = additionalFields.length;
151-
if (!event.getContextData().isEmpty() || length > 0) {
152-
if (length > 0) {
153-
final StrSubstitutor strSubstitutor = getConfiguration().getStrSubstitutor();
154-
for (int i = 0; i < length; i++) {
155-
KeyValuePair additionalField = additionalFields[i];
156-
PatternFormatter[] formatters = fieldValuePatternFormatter[i];
157-
CharSequence value = null;
158-
if (formatters != null) {
159-
StringBuilder buffer = EcsJsonSerializer.getMessageStringBuilder();
160-
formatPattern(event, formatters, buffer);
161-
if (buffer.length() > 0) {
162-
value = buffer;
163-
}
164-
} else if (valueNeedsLookup(additionalField.getValue())) {
165-
StringBuilder lookupValue = EcsJsonSerializer.getMessageStringBuilder();
166-
lookupValue.append(additionalField.getValue());
167-
if (strSubstitutor.replaceIn(event, lookupValue)) {
168-
value = lookupValue;
169-
}
170-
} else {
171-
value = additionalField.getValue();
142+
if (length > 0) {
143+
final StrSubstitutor strSubstitutor = getConfiguration().getStrSubstitutor();
144+
for (int i = 0; i < length; i++) {
145+
KeyValuePair additionalField = additionalFields[i];
146+
PatternFormatter[] formatters = fieldValuePatternFormatter[i];
147+
CharSequence value = null;
148+
if (formatters != null) {
149+
StringBuilder buffer = EcsJsonSerializer.getMessageStringBuilder();
150+
formatPattern(event, formatters, buffer);
151+
if (buffer.length() > 0) {
152+
value = buffer;
172153
}
173-
174-
if (value != null) {
175-
builder.append('\"');
176-
JsonUtils.quoteAsString(additionalField.getKey(), builder);
177-
builder.append("\":\"");
178-
JsonUtils.quoteAsString(EcsJsonSerializer.toNullSafeString(value), builder);
179-
builder.append("\",");
154+
} else if (valueNeedsLookup(additionalField.getValue())) {
155+
StringBuilder lookupValue = EcsJsonSerializer.getMessageStringBuilder();
156+
lookupValue.append(additionalField.getValue());
157+
if (strSubstitutor.replaceIn(event, lookupValue)) {
158+
value = lookupValue;
180159
}
160+
} else {
161+
value = additionalField.getValue();
162+
}
163+
164+
if (value != null) {
165+
builder.append('\"');
166+
JsonUtils.quoteAsString(additionalField.getKey(), builder);
167+
builder.append("\":\"");
168+
JsonUtils.quoteAsString(EcsJsonSerializer.toNullSafeString(value), builder);
169+
builder.append("\",");
181170
}
182171
}
183-
event.getContextData().forEach(WRITE_MDC, builder);
184172
}
173+
MDC_SERIALIZER.serializeMdc(event, builder);
185174
}
186175

187176
private static void formatPattern(LogEvent event, PatternFormatter[] formatters, StringBuilder buffer) {
@@ -192,7 +181,13 @@ private static void formatPattern(LogEvent event, PatternFormatter[] formatters,
192181
}
193182

194183
private void serializeTags(LogEvent event, StringBuilder builder) {
195-
List<String> contextStack = event.getContextStack().asList();
184+
ThreadContext.ContextStack stack = event.getContextStack();
185+
List<String> contextStack;
186+
if (stack == null) {
187+
contextStack = Collections.emptyList();
188+
} else {
189+
contextStack = stack.asList();
190+
}
196191
Marker marker = event.getMarker();
197192
boolean hasTags = !contextStack.isEmpty() || (includeMarkers && marker != null);
198193
if (hasTags) {
@@ -235,9 +230,9 @@ private void serializeMessage(StringBuilder builder, boolean gcFree, Message mes
235230
} else {
236231
serializeSimpleMessage(builder, gcFree, message, thrown);
237232
}
238-
} else if (objectMessageJacksonSerializer != null && message instanceof ObjectMessage) {
233+
} else if (JACKSON_SERIALIZER != null && message instanceof ObjectMessage) {
239234
final StringBuilder jsonBuffer = EcsJsonSerializer.getMessageStringBuilder();
240-
objectMessageJacksonSerializer.formatTo(jsonBuffer, (ObjectMessage) message);
235+
JACKSON_SERIALIZER.formatTo(jsonBuffer, (ObjectMessage) message);
241236
addJson(builder, jsonBuffer);
242237
} else {
243238
serializeSimpleMessage(builder, gcFree, message, thrown);
@@ -246,11 +241,7 @@ private void serializeMessage(StringBuilder builder, boolean gcFree, Message mes
246241

247242
private static void serializeJsonMessage(StringBuilder builder, MultiformatMessage message) {
248243
final StringBuilder messageBuffer = EcsJsonSerializer.getMessageStringBuilder();
249-
if (message instanceof MultiFormatStringBuilderFormattable) {
250-
((MultiFormatStringBuilderFormattable) message).formatTo(JSON_FORMAT, messageBuffer);
251-
} else {
252-
messageBuffer.append(message.getFormattedMessage(JSON_FORMAT));
253-
}
244+
MULTI_FORMAT_HANDLER.formatJsonTo(message, messageBuffer);
254245
addJson(builder, messageBuffer);
255246
}
256247

@@ -283,20 +274,27 @@ private void serializeSimpleMessage(StringBuilder builder, boolean gcFree, Messa
283274
((StringBuilderFormattable) message).formatTo(messageBuffer);
284275
JsonUtils.quoteAsString(messageBuffer, builder);
285276
} finally {
286-
trimToMaxSize(messageBuffer);
277+
trimToMaxSizeCopy(messageBuffer);
287278
}
288279
} else {
289280
JsonUtils.quoteAsString(EcsJsonSerializer.toNullSafeString(message.getFormattedMessage()), builder);
290281
}
291282
builder.append("\", ");
292283
}
293284

285+
static void trimToMaxSizeCopy(final StringBuilder stringBuilder) {
286+
if (stringBuilder.length() > MAX_STRING_BUILDER_SIZE) {
287+
stringBuilder.setLength(MAX_STRING_BUILDER_SIZE);
288+
stringBuilder.trimToSize();
289+
}
290+
}
291+
294292
private static boolean isObject(StringBuilder messageBuffer) {
295-
return messageBuffer.length() > 1 && messageBuffer.charAt(0) == '{' && messageBuffer.charAt(messageBuffer.length() -1) == '}';
293+
return messageBuffer.length() > 1 && messageBuffer.charAt(0) == '{' && messageBuffer.charAt(messageBuffer.length() - 1) == '}';
296294
}
297295

298296
private static boolean isString(StringBuilder messageBuffer) {
299-
return messageBuffer.length() > 1 && messageBuffer.charAt(0) == '"' && messageBuffer.charAt(messageBuffer.length() -1) == '"';
297+
return messageBuffer.length() > 1 && messageBuffer.charAt(0) == '"' && messageBuffer.charAt(messageBuffer.length() - 1) == '"';
300298
}
301299

302300
private static void moveToRoot(StringBuilder messageBuffer) {
@@ -319,9 +317,10 @@ private boolean supportsJson(MultiformatMessage message) {
319317
return supportsJson;
320318
}
321319

322-
public static class Builder extends AbstractStringLayout.Builder<EcsLayout.Builder>
323-
implements org.apache.logging.log4j.core.util.Builder<EcsLayout> {
320+
public static class Builder implements org.apache.logging.log4j.core.util.Builder<EcsLayout> {
324321

322+
@PluginConfiguration
323+
private Configuration configuration;
325324
@PluginBuilderAttribute("serviceName")
326325
private String serviceName;
327326
@PluginBuilderAttribute("eventDataset")
@@ -336,8 +335,15 @@ public static class Builder extends AbstractStringLayout.Builder<EcsLayout.Build
336335
private boolean includeOrigin = false;
337336

338337
Builder() {
339-
super();
340-
setCharset(UTF_8);
338+
}
339+
340+
public Configuration getConfiguration() {
341+
return configuration;
342+
}
343+
344+
public EcsLayout.Builder setConfiguration(final Configuration configuration) {
345+
this.configuration = configuration;
346+
return this;
341347
}
342348

343349
public KeyValuePair[] getAdditionalFields() {
@@ -367,32 +373,32 @@ public boolean isIncludeOrigin() {
367373
*/
368374
public EcsLayout.Builder setAdditionalFields(final KeyValuePair[] additionalFields) {
369375
this.additionalFields = additionalFields.clone();
370-
return asBuilder();
376+
return this;
371377
}
372378

373379
public EcsLayout.Builder setServiceName(final String serviceName) {
374380
this.serviceName = serviceName;
375-
return asBuilder();
381+
return this;
376382
}
377383

378384
public EcsLayout.Builder setEventDataset(String eventDataset) {
379385
this.eventDataset = eventDataset;
380-
return asBuilder();
386+
return this;
381387
}
382388

383389
public EcsLayout.Builder setIncludeMarkers(final boolean includeMarkers) {
384390
this.includeMarkers = includeMarkers;
385-
return asBuilder();
391+
return this;
386392
}
387393

388394
public EcsLayout.Builder setIncludeOrigin(final boolean includeOrigin) {
389395
this.includeOrigin = includeOrigin;
390-
return asBuilder();
396+
return this;
391397
}
392398

393399
public EcsLayout.Builder setStackTraceAsArray(boolean stackTraceAsArray) {
394400
this.stackTraceAsArray = stackTraceAsArray;
395-
return asBuilder();
401+
return this;
396402
}
397403

398404
@Override

0 commit comments

Comments
 (0)