Browse Source

Merge pull request #43768 from nosan

* pr/43768:
  Polish "Add marker information to ECS structured logging"
  Add marker information to ECS structured logging

Closes gh-43768
pull/43789/head
Moritz Halbritter 1 year ago
parent
commit
302756b042
  1. 24
      spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/ElasticCommonSchemaStructuredLogFormatter.java
  2. 27
      spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/logback/ElasticCommonSchemaStructuredLogFormatter.java
  3. 26
      spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/log4j2/ElasticCommonSchemaStructuredLogFormatterTests.java
  4. 27
      spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/logback/ElasticCommonSchemaStructuredLogFormatterTests.java

24
spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/ElasticCommonSchemaStructuredLogFormatter.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2012-2024 the original author or authors.
* Copyright 2012-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -17,8 +17,11 @@ @@ -17,8 +17,11 @@
package org.springframework.boot.logging.log4j2;
import java.util.Objects;
import java.util.Set;
import java.util.TreeSet;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.Marker;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.impl.ThrowableProxy;
import org.apache.logging.log4j.core.time.Instant;
@ -66,6 +69,10 @@ class ElasticCommonSchemaStructuredLogFormatter extends JsonWriterStructuredLogF @@ -66,6 +69,10 @@ class ElasticCommonSchemaStructuredLogFormatter extends JsonWriterStructuredLogF
thrownProxyMembers.add("error.message", ThrowableProxy::getMessage);
thrownProxyMembers.add("error.stack_trace", ThrowableProxy::getExtendedStackTraceAsString);
});
members.add("tags", LogEvent::getMarker)
.whenNotNull()
.as(ElasticCommonSchemaStructuredLogFormatter::getMarkers)
.whenNotEmpty();
members.add("ecs.version", "8.11");
}
@ -73,4 +80,19 @@ class ElasticCommonSchemaStructuredLogFormatter extends JsonWriterStructuredLogF @@ -73,4 +80,19 @@ class ElasticCommonSchemaStructuredLogFormatter extends JsonWriterStructuredLogF
return java.time.Instant.ofEpochMilli(instant.getEpochMillisecond()).plusNanos(instant.getNanoOfMillisecond());
}
private static Set<String> getMarkers(Marker marker) {
Set<String> result = new TreeSet<>();
addMarkers(result, marker);
return result;
}
private static void addMarkers(Set<String> result, Marker marker) {
result.add(marker.getName());
if (marker.hasParents()) {
for (Marker parent : marker.getParents()) {
addMarkers(result, parent);
}
}
}
}

27
spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/logback/ElasticCommonSchemaStructuredLogFormatter.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2012-2024 the original author or authors.
* Copyright 2012-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,11 +16,16 @@ @@ -16,11 +16,16 @@
package org.springframework.boot.logging.logback;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.TreeSet;
import ch.qos.logback.classic.pattern.ThrowableProxyConverter;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.classic.spi.IThrowableProxy;
import org.slf4j.Marker;
import org.slf4j.event.KeyValuePair;
import org.springframework.boot.json.JsonWriter;
@ -69,6 +74,26 @@ class ElasticCommonSchemaStructuredLogFormatter extends JsonWriterStructuredLogF @@ -69,6 +74,26 @@ class ElasticCommonSchemaStructuredLogFormatter extends JsonWriterStructuredLogF
throwableMembers.add("error.stack_trace", throwableProxyConverter::convert);
});
members.add("ecs.version", "8.11");
members.add("tags", ILoggingEvent::getMarkerList)
.whenNotNull()
.as(ElasticCommonSchemaStructuredLogFormatter::getMarkers)
.whenNotEmpty();
}
private static Set<String> getMarkers(List<Marker> markers) {
Set<String> result = new TreeSet<>();
addMarkers(result, markers.iterator());
return result;
}
private static void addMarkers(Set<String> result, Iterator<Marker> iterator) {
while (iterator.hasNext()) {
Marker marker = iterator.next();
result.add(marker.getName());
if (marker.hasReferences()) {
addMarkers(result, marker.iterator());
}
}
}
}

