Skip to content

Commit fb287cc

Browse files
authored
Add support for service.node.name (#135)
1 parent 0f8c44c commit fb287cc

File tree

17 files changed

+133
-8
lines changed

17 files changed

+133
-8
lines changed

docs/tab-widgets/ecs-encoder.asciidoc

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ All you have to do is to use the `co.elastic.logging.logback.EcsEncoder` instead
3939
----
4040
<encoder class="co.elastic.logging.logback.EcsEncoder">
4141
<serviceName>my-application</serviceName>
42+
<serviceNodeName>my-application-cluster-node</serviceNodeName>
4243
</encoder>
4344
----
4445

@@ -52,6 +53,11 @@ All you have to do is to use the `co.elastic.logging.logback.EcsEncoder` instead
5253
|
5354
|Sets the `service.name` field so you can filter your logs by a particular service
5455

56+
|`serviceNodeName`
57+
|String
58+
|
59+
|Sets the `service.node.name` field so you can filter your logs by a particular node of your clustered service
60+
5561
|`eventDataset`
5662
|String
5763
|`${serviceName}.log`
@@ -102,10 +108,10 @@ For example:
102108
<Configuration status="DEBUG">
103109
<Appenders>
104110
<Console name="LogToConsole" target="SYSTEM_OUT">
105-
<EcsLayout serviceName="my-app"/>
111+
<EcsLayout serviceName="my-app" serviceNodeName="my-app-cluster-node"/>
106112
</Console>
107113
<File name="LogToFile" fileName="logs/app.log">
108-
<EcsLayout serviceName="my-app"/>
114+
<EcsLayout serviceName="my-app" serviceNodeName="my-app-cluster-node"/>
109115
</File>
110116
</Appenders>
111117
<Loggers>
@@ -127,6 +133,11 @@ For example:
127133
|
128134
|Sets the `service.name` field so you can filter your logs by a particular service
129135

136+
|`serviceNodeName`
137+
|String
138+
|
139+
|Sets the `service.node.name` field so you can filter your logs by a particular node of your clustered service
140+
130141
|`eventDataset`
131142
|String
132143
|`${serviceName}.log`
@@ -180,12 +191,14 @@ For example:
180191
<param name="Target" value="System.out"/>
181192
<layout class="co.elastic.logging.log4j.EcsLayout">
182193
<param name="serviceName" value="my-app"/>
194+
<param name="serviceNodeName" value="my-app-cluster-node"/>
183195
</layout>
184196
</appender>
185197
<appender name="LogToFile" class="org.apache.log4j.RollingFileAppender">
186198
<param name="File" value="logs/app.log"/>
187199
<layout class="co.elastic.logging.log4j.EcsLayout">
188200
<param name="serviceName" value="my-app"/>
201+
<param name="serviceNodeName" value="my-app-cluster-node"/>
189202
</layout>
190203
</appender>
191204
<root>
@@ -207,6 +220,11 @@ For example:
207220
|
208221
|Sets the `service.name` field so you can filter your logs by a particular service
209222

223+
|`serviceNodeName`
224+
|String
225+
|
226+
|Sets the `service.node.name` field so you can filter your logs by a particular node of your clustered service
227+
210228
|`eventDataset`
211229
|String
212230
|`${serviceName}.log`
@@ -249,6 +267,7 @@ For example, in `$CATALINA_HOME/conf/logging.properties`:
249267
java.util.logging.ConsoleHandler.level = FINE
250268
java.util.logging.ConsoleHandler.formatter = co.elastic.logging.jul.EcsFormatter
251269
co.elastic.logging.jul.EcsFormatter.serviceName=my-app
270+
co.elastic.logging.jul.EcsFormatter.serviceNodeName=my-app-cluster-node
252271
----
253272

254273
**Layout Parameters**
@@ -261,6 +280,11 @@ co.elastic.logging.jul.EcsFormatter.serviceName=my-app
261280
|
262281
|Sets the `service.name` field so you can filter your logs by a particular service
263282

283+
|`serviceNodeName`
284+
|String
285+
|
286+
|Sets the `service.node.name` field so you can filter your logs by a particular node of your clustered service
287+
264288
|`eventDataset`
265289
|String
266290
|`${serviceName}.log`
@@ -301,7 +325,8 @@ Add the formatter to a handler in the logging subsystem:
301325

302326
[source,bash]
303327
----
304-
$WILDFLY_HOME/bin/jboss-cli.sh -c '/subsystem=logging/custom-formatter=ECS:add(module=co.elastic.logging.jboss-logmanager-ecs-formatter, class=co.elastic.logging.jboss.logmanager.EcsFormatter, properties={serviceName=my-app}),\
328+
$WILDFLY_HOME/bin/jboss-cli.sh -c '/subsystem=logging/custom-formatter=ECS:add(module=co.elastic.logging.jboss-logmanager-ecs-formatter,
329+
class=co.elastic.logging.jboss.logmanager.EcsFormatter, properties={serviceName=my-app,serviceNodeName=my-app-cluster-node}),\
305330
/subsystem=logging/console-handler=CONSOLE:write-attribute(name=named-formatter,value=ECS)'
306331
----
307332

