Skip to content

Commit 06e3f37

Browse files
committed
Polish
1 parent a418943 commit 06e3f37

File tree

8 files changed

+109
-74
lines changed

8 files changed

+109
-74
lines changed

spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/opentelemetry/otlp/package-info.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2023 the original author or authors.
2+
* Copyright 2012-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.

spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/features/logging.adoc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,7 @@ To enable structured logging, set the property configprop:logging.structured.for
454454

455455
[[features.logging.structured.ecs]]
456456
=== Elastic Common Schema
457+
457458
https://www.elastic.co/guide/en/ecs/8.11/ecs-reference.html[Elastic Common Schema] is a JSON based logging format.
458459

459460
To enable the Elastic Common Schema log format, set the appropriate `format` property to `ecs`:
@@ -499,6 +500,7 @@ NOTE: configprop:logging.structured.ecs.service.version[] will default to config
499500

500501
[[features.logging.structured.gelf]]
501502
=== Graylog Extended Log Format (GELF)
503+
502504
https://go2docs.graylog.org/current/getting_in_log_data/gelf.html[Graylog Extended Log Format] is a JSON based logging format for the Graylog log analytics platform.
503505

504506
To enable the Graylog Extended Log Format, set the appropriate `format` property to `gelf`:

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/ConfigurationPropertiesBeanRegistrar.java

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -102,12 +102,11 @@ private BeanDefinitionHolder createBeanDefinition(String beanName, Class<?> type
102102

103103
static BeanDefinitionHolder applyScopedProxyMode(ScopeMetadata metadata, BeanDefinitionHolder definition,
104104
BeanDefinitionRegistry registry) {
105-
ScopedProxyMode scopedProxyMode = metadata.getScopedProxyMode();
106-
if (scopedProxyMode.equals(ScopedProxyMode.NO)) {
107-
return definition;
105+
ScopedProxyMode mode = metadata.getScopedProxyMode();
106+
if (mode != ScopedProxyMode.NO) {
107+
return ScopedProxyUtils.createScopedProxy(definition, registry, mode == ScopedProxyMode.TARGET_CLASS);
108108
}
109-
boolean proxyTargetClass = scopedProxyMode.equals(ScopedProxyMode.TARGET_CLASS);
110-
return ScopedProxyUtils.createScopedProxy(definition, registry, proxyTargetClass);
109+
return definition;
111110
}
112111

113112
}

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

Lines changed: 39 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import org.apache.logging.log4j.util.ReadOnlyStringMap;
3333

3434
import org.springframework.boot.json.JsonWriter;
35+
import org.springframework.boot.json.JsonWriter.Members;
3536
import org.springframework.boot.json.JsonWriter.WritableJson;
3637
import org.springframework.boot.logging.structured.CommonStructuredLogFormat;
3738
import org.springframework.boot.logging.structured.GraylogExtendedLogFormatService;
@@ -41,6 +42,7 @@
4142
import org.springframework.core.log.LogMessage;
4243
import org.springframework.util.Assert;
4344
import org.springframework.util.ObjectUtils;
45+
import org.springframework.util.StringUtils;
4446

4547
/**
4648
* Log4j2 {@link StructuredLogFormatter} for
@@ -60,12 +62,6 @@ class GraylogExtendedLogFormatStructuredLogFormatter extends JsonWriterStructure
6062
*/
6163
private static final Pattern FIELD_NAME_VALID_PATTERN = Pattern.compile("^[\\w.\\-]*$");
6264

63-
/**
64-
* Every field been sent and prefixed with an underscore "_" will be treated as an
65-
* additional field.
66-
*/
67-
private static final String ADDITIONAL_FIELD_PREFIX = "_";
68-
6965
/**
7066
* Libraries SHOULD not allow to send id as additional field ("_id"). Graylog server
7167
* nodes omit this field automatically.
@@ -78,9 +74,8 @@ class GraylogExtendedLogFormatStructuredLogFormatter extends JsonWriterStructure
7874

7975
private static void jsonMembers(Environment environment, JsonWriter.Members<LogEvent> members) {
8076
members.add("version", "1.1");
81-
// note: a blank message will lead to a Graylog error as of Graylog v6.0.x. We are
82-
// ignoring this here.
83-
members.add("short_message", LogEvent::getMessage).as(Message::getFormattedMessage);
77+
members.add("short_message", LogEvent::getMessage)
78+
.as(GraylogExtendedLogFormatStructuredLogFormatter::getMessageText);
8479
members.add("timestamp", LogEvent::getInstant)
8580
.as(GraylogExtendedLogFormatStructuredLogFormatter::formatTimeStamp);
8681
members.add("level", GraylogExtendedLogFormatStructuredLogFormatter::convertLevel);
@@ -92,25 +87,23 @@ private static void jsonMembers(Environment environment, JsonWriter.Members<LogE
9287
members.add("_log_logger", LogEvent::getLoggerName);
9388
members.from(LogEvent::getContextData)
9489
.whenNot(ReadOnlyStringMap::isEmpty)
95-
.usingPairs((contextData, pairs) -> contextData
96-
.forEach((key, value) -> createAdditionalField(key, value, pairs)));
97-
members.add().whenNotNull(LogEvent::getThrownProxy).usingMembers((eventMembers) -> {
98-
eventMembers.add("full_message",
99-
GraylogExtendedLogFormatStructuredLogFormatter::formatFullMessageWithThrowable);
100-
eventMembers.add("_error_type", (event) -> event.getThrownProxy().getThrowable())
101-
.whenNotNull()
102-
.as(ObjectUtils::nullSafeClassName);
103-
eventMembers.add("_error_stack_trace", (event) -> event.getThrownProxy().getExtendedStackTraceAsString());
104-
eventMembers.add("_error_message", (event) -> event.getThrownProxy().getMessage());
105-
});
90+
.usingPairs(GraylogExtendedLogFormatStructuredLogFormatter::createAdditionalFields);
91+
members.add()
92+
.whenNotNull(LogEvent::getThrownProxy)
93+
.usingMembers(GraylogExtendedLogFormatStructuredLogFormatter::throwableMembers);
94+
}
95+
96+
private static String getMessageText(Message message) {
97+
// Always return text as a blank message will lead to a error as of Graylog v6
98+
String formattedMessage = message.getFormattedMessage();
99+
return (!StringUtils.hasText(formattedMessage)) ? "(blank)" : formattedMessage;
106100
}
107101

108102
/**
109103
* GELF requires "seconds since UNIX epoch with optional <b>decimal places for
110104
* milliseconds</b>". To comply with this requirement, we format a POSIX timestamp
111105
* with millisecond precision as e.g. "1725459730385" -> "1725459730.385"
112-
* @param timeStamp the timestamp of the log message. Note it is not the standard Java
113-
* `Instant` type but {@link org.apache.logging.log4j.core.time}
106+
* @param timeStamp the timestamp of the log message.
114107
* @return the timestamp formatted as string with millisecond precision
115108
*/
116109
private static WritableJson formatTimeStamp(Instant timeStamp) {
@@ -127,23 +120,39 @@ private static int convertLevel(LogEvent event) {
127120
return Severity.getSeverity(event.getLevel()).getCode();
128121
}
129122

123+
private static void throwableMembers(Members<LogEvent> members) {
124+
members.add("full_message", GraylogExtendedLogFormatStructuredLogFormatter::formatFullMessageWithThrowable);
125+
members.add("_error_type", (event) -> event.getThrownProxy().getThrowable())
126+
.whenNotNull()
127+
.as(ObjectUtils::nullSafeClassName);
128+
members.add("_error_stack_trace", (event) -> event.getThrownProxy().getExtendedStackTraceAsString());
129+
members.add("_error_message", (event) -> event.getThrownProxy().getMessage());
130+
}
131+
130132
private static String formatFullMessageWithThrowable(LogEvent event) {
131133
return event.getMessage().getFormattedMessage() + "\n\n"
132134
+ event.getThrownProxy().getExtendedStackTraceAsString();
133135
}
134136

135-
private static void createAdditionalField(String fieldName, Object value, BiConsumer<Object, Object> pairs) {
136-
Assert.notNull(fieldName, "fieldName must not be null");
137-
if (!FIELD_NAME_VALID_PATTERN.matcher(fieldName).matches()) {
138-
logger.warn(LogMessage.format("'%s' is not a valid field name according to GELF standard", fieldName));
137+
private static void createAdditionalFields(ReadOnlyStringMap contextData, BiConsumer<Object, Object> pairs) {
138+
contextData.forEach((name, value) -> createAdditionalField(name, value, pairs));
139+
}
140+
141+
private static void createAdditionalField(String name, Object value, BiConsumer<Object, Object> pairs) {
142+
Assert.notNull(name, "fieldName must not be null");
143+
if (!FIELD_NAME_VALID_PATTERN.matcher(name).matches()) {
144+
logger.warn(LogMessage.format("'%s' is not a valid field name according to GELF standard", name));
139145
return;
140146
}
141-
if (ADDITIONAL_FIELD_ILLEGAL_KEYS.contains(fieldName)) {
142-
logger.warn(LogMessage.format("'%s' is an illegal field name according to GELF standard", fieldName));
147+
if (ADDITIONAL_FIELD_ILLEGAL_KEYS.contains(name)) {
148+
logger.warn(LogMessage.format("'%s' is an illegal field name according to GELF standard", name));
143149
return;
144150
}
145-
String key = (fieldName.startsWith(ADDITIONAL_FIELD_PREFIX)) ? fieldName : ADDITIONAL_FIELD_PREFIX + fieldName;
146-
pairs.accept(key, value);
151+
pairs.accept(asAdditionalFieldName(name), value);
152+
}
153+
154+
private static Object asAdditionalFieldName(String name) {
155+
return (!name.startsWith("_")) ? "_" + name : name;
147156
}
148157

149158
}

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

Lines changed: 38 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package org.springframework.boot.logging.logback;
1818

1919
import java.math.BigDecimal;
20+
import java.util.List;
2021
import java.util.Objects;
2122
import java.util.Set;
2223
import java.util.function.BiConsumer;
@@ -28,8 +29,10 @@
2829
import ch.qos.logback.classic.util.LevelToSyslogSeverity;
2930
import org.apache.commons.logging.Log;
3031
import org.apache.commons.logging.LogFactory;
32+
import org.slf4j.event.KeyValuePair;
3133

3234
import org.springframework.boot.json.JsonWriter;
35+
import org.springframework.boot.json.JsonWriter.Members;
3336
import org.springframework.boot.json.JsonWriter.WritableJson;
3437
import org.springframework.boot.logging.structured.CommonStructuredLogFormat;
3538
import org.springframework.boot.logging.structured.GraylogExtendedLogFormatService;
@@ -39,6 +42,7 @@
3942
import org.springframework.core.log.LogMessage;
4043
import org.springframework.util.Assert;
4144
import org.springframework.util.CollectionUtils;
45+
import org.springframework.util.StringUtils;
4246

4347
/**
4448
* Logback {@link StructuredLogFormatter} for
@@ -58,12 +62,6 @@ class GraylogExtendedLogFormatStructuredLogFormatter extends JsonWriterStructure
5862
*/
5963
private static final Pattern FIELD_NAME_VALID_PATTERN = Pattern.compile("^[\\w.\\-]*$");
6064

61-
/**
62-
* Every field been sent and prefixed with an underscore "_" will be treated as an
63-
* additional field.
64-
*/
65-
private static final String ADDITIONAL_FIELD_PREFIX = "_";
66-
6765
/**
6866
* Libraries SHOULD not allow to send id as additional field ("_id"). Graylog server
6967
* nodes omit this field automatically.
@@ -78,9 +76,8 @@ class GraylogExtendedLogFormatStructuredLogFormatter extends JsonWriterStructure
7876
private static void jsonMembers(Environment environment, ThrowableProxyConverter throwableProxyConverter,
7977
JsonWriter.Members<ILoggingEvent> members) {
8078
members.add("version", "1.1");
81-
// note: a blank message will lead to a Graylog error as of Graylog v6.0.x. We are
82-
// ignoring this here.
83-
members.add("short_message", ILoggingEvent::getFormattedMessage);
79+
members.add("short_message", ILoggingEvent::getFormattedMessage)
80+
.as(GraylogExtendedLogFormatStructuredLogFormatter::getMessageText);
8481
members.add("timestamp", ILoggingEvent::getTimeStamp)
8582
.as(GraylogExtendedLogFormatStructuredLogFormatter::formatTimeStamp);
8683
members.add("level", LevelToSyslogSeverity::convert);
@@ -95,15 +92,15 @@ private static void jsonMembers(Environment environment, ThrowableProxyConverter
9592
.usingPairs((mdc, pairs) -> mdc.forEach((key, value) -> createAdditionalField(key, value, pairs)));
9693
members.from(ILoggingEvent::getKeyValuePairs)
9794
.when((keyValuePairs) -> !CollectionUtils.isEmpty(keyValuePairs))
98-
.usingPairs((keyValuePairs, pairs) -> keyValuePairs
99-
.forEach((keyValuePair) -> createAdditionalField(keyValuePair.key, keyValuePair.value, pairs)));
100-
members.add().whenNotNull(ILoggingEvent::getThrowableProxy).usingMembers((throwableMembers) -> {
101-
throwableMembers.add("full_message",
102-
(event) -> formatFullMessageWithThrowable(throwableProxyConverter, event));
103-
throwableMembers.add("_error_type", ILoggingEvent::getThrowableProxy).as(IThrowableProxy::getClassName);
104-
throwableMembers.add("_error_stack_trace", throwableProxyConverter::convert);
105-
throwableMembers.add("_error_message", ILoggingEvent::getThrowableProxy).as(IThrowableProxy::getMessage);
106-
});
95+
.usingPairs(GraylogExtendedLogFormatStructuredLogFormatter::createAdditionalField);
96+
members.add()
97+
.whenNotNull(ILoggingEvent::getThrowableProxy)
98+
.usingMembers((throwableMembers) -> throwableMembers(throwableMembers, throwableProxyConverter));
99+
}
100+
101+
private static String getMessageText(String formattedMessage) {
102+
// Always return text as a blank message will lead to a error as of Graylog v6
103+
return (!StringUtils.hasText(formattedMessage)) ? "(blank)" : formattedMessage;
107104
}
108105

109106
/**
@@ -117,23 +114,38 @@ private static WritableJson formatTimeStamp(long timeStamp) {
117114
return (out) -> out.append(new BigDecimal(timeStamp).movePointLeft(3).toPlainString());
118115
}
119116

117+
private static void throwableMembers(Members<ILoggingEvent> members,
118+
ThrowableProxyConverter throwableProxyConverter) {
119+
members.add("full_message", (event) -> formatFullMessageWithThrowable(throwableProxyConverter, event));
120+
members.add("_error_type", ILoggingEvent::getThrowableProxy).as(IThrowableProxy::getClassName);
121+
members.add("_error_stack_trace", throwableProxyConverter::convert);
122+
members.add("_error_message", ILoggingEvent::getThrowableProxy).as(IThrowableProxy::getMessage);
123+
}
124+
120125
private static String formatFullMessageWithThrowable(ThrowableProxyConverter throwableProxyConverter,
121126
ILoggingEvent event) {
122127
return event.getFormattedMessage() + "\n\n" + throwableProxyConverter.convert(event);
123128
}
124129

125-
private static void createAdditionalField(String key, Object value, BiConsumer<Object, Object> pairs) {
126-
Assert.notNull(key, "fieldName must not be null");
127-
if (!FIELD_NAME_VALID_PATTERN.matcher(key).matches()) {
128-
logger.warn(LogMessage.format("'%s' is not a valid field name according to GELF standard", key));
130+
private static void createAdditionalField(List<KeyValuePair> keyValuePairs, BiConsumer<Object, Object> pairs) {
131+
keyValuePairs.forEach((keyValuePair) -> createAdditionalField(keyValuePair.key, keyValuePair.value, pairs));
132+
}
133+
134+
private static void createAdditionalField(String name, Object value, BiConsumer<Object, Object> pairs) {
135+
Assert.notNull(name, "fieldName must not be null");
136+
if (!FIELD_NAME_VALID_PATTERN.matcher(name).matches()) {
137+
logger.warn(LogMessage.format("'%s' is not a valid field name according to GELF standard", name));
129138
return;
130139
}
131-
if (ADDITIONAL_FIELD_ILLEGAL_KEYS.contains(key)) {
132-
logger.warn(LogMessage.format("'%s' is an illegal field name according to GELF standard", key));
140+
if (ADDITIONAL_FIELD_ILLEGAL_KEYS.contains(name)) {
141+
logger.warn(LogMessage.format("'%s' is an illegal field name according to GELF standard", name));
133142
return;
134143
}
135-
String keyWithPrefix = (key.startsWith(ADDITIONAL_FIELD_PREFIX)) ? key : ADDITIONAL_FIELD_PREFIX + key;
136-
pairs.accept(keyWithPrefix, value);
144+
pairs.accept(asAdditionalFieldName(name), value);
145+
}
146+
147+
private static Object asAdditionalFieldName(String name) {
148+
return (!name.startsWith("_")) ? "_" + name : name;
137149
}
138150

139151
}

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

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import org.springframework.boot.logging.structured.StructuredLogFormatter;
2929
import org.springframework.boot.logging.structured.StructuredLogFormatterFactory;
3030
import org.springframework.boot.logging.structured.StructuredLogFormatterFactory.CommonFormatters;
31+
import org.springframework.boot.util.Instantiator;
3132
import org.springframework.boot.util.Instantiator.AvailableParameters;
3233
import org.springframework.core.env.Environment;
3334
import org.springframework.util.Assert;
@@ -79,16 +80,29 @@ private void addAvailableParameters(AvailableParameters availableParameters) {
7980
}
8081

8182
private void addCommonFormatters(CommonFormatters<ILoggingEvent> commonFormatters) {
82-
commonFormatters.add(CommonStructuredLogFormat.ELASTIC_COMMON_SCHEMA,
83-
(instantiator) -> new ElasticCommonSchemaStructuredLogFormatter(instantiator.getArg(Environment.class),
84-
instantiator.getArg(ThrowableProxyConverter.class)));
85-
commonFormatters
86-
.add(CommonStructuredLogFormat.GRAYLOG_EXTENDED_LOG_FORMAT,
87-
(instantiator) -> new GraylogExtendedLogFormatStructuredLogFormatter(
88-
instantiator.getArg(Environment.class),
89-
instantiator.getArg(ThrowableProxyConverter.class)));
90-
commonFormatters.add(CommonStructuredLogFormat.LOGSTASH, (instantiator) -> new LogstashStructuredLogFormatter(
91-
instantiator.getArg(ThrowableProxyConverter.class)));
83+
commonFormatters.add(CommonStructuredLogFormat.ELASTIC_COMMON_SCHEMA, this::createEcsFormatter);
84+
commonFormatters.add(CommonStructuredLogFormat.GRAYLOG_EXTENDED_LOG_FORMAT, this::createGraylogFormatter);
85+
commonFormatters.add(CommonStructuredLogFormat.LOGSTASH, this::createLogstashFormatter);
86+
}
87+
88+
private StructuredLogFormatter<ILoggingEvent> createEcsFormatter(
89+
Instantiator<StructuredLogFormatter<ILoggingEvent>> instantiator) {
90+
Environment environment = instantiator.getArg(Environment.class);
91+
ThrowableProxyConverter throwableProxyConverter = instantiator.getArg(ThrowableProxyConverter.class);
92+
return new ElasticCommonSchemaStructuredLogFormatter(environment, throwableProxyConverter);
93+
}
94+
95+
private StructuredLogFormatter<ILoggingEvent> createGraylogFormatter(
96+
Instantiator<StructuredLogFormatter<ILoggingEvent>> instantiator) {
97+
Environment environment = instantiator.getArg(Environment.class);
98+
ThrowableProxyConverter throwableProxyConverter = instantiator.getArg(ThrowableProxyConverter.class);
99+
return new GraylogExtendedLogFormatStructuredLogFormatter(environment, throwableProxyConverter);
100+
}
101+
102+
private StructuredLogFormatter<ILoggingEvent> createLogstashFormatter(
103+
Instantiator<StructuredLogFormatter<ILoggingEvent>> instantiator) {
104+
ThrowableProxyConverter throwableProxyConverter = instantiator.getArg(ThrowableProxyConverter.class);
105+
return new LogstashStructuredLogFormatter(throwableProxyConverter);
92106
}
93107

94108
@Override

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/structured/GraylogExtendedLogFormatService.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,6 @@ private String withFallbackProperty(Environment environment, String value, Strin
4848
* @param members the members to add to
4949
*/
5050
public void jsonMembers(JsonWriter.Members<?> members) {
51-
// note "host" is a field name prescribed by GELF
5251
members.add("host", this::name).whenHasLength();
5352
members.add("_service_version", this::version).whenHasLength();
5453
}
@@ -65,4 +64,5 @@ public static GraylogExtendedLogFormatService get(Environment environment) {
6564
.orElse(NONE)
6665
.withDefaults(environment);
6766
}
67+
6868
}

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,6 @@ void shouldFormatException() {
134134
.formatted());
135135
assertThat(deserialized)
136136
.containsAllEntriesOf(map("_error_type", "java.lang.RuntimeException", "_error_message", "Boom"));
137-
138137
assertThat(stackTrace).startsWith(
139138
"java.lang.RuntimeException: Boom%n\tat org.springframework.boot.logging.logback.GraylogExtendedLogFormatStructuredLogFormatterTests.shouldFormatException"
140139
.formatted());

0 commit comments

Comments
 (0)