26
spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/log4j2/ElasticCommonSchemaStructuredLogFormatterTests.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2012-2024 the original author or authors.
* Copyright 2012-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,8 +16,11 @@ @@ -16,8 +16,11 @@
package org.springframework.boot.logging.log4j2;
import java.util.List;
import java.util.Map;
import org.apache.logging.log4j.Marker;
import org.apache.logging.log4j.MarkerManager;
import org.apache.logging.log4j.core.impl.JdkMapAdapterStringMap;
import org.apache.logging.log4j.core.impl.MutableLogEvent;
import org.apache.logging.log4j.message.MapMessage;
@ -100,4 +103,25 @@ class ElasticCommonSchemaStructuredLogFormatterTests extends AbstractStructuredL @@ -100,4 +103,25 @@ class ElasticCommonSchemaStructuredLogFormatterTests extends AbstractStructuredL
"org.example.Test", "message", expectedMessage, "ecs.version", "8.11"));
}
@Test
void shouldFormatMarkersAsTags() {
MutableLogEvent event = createEvent();
Marker parent = MarkerManager.getMarker("parent");
parent.addParents(MarkerManager.getMarker("grandparent"));
Marker parent1 = MarkerManager.getMarker("parent1");
parent1.addParents(MarkerManager.getMarker("grandparent1"));
Marker grandchild = MarkerManager.getMarker("grandchild");
grandchild.addParents(parent);
grandchild.addParents(parent1);
event.setMarker(grandchild);
String json = this.formatter.format(event);
assertThat(json).endsWith("\n");
Map<String, Object> deserialized = deserialize(json);
assertThat(deserialized).containsExactlyInAnyOrderEntriesOf(map("@timestamp", "2024-07-02T08:49:53Z",
"log.level", "INFO", "process.pid", 1, "process.thread.name", "main", "service.name", "name",
"service.version", "1.0.0", "service.environment", "test", "service.node.name", "node-1", "log.logger",
"org.example.Test", "message", "message", "ecs.version", "8.11", "tags",
List.of("grandchild", "grandparent", "grandparent1", "parent", "parent1")));
}
}

27
spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/logback/ElasticCommonSchemaStructuredLogFormatterTests.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2012-2024 the original author or authors.
* Copyright 2012-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -17,12 +17,15 @@ @@ -17,12 +17,15 @@
package org.springframework.boot.logging.logback;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import ch.qos.logback.classic.spi.LoggingEvent;
import ch.qos.logback.classic.spi.ThrowableProxy;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.slf4j.Marker;
import org.slf4j.MarkerFactory;
import org.springframework.mock.env.MockEnvironment;
@ -92,4 +95,26 @@ class ElasticCommonSchemaStructuredLogFormatterTests extends AbstractStructuredL @@ -92,4 +95,26 @@ class ElasticCommonSchemaStructuredLogFormatterTests extends AbstractStructuredL
.replace("\r", "\\r"));
}
@Test
void shouldFormatMarkersAsTags() {
LoggingEvent event = createEvent();
event.setMDCPropertyMap(Collections.emptyMap());
Marker parent = MarkerFactory.getDetachedMarker("parent");
parent.add(MarkerFactory.getDetachedMarker("child"));
Marker parent1 = MarkerFactory.getDetachedMarker("parent1");
parent1.add(MarkerFactory.getDetachedMarker("child1"));
Marker grandparent = MarkerFactory.getMarker("grandparent");
grandparent.add(parent);
grandparent.add(parent1);
event.addMarker(grandparent);
String json = this.formatter.format(event);
assertThat(json).endsWith("\n");
Map<String, Object> deserialized = deserialize(json);
assertThat(deserialized).containsExactlyInAnyOrderEntriesOf(
map("@timestamp", "2024-07-02T08:49:53Z", "log.level", "INFO", "process.pid", 1, "process.thread.name",
"main", "service.name", "name", "service.version", "1.0.0", "service.environment", "test",
"service.node.name", "node-1", "log.logger", "org.example.Test", "message", "message",
"ecs.version", "8.11", "tags", List.of("child", "child1", "grandparent", "parent", "parent1")));
}
}

Loading…
Cancel
Save