@@ -315,6 +340,11 @@ $WILDFLY_HOME/bin/jboss-cli.sh -c '/subsystem=logging/custom-formatter=ECS:add(m
315340
|
316341
|Sets the `service.name` field so you can filter your logs by a particular service
317342

343+
|`serviceNodeName`
344+
|String
345+
|
346+
|Sets the `service.node.name` field so you can filter your logs by a particular node of your clustered service
347+
318348
|`eventDataset`
319349
|String
320350
|`${serviceName}.log`

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

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,13 +87,25 @@ public static void serializeFormattedMessage(StringBuilder builder, String messa
8787

8888
public static void serializeServiceName(StringBuilder builder, String serviceName) {
8989
if (serviceName != null) {
90-
builder.append("\"service.name\":\"").append(serviceName).append("\",");
90+
builder.append("\"service.name\":\"");
91+
JsonUtils.quoteAsString(serviceName, builder);
92+
builder.append("\",");
93+
}
94+
}
95+
96+
public static void serializeServiceNodeName(StringBuilder builder, String serviceNodeName) {
97+
if (serviceNodeName != null) {
98+
builder.append("\"service.node.name\":\"");
99+
JsonUtils.quoteAsString(serviceNodeName, builder);
100+
builder.append("\",");
91101
}
92102
}
93103

94104
public static void serializeEventDataset(StringBuilder builder, String eventDataset) {
95105
if (eventDataset != null) {
96-
builder.append("\"event.dataset\":\"").append(eventDataset).append("\",");
106+
builder.append("\"event.dataset\":\"");
107+
JsonUtils.quoteAsString(eventDataset, builder);
108+
builder.append("\",");
97109
}
98110
}
99111

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ void testMetadata() throws Exception {
5757
debug("test");
5858
assertThat(getAndValidateLastLogLine().get("process.thread.name").textValue()).isEqualTo(Thread.currentThread().getName());
5959
assertThat(getAndValidateLastLogLine().get("service.name").textValue()).isEqualTo("test");
60+
assertThat(getAndValidateLastLogLine().get("service.node.name").textValue()).isEqualTo("test-node");
6061
assertThat(Instant.parse(getAndValidateLastLogLine().get("@timestamp").textValue())).isCloseTo(Instant.now(), within(1, ChronoUnit.MINUTES));
6162
assertThat(getAndValidateLastLogLine().get("log.level").textValue()).isIn("DEBUG", "FINE");
6263
assertThat(getAndValidateLastLogLine().get("log.logger")).isNotNull();

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

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import java.io.IOException;
3333
import java.io.PrintWriter;
3434
import java.io.StringWriter;
35+
import java.util.List;
3536
import java.util.stream.Collectors;
3637
import java.util.stream.StreamSupport;
3738

@@ -55,6 +56,37 @@ void serializeExceptionAsString() throws IOException {
5556
assertThat(jsonNode.get("error.stack_trace").textValue()).isEqualTo(stringWriter.toString());
5657
}
5758

59+
@Test
60+
void testEscaping() throws IOException {
61+
String loggerName = "logger\"";
62+
String serviceName = "test\"";
63+
String serviceNodeName = "test-node\"";
64+
String eventDataset = "event-dataset\"";
65+
String threadName = "event-dataset\"";
66+
String additionalKey = "key\"";
67+
String additionalValue = "=value\"";
68+
69+
StringBuilder jsonBuilder = new StringBuilder();
70+
jsonBuilder.append('{');
71+
EcsJsonSerializer.serializeLoggerName(jsonBuilder, loggerName);
72+
EcsJsonSerializer.serializeServiceName(jsonBuilder, serviceName);
73+
EcsJsonSerializer.serializeServiceNodeName(jsonBuilder, serviceNodeName);
74+
EcsJsonSerializer.serializeEventDataset(jsonBuilder, eventDataset);
75+
EcsJsonSerializer.serializeThreadName(jsonBuilder, threadName);
76+
EcsJsonSerializer.serializeAdditionalFields(jsonBuilder, List.of(new AdditionalField(additionalKey, additionalValue)));
77+
EcsJsonSerializer.serializeObjectEnd(jsonBuilder);
78+
jsonBuilder.append('}');
79+
JsonNode jsonNode = new ObjectMapper().readTree(jsonBuilder.toString());
80+
81+
assertThat(jsonNode.get("log.logger").textValue()).isEqualTo(loggerName);
82+
assertThat(jsonNode.get("service.name").textValue()).isEqualTo(serviceName);
83+
assertThat(jsonNode.get("service.node.name").textValue()).isEqualTo(serviceNodeName);
84+
assertThat(jsonNode.get("event.dataset").textValue()).isEqualTo(eventDataset);
85+
assertThat(jsonNode.get("process.thread.name").textValue()).isEqualTo(eventDataset);
86+
assertThat(jsonNode.get("process.thread.name").textValue()).isEqualTo(eventDataset);
87+
assertThat(jsonNode.get(additionalKey).textValue()).isEqualTo(additionalValue);
88+
}
89+
5890
@Test
5991
void serializeNullDoesNotThrowAnException() throws JsonProcessingException {
6092
StringBuilder stringBuilder = new StringBuilder();

jboss-logmanager-ecs-formatter/src/main/java/co/elastic/logging/jboss/logmanager/EcsFormatter.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,13 +37,15 @@
3737
public class EcsFormatter extends ExtFormatter {
3838

3939
private String serviceName;
40+
private String serviceNodeName;
4041
private String eventDataset;
4142
private List<AdditionalField> additionalFields = Collections.emptyList();
4243
private boolean includeOrigin;
4344
private boolean stackTraceAsArray;
4445

4546
public EcsFormatter() {
4647
serviceName = getProperty("co.elastic.logging.jboss.logmanager.EcsFormatter.serviceName", null);
48+
serviceNodeName = getProperty("co.elastic.logging.jboss.logmanager.EcsFormatter.serviceNodeName", null);
4749
eventDataset = getProperty("co.elastic.logging.jboss.logmanager.EcsFormatter.eventDataset", null);
4850
eventDataset = EcsJsonSerializer.computeEventDataset(eventDataset, serviceName);
4951
includeOrigin = Boolean.getBoolean(getProperty("co.elastic.logging.jboss.logmanager.EcsFormatter.includeOrigin", "false"));
@@ -58,6 +60,7 @@ public String format(ExtLogRecord record) {
5860
EcsJsonSerializer.serializeFormattedMessage(builder, record.getFormattedMessage());
5961
EcsJsonSerializer.serializeEcsVersion(builder);
6062
EcsJsonSerializer.serializeServiceName(builder, serviceName);
63+
EcsJsonSerializer.serializeServiceNodeName(builder, serviceNodeName);
6164
EcsJsonSerializer.serializeEventDataset(builder, eventDataset);
6265
EcsJsonSerializer.serializeThreadName(builder, record.getThreadName());
6366
EcsJsonSerializer.serializeLoggerName(builder, record.getLoggerName());
@@ -91,6 +94,10 @@ public void setServiceName(final String serviceName) {
9194
eventDataset = EcsJsonSerializer.computeEventDataset(eventDataset, serviceName);
9295
}
9396

97+
public void setServiceNodeName(final String serviceNodeName) {
98+
this.serviceNodeName = serviceNodeName;
99+
}
100+
94101
public void setStackTraceAsArray(final boolean stackTraceAsArray) {
95102
this.stackTraceAsArray = stackTraceAsArray;
96103
}

jboss-logmanager-ecs-formatter/src/test/java/co/elastic/logging/jboss/logmanager/JBossLogManagerTest.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ public JsonNode getLastLogLine() throws IOException {
8989
void setUp() {
9090
formatter.setIncludeOrigin(true);
9191
formatter.setServiceName("test");
92+
formatter.setServiceNodeName("test-node");
9293
formatter.setEventDataset("testdataset.log");
9394
formatter.setAdditionalFields("key1=value1,key2=value2");
9495

jul-ecs-formatter/src/main/java/co/elastic/logging/jul/EcsFormatter.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ public class EcsFormatter extends Formatter {
4040

4141
private boolean stackTraceAsArray;
4242
private String serviceName;
43+
private String serviceNodeName;
4344
private boolean includeOrigin;
4445
private String eventDataset;
4546
private List<AdditionalField> additionalFields = Collections.emptyList();
@@ -49,6 +50,7 @@ public class EcsFormatter extends Formatter {
4950
*/
5051
public EcsFormatter() {
5152
serviceName = getProperty("co.elastic.logging.jul.EcsFormatter.serviceName", null);
53+
serviceNodeName = getProperty("co.elastic.logging.jul.EcsFormatter.serviceNodeName", null);
5254
includeOrigin = Boolean.getBoolean(getProperty("co.elastic.logging.jul.EcsFormatter.includeOrigin", "false"));
5355
stackTraceAsArray = Boolean
5456
.getBoolean(getProperty("co.elastic.logging.jul.EcsFormatter.stackTraceAsArray", "false"));
@@ -67,6 +69,7 @@ public String format(final LogRecord record) {
6769
EcsJsonSerializer.serializeAdditionalFields(builder, additionalFields);
6870
EcsJsonSerializer.serializeMDC(builder, mdcSupplier.getMDC());
6971
EcsJsonSerializer.serializeServiceName(builder, serviceName);
72+
EcsJsonSerializer.serializeServiceNodeName(builder, serviceNodeName);
7073
EcsJsonSerializer.serializeEventDataset(builder, eventDataset);
7174
if (Thread.currentThread().getId() == record.getThreadID()) {
7275
EcsJsonSerializer.serializeThreadName(builder, Thread.currentThread().getName());
@@ -93,6 +96,10 @@ protected void setServiceName(final String serviceName) {
9396
this.serviceName = serviceName;
9497
}
9598

99+
protected void setServiceNodeName(final String serviceNodeName) {
100+
this.serviceNodeName = serviceNodeName;
101+
}
102+
96103
protected void setStackTraceAsArray(final boolean stackTraceAsArray) {
97104
this.stackTraceAsArray = stackTraceAsArray;
98105
}

jul-ecs-formatter/src/test/java/co/elastic/logging/jul/JulLoggingTest.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ void setUp() {
112112

113113
formatter.setIncludeOrigin(true);
114114
formatter.setServiceName("test");
115+
formatter.setServiceNodeName("test-node");
115116
formatter.setEventDataset("testdataset.log");
116117
formatter.setAdditionalFields("key1=value1,key2=value2");
117118

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ public class EcsLayout extends Layout {
4040

4141
private boolean stackTraceAsArray = false;
4242
private String serviceName;
43+
private String serviceNodeName;
4344
private boolean includeOrigin;
4445
private String eventDataset;
4546
private List<AdditionalField> additionalFields = new ArrayList<AdditionalField>();
@@ -52,6 +53,7 @@ public String format(LoggingEvent event) {
5253
EcsJsonSerializer.serializeFormattedMessage(builder, event.getRenderedMessage());
5354
EcsJsonSerializer.serializeEcsVersion(builder);
5455
EcsJsonSerializer.serializeServiceName(builder, serviceName);
56+
EcsJsonSerializer.serializeServiceNodeName(builder, serviceNodeName);
5557
EcsJsonSerializer.serializeEventDataset(builder, eventDataset);
5658
EcsJsonSerializer.serializeThreadName(builder, event.getThreadName());
5759
EcsJsonSerializer.serializeLoggerName(builder, event.categoryName);
@@ -99,6 +101,10 @@ public void setServiceName(String serviceName) {
99101
this.serviceName = serviceName;
100102
}
101103

104+
public void setServiceNodeName(String serviceNodeName) {
105+
this.serviceNodeName = serviceNodeName;
106+
}
107+
102108
public void setIncludeOrigin(boolean includeOrigin) {
103109
this.includeOrigin = includeOrigin;
104110
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ void setUp() {
5151
logger.addAppender(appender);
5252
ecsLayout = new EcsLayout();
5353
ecsLayout.setServiceName("test");
54+
ecsLayout.setServiceNodeName("test-node");
5455
ecsLayout.setIncludeOrigin(true);
5556
ecsLayout.setEventDataset("testdataset.log");
5657
ecsLayout.activateOptions();

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
<appender name="List" class="co.elastic.logging.log4j.ListAppender">
55
<layout class="co.elastic.logging.log4j.EcsLayout">
66
<param name="serviceName" value="test"/>
7+
<param name="serviceNodeName" value="test-node"/>
78
<param name="eventDataset" value="testdataset.log"/>
89
<param name="includeOrigin" value="true"/>
910
<param name="additionalField" value="key1=value1"/>

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

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,14 +68,17 @@ public class EcsLayout extends AbstractStringLayout {
6868
private final PatternFormatter[][] fieldValuePatternFormatter;
6969
private final boolean stackTraceAsArray;
7070
private final String serviceName;
71+
private final String serviceNodeName;
7172
private final String eventDataset;
7273
private final boolean includeMarkers;
7374
private final boolean includeOrigin;
7475
private final ConcurrentMap<Class<? extends MultiformatMessage>, Boolean> supportsJson = new ConcurrentHashMap<Class<? extends MultiformatMessage>, Boolean>();
7576

76-
private EcsLayout(Configuration config, String serviceName, String eventDataset, boolean includeMarkers, KeyValuePair[] additionalFields, boolean includeOrigin, boolean stackTraceAsArray) {
77+
private EcsLayout(Configuration config, String serviceName, String serviceNodeName, String eventDataset, boolean includeMarkers,
78+
KeyValuePair[] additionalFields, boolean includeOrigin, boolean stackTraceAsArray) {
7779
super(config, UTF_8, null, null);
7880
this.serviceName = serviceName;
81+
this.serviceNodeName = serviceNodeName;
7982
this.eventDataset = eventDataset;
8083
this.includeMarkers = includeMarkers;
8184
this.includeOrigin = includeOrigin;
@@ -125,6 +128,7 @@ private StringBuilder toText(LogEvent event, StringBuilder builder, boolean gcFr
125128
serializeMessage(builder, gcFree, event.getMessage(), event.getThrown());
126129
EcsJsonSerializer.serializeEcsVersion(builder);
127130
EcsJsonSerializer.serializeServiceName(builder, serviceName);
131+
EcsJsonSerializer.serializeServiceNodeName(builder, serviceNodeName);
128132
EcsJsonSerializer.serializeEventDataset(builder, eventDataset);
129133
EcsJsonSerializer.serializeThreadName(builder, event.getThreadName());
130134
EcsJsonSerializer.serializeLoggerName(builder, event.getLoggerName());
@@ -324,6 +328,8 @@ public static class Builder implements org.apache.logging.log4j.core.util.Builde
324328
private Configuration configuration;
325329
@PluginBuilderAttribute("serviceName")
326330
private String serviceName;
331+
@PluginBuilderAttribute("serviceNodeName")
332+
private String serviceNodeName;
327333
@PluginBuilderAttribute("eventDataset")
328334
private String eventDataset;
329335
@PluginBuilderAttribute("includeMarkers")
@@ -355,6 +361,10 @@ public String getServiceName() {
355361
return serviceName;
356362
}
357363

364+
public String getServiceNodeName() {
365+
return serviceNodeName;
366+
}
367+
358368
public String getEventDataset() {
359369
return eventDataset;
360370
}
@@ -382,6 +392,11 @@ public EcsLayout.Builder setServiceName(final String serviceName) {
382392
return this;
383393
}
384394

395+
public EcsLayout.Builder setServiceNodeName(final String serviceNodeName) {
396+
this.serviceNodeName = serviceNodeName;
397+
return this;
398+
}
399+
385400
public EcsLayout.Builder setEventDataset(String eventDataset) {
386401
this.eventDataset = eventDataset;
387402
return this;
@@ -404,7 +419,8 @@ public EcsLayout.Builder setStackTraceAsArray(boolean stackTraceAsArray) {
404419

405420
@Override
406421
public EcsLayout build() {
407-
return new EcsLayout(getConfiguration(), serviceName, EcsJsonSerializer.computeEventDataset(eventDataset, serviceName), includeMarkers, additionalFields, includeOrigin, stackTraceAsArray);
422+
return new EcsLayout(getConfiguration(), serviceName, serviceNodeName, EcsJsonSerializer.computeEventDataset(eventDataset, serviceName),
423+
includeMarkers, additionalFields, includeOrigin, stackTraceAsArray);
408424
}
409425

410426
public boolean isStackTraceAsArray() {

0 commit comments

Comments
 (0)