Skip to content

Commit fbc5f63

Browse files
authored
Make all fields under log.origin.* be consistently nested (#120)
1 parent 9d19a70 commit fbc5f63

File tree

5 files changed

+51
-34
lines changed

5 files changed

+51
-34
lines changed

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

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -142,19 +142,23 @@ public static void serializeOrigin(StringBuilder builder, StackTraceElement stac
142142
}
143143

144144
public static void serializeOrigin(StringBuilder builder, String fileName, String methodName, int lineNumber) {
145-
builder.append("\"log.origin\":{");
146-
builder.append("\"file.name\":\"");
145+
builder.append("\"log\":{");
146+
builder.append("\"origin\":{");
147+
builder.append("\"file\":{");
148+
builder.append("\"name\":\"");
147149
JsonUtils.quoteAsString(fileName, builder);
148-
builder.append("\",");
149-
builder.append("\"function\":\"");
150-
JsonUtils.quoteAsString(methodName, builder);
151150
builder.append('"');
152151
if (lineNumber >= 0) {
153152
builder.append(',');
154-
builder.append("\"file.line\":");
153+
builder.append("\"line\":");
155154
builder.append(lineNumber);
156155
}
157156
builder.append("},");
157+
builder.append("\"function\":\"");
158+
JsonUtils.quoteAsString(methodName, builder);
159+
builder.append('"');
160+
builder.append("}");
161+
builder.append("},");
158162
}
159163

160164
public static void serializeMDC(StringBuilder builder, Map<String, ?> properties) {

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

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,6 @@
3737
import java.util.Iterator;
3838
import java.util.List;
3939
import java.util.Map;
40-
import java.util.stream.Collectors;
41-
import java.util.stream.StreamSupport;
4240

4341
import static org.assertj.core.api.Assertions.assertThat;
4442
import static org.assertj.core.api.Assertions.assertThatCode;
@@ -92,20 +90,30 @@ void validateLog(JsonNode logLine) {
9290
String specFieldName = specField.getKey();
9391
JsonNode specForField = specField.getValue();
9492
JsonNode fieldInLog = logLine.get(specFieldName);
93+
if (fieldInLog == null) {
94+
fieldInLog = logLine.at("/" + specFieldName.replace('.', '/'));
95+
}
9596

96-
validateRequiredField(logLine, specFieldName, specForField.get("required").booleanValue());
97-
if (fieldInLog != null) {
97+
validateRequiredField(logLine, specFieldName, fieldInLog, specForField.get("required").booleanValue());
98+
if (!fieldInLog.isMissingNode()) {
9899
validateIndex(logLine, logFieldNames, specFieldName, specForField.get("index"));
99100
validateType(fieldInLog, specForField.get("type").textValue());
101+
validateNesting(logLine, specFieldName, specForField.has("top_level_field") && specForField.get("top_level_field").asBoolean(false));
100102
}
101103
}
102104
}
103105

104-
private void validateRequiredField(JsonNode logLine, String specFieldName, boolean required) {
106+
private void validateNesting(JsonNode logLine, String specFieldName, boolean topLevelField) {
107+
if (topLevelField) {
108+
assertThat(logLine.at("/" + specFieldName.replace('.', '/')).isMissingNode()).isTrue();
109+
}
110+
}
111+
112+
private void validateRequiredField(JsonNode logLine, String specFieldName, JsonNode fieldInLog, boolean required) {
105113
if (required) {
106-
assertThat(logLine.get(specFieldName))
107-
.describedAs(logLine.toString())
108-
.isNotNull();
114+
assertThat(fieldInLog.isMissingNode())
115+
.describedAs("Expected %s to be non-null: %s", specFieldName, logLine)
116+
.isFalse();
109117
}
110118
}
111119

@@ -196,9 +204,9 @@ void testLogExceptionNullMessage() throws Exception {
196204
@Test
197205
void testLogOrigin() throws Exception {
198206
debug("test");
199-
assertThat(getAndValidateLastLogLine().get("log.origin").get("file.name").textValue()).endsWith(".java");
200-
assertThat(getAndValidateLastLogLine().get("log.origin").get("function").textValue()).isEqualTo("debug");
201-
assertThat(getAndValidateLastLogLine().get("log.origin").get("file.line").intValue()).isPositive();
207+
assertThat(getAndValidateLastLogLine().at("/log/origin/file/name").textValue()).endsWith(".java");
208+
assertThat(getAndValidateLastLogLine().at("/log/origin/function").textValue()).isEqualTo("debug");
209+
assertThat(getAndValidateLastLogLine().at("/log/origin/file/line").intValue()).isPositive();
202210
}
203211

204212
public boolean putMdc(String key, String value) {

ecs-logging-core/src/test/resources/spec/spec.json

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,15 @@
1515
"type": "string",
1616
"required": true,
1717
"index": 1,
18-
"nesting_allowed": false,
19-
"url": "https://www.elastic.co/guide/en/ecs/current/ecs-log.html"
18+
"top_level_field": true,
19+
"url": "https://www.elastic.co/guide/en/ecs/current/ecs-log.html",
20+
"comment": [
21+
"This field SHOULD NOT be a nested object field but at the top level with a dot in the property name.",
22+
"This is to make the JSON logs more human-readable.",
23+
"Loggers MAY indent the log level so that the `message` field always starts at the exact same offset,",
24+
"no matter the number of characters the log level has.",
25+
"For example: `'DEBUG'` (5 chars) will not be indented, whereas ` 'WARN'` (4 chars) will be indented by one space character."
26+
]
2027
},
2128
"message": {
2229
"type": "string",

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

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,16 +24,16 @@
2424
*/
2525
package co.elastic.logging.jul;
2626

27-
import static org.assertj.core.api.Assertions.assertThat;
27+
import com.fasterxml.jackson.databind.JsonNode;
28+
import com.fasterxml.jackson.databind.ObjectMapper;
29+
import org.junit.jupiter.api.BeforeEach;
30+
import org.junit.jupiter.api.Test;
2831

2932
import java.time.Instant;
3033
import java.util.logging.Level;
3134
import java.util.logging.LogRecord;
3235

33-
import com.fasterxml.jackson.databind.JsonNode;
34-
import com.fasterxml.jackson.databind.ObjectMapper;
35-
import org.junit.jupiter.api.BeforeEach;
36-
import org.junit.jupiter.api.Test;
36+
import static org.assertj.core.api.Assertions.assertThat;
3737

3838
public class EcsFormatterTest {
3939

@@ -57,8 +57,8 @@ public void testFormatWithIncludeOriginFlag() throws Exception {
5757

5858
final String result = formatter.format(record);
5959

60-
assertThat(objectMapper.readTree(result).get("log.origin").get("file.name").textValue()).isEqualTo("ExampleClass.java");
61-
assertThat(objectMapper.readTree(result).get("log.origin").get("function").textValue()).isEqualTo("exampleMethod");
60+
assertThat(objectMapper.readTree(result).at("/log/origin/file/name").textValue()).isEqualTo("ExampleClass.java");
61+
assertThat(objectMapper.readTree(result).at("/log/origin/function").textValue()).isEqualTo("exampleMethod");
6262
}
6363

6464
@Test
@@ -91,8 +91,8 @@ public void testFormatWithInnerClassName() throws Exception {
9191
record.setSourceClassName("test.ExampleClass$InnerClass");
9292

9393
JsonNode result = objectMapper.readTree(formatter.format(record));
94-
assertThat(result.get("log.origin").get("file.name").textValue()).isEqualTo("ExampleClass.java");
95-
assertThat(result.get("log.origin").get("function").textValue()).isEqualTo("exampleMethod");
94+
assertThat(result.at("/log/origin/file/name").textValue()).isEqualTo("ExampleClass.java");
95+
assertThat(result.at("/log/origin/function").textValue()).isEqualTo("exampleMethod");
9696
}
9797

9898
@Test
@@ -101,8 +101,8 @@ public void testFormatWithInvalidClassName() throws Exception {
101101
record.setSourceClassName("$test.ExampleClass");
102102

103103
JsonNode result = objectMapper.readTree(formatter.format(record));
104-
assertThat(result.get("log.origin").get("file.name").textValue()).isEqualTo("<Unknown>");
105-
assertThat(result.get("log.origin").get("function").textValue()).isEqualTo("exampleMethod");
104+
assertThat(result.at("/log/origin/file/name").textValue()).isEqualTo("<Unknown>");
105+
assertThat(result.at("/log/origin/function").textValue()).isEqualTo("exampleMethod");
106106
}
107107

108108
}

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

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,6 @@
4040
import java.util.logging.LogRecord;
4141
import java.util.logging.Logger;
4242
import java.util.logging.StreamHandler;
43-
import java.util.stream.Collectors;
44-
import java.util.stream.StreamSupport;
4543

4644
import static org.assertj.core.api.Assertions.assertThat;
4745

@@ -127,8 +125,8 @@ void setUp() {
127125
@Test
128126
void testLogOrigin() throws Exception {
129127
debug("test");
130-
assertThat(getAndValidateLastLogLine().get("log.origin").get("file.name").textValue()).endsWith(".java");
131-
assertThat(getAndValidateLastLogLine().get("log.origin").get("function").textValue()).isEqualTo("debug");
128+
assertThat(getAndValidateLastLogLine().at("/log/origin/file/name").textValue()).endsWith(".java");
129+
assertThat(getAndValidateLastLogLine().at("/log/origin/function").textValue()).isEqualTo("debug");
132130
//No file.line for JUL
133131
}
134132

0 commit comments

Comments
 (0)