Skip to content

Commit 4c8b93b

Browse files
committed
Merge branch '2.7.x' into 3.0.x
Closes gh-35920
2 parents eeea065 + 137f4ee commit 4c8b93b

File tree

7 files changed

+402
-37
lines changed

7 files changed

+402
-37
lines changed

spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/logging/LoggersEndpoint.java

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2022 the original author or authors.
2+
* Copyright 2012-2023 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.
@@ -34,6 +34,8 @@
3434
import org.springframework.boot.actuate.logging.LoggersEndpoint.SingleLoggerLevelsDescriptor;
3535
import org.springframework.boot.logging.LogLevel;
3636
import org.springframework.boot.logging.LoggerConfiguration;
37+
import org.springframework.boot.logging.LoggerConfiguration.ConfigurationScope;
38+
import org.springframework.boot.logging.LoggerConfiguration.LevelConfiguration;
3739
import org.springframework.boot.logging.LoggerGroup;
3840
import org.springframework.boot.logging.LoggerGroups;
3941
import org.springframework.boot.logging.LoggingSystem;
@@ -164,7 +166,11 @@ public static class LoggerLevelsDescriptor implements OperationResponseBody {
164166
private final String configuredLevel;
165167

166168
public LoggerLevelsDescriptor(LogLevel configuredLevel) {
167-
this.configuredLevel = getName(configuredLevel);
169+
this.configuredLevel = (configuredLevel != null) ? configuredLevel.name() : null;
170+
}
171+
172+
LoggerLevelsDescriptor(LevelConfiguration directConfiguration) {
173+
this.configuredLevel = (directConfiguration != null) ? directConfiguration.getName() : null;
168174
}
169175

170176
protected final String getName(LogLevel level) {
@@ -203,8 +209,8 @@ public static class SingleLoggerLevelsDescriptor extends LoggerLevelsDescriptor
203209
private final String effectiveLevel;
204210

205211
public SingleLoggerLevelsDescriptor(LoggerConfiguration configuration) {
206-
super(configuration.getConfiguredLevel());
207-
this.effectiveLevel = getName(configuration.getEffectiveLevel());
212+
super(configuration.getLevelConfiguration(ConfigurationScope.DIRECT));
213+
this.effectiveLevel = configuration.getLevelConfiguration().getName();
208214
}
209215

210216
public String getEffectiveLevel() {

spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/logging/LoggersEndpointTests.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import org.springframework.boot.actuate.logging.LoggersEndpoint.SingleLoggerLevelsDescriptor;
3636
import org.springframework.boot.logging.LogLevel;
3737
import org.springframework.boot.logging.LoggerConfiguration;
38+
import org.springframework.boot.logging.LoggerConfiguration.LevelConfiguration;
3839
import org.springframework.boot.logging.LoggerGroups;
3940
import org.springframework.boot.logging.LoggingSystem;
4041

@@ -113,6 +114,17 @@ void loggerLevelsWhenNameSpecifiedShouldReturnLevels() {
113114
assertThat(levels.getEffectiveLevel()).isEqualTo("DEBUG");
114115
}
115116

117+
@Test // gh-35227
118+
void loggerLevelsWhenCustomLevelShouldReturnLevels() {
119+
given(this.loggingSystem.getLoggerConfiguration("ROOT"))
120+
.willReturn(new LoggerConfiguration("ROOT", null, LevelConfiguration.ofCustom("FINEST")));
121+
SingleLoggerLevelsDescriptor levels = (SingleLoggerLevelsDescriptor) new LoggersEndpoint(this.loggingSystem,
122+
this.loggerGroups)
123+
.loggerLevels("ROOT");
124+
assertThat(levels.getConfiguredLevel()).isNull();
125+
assertThat(levels.getEffectiveLevel()).isEqualTo("FINEST");
126+
}
127+
116128
@Test
117129
void groupNameSpecifiedShouldReturnConfiguredLevelAndMembers() {
118130
GroupLoggerLevelsDescriptor levels = (GroupLoggerLevelsDescriptor) new LoggersEndpoint(this.loggingSystem,
Lines changed: 169 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2022 the original author or authors.
2+
* Copyright 2012-2023 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.
@@ -16,22 +16,25 @@
1616

1717
package org.springframework.boot.logging;
1818

19+
import java.util.Objects;
20+
1921
import org.springframework.util.Assert;
2022
import org.springframework.util.ObjectUtils;
2123

2224
/**
2325
* Immutable class that represents the configuration of a {@link LoggingSystem}'s logger.
2426
*
2527
* @author Ben Hale
28+
* @author Phillip Webb
2629
* @since 1.5.0
2730
*/
2831
public final class LoggerConfiguration {
2932

3033
private final String name;
3134

32-
private final LogLevel configuredLevel;
35+
private final LevelConfiguration levelConfiguration;
3336

34-
private final LogLevel effectiveLevel;
37+
private final LevelConfiguration inheritedLevelConfiguration;
3538

3639
/**
3740
* Create a new {@link LoggerConfiguration instance}.
@@ -43,66 +46,204 @@ public LoggerConfiguration(String name, LogLevel configuredLevel, LogLevel effec
4346
Assert.notNull(name, "Name must not be null");
4447
Assert.notNull(effectiveLevel, "EffectiveLevel must not be null");
4548
this.name = name;
46-
this.configuredLevel = configuredLevel;
47-
this.effectiveLevel = effectiveLevel;
49+
this.levelConfiguration = (configuredLevel != null) ? LevelConfiguration.of(configuredLevel) : null;
50+
this.inheritedLevelConfiguration = LevelConfiguration.of(effectiveLevel);
51+
}
52+
53+
/**
54+
* Create a new {@link LoggerConfiguration instance}.
55+
* @param name the name of the logger
56+
* @param levelConfiguration the level configuration
57+
* @param inheritedLevelConfiguration the inherited level configuration
58+
* @since 2.7.13
59+
*/
60+
public LoggerConfiguration(String name, LevelConfiguration levelConfiguration,
61+
LevelConfiguration inheritedLevelConfiguration) {
62+
Assert.notNull(name, "Name must not be null");
63+
Assert.notNull(inheritedLevelConfiguration, "EffectiveLevelConfiguration must not be null");
64+
this.name = name;
65+
this.levelConfiguration = levelConfiguration;
66+
this.inheritedLevelConfiguration = inheritedLevelConfiguration;
67+
}
68+
69+
/**
70+
* Returns the name of the logger.
71+
* @return the name of the logger
72+
*/
73+
public String getName() {
74+
return this.name;
4875
}
4976

5077
/**
5178
* Returns the configured level of the logger.
5279
* @return the configured level of the logger
80+
* @see #getLevelConfiguration(ConfigurationScope)
5381
*/
5482
public LogLevel getConfiguredLevel() {
55-
return this.configuredLevel;
83+
LevelConfiguration configuration = getLevelConfiguration(ConfigurationScope.DIRECT);
84+
return (configuration != null) ? configuration.getLevel() : null;
5685
}
5786

5887
/**
5988
* Returns the effective level of the logger.
6089
* @return the effective level of the logger
90+
* @see #getLevelConfiguration(ConfigurationScope)
6191
*/
6292
public LogLevel getEffectiveLevel() {
63-
return this.effectiveLevel;
93+
return getLevelConfiguration().getLevel();
6494
}
6595

6696
/**
67-
* Returns the name of the logger.
68-
* @return the name of the logger
97+
* Return the level configuration, considering inherited loggers.
98+
* @return the level configuration
99+
* @since 2.7.13
69100
*/
70-
public String getName() {
71-
return this.name;
101+
public LevelConfiguration getLevelConfiguration() {
102+
return getLevelConfiguration(ConfigurationScope.INHERITED);
103+
}
104+
105+
/**
106+
* Return the level configuration for the given scope.
107+
* @param scope the configuration scope
108+
* @return the level configuration or {@code null} for
109+
* {@link ConfigurationScope#DIRECT direct scope} results without applied
110+
* configuration
111+
* @since 2.7.13
112+
*/
113+
public LevelConfiguration getLevelConfiguration(ConfigurationScope scope) {
114+
return (scope != ConfigurationScope.DIRECT) ? this.inheritedLevelConfiguration : this.levelConfiguration;
72115
}
73116

74117
@Override
75118
public boolean equals(Object obj) {
76119
if (this == obj) {
77120
return true;
78121
}
79-
if (obj == null) {
122+
if (obj == null || getClass() != obj.getClass()) {
80123
return false;
81124
}
82-
if (obj instanceof LoggerConfiguration other) {
83-
boolean rtn = true;
84-
rtn = rtn && ObjectUtils.nullSafeEquals(this.name, other.name);
85-
rtn = rtn && ObjectUtils.nullSafeEquals(this.configuredLevel, other.configuredLevel);
86-
rtn = rtn && ObjectUtils.nullSafeEquals(this.effectiveLevel, other.effectiveLevel);
87-
return rtn;
88-
}
89-
return super.equals(obj);
125+
LoggerConfiguration other = (LoggerConfiguration) obj;
126+
return ObjectUtils.nullSafeEquals(this.name, other.name)
127+
&& ObjectUtils.nullSafeEquals(this.levelConfiguration, other.levelConfiguration)
128+
&& ObjectUtils.nullSafeEquals(this.inheritedLevelConfiguration, other.inheritedLevelConfiguration);
90129
}
91130

92131
@Override
93132
public int hashCode() {
94-
final int prime = 31;
95-
int result = 1;
96-
result = prime * result + ObjectUtils.nullSafeHashCode(this.name);
97-
result = prime * result + ObjectUtils.nullSafeHashCode(this.configuredLevel);
98-
result = prime * result + ObjectUtils.nullSafeHashCode(this.effectiveLevel);
99-
return result;
133+
return Objects.hash(this.name, this.levelConfiguration, this.inheritedLevelConfiguration);
100134
}
101135

102136
@Override
103137
public String toString() {
104-
return "LoggerConfiguration [name=" + this.name + ", configuredLevel=" + this.configuredLevel
105-
+ ", effectiveLevel=" + this.effectiveLevel + "]";
138+
return "LoggerConfiguration [name=" + this.name + ", levelConfiguration=" + this.levelConfiguration
139+
+ ", inheritedLevelConfiguration=" + this.inheritedLevelConfiguration + "]";
140+
}
141+
142+
/**
143+
* Supported logger configurations scopes.
144+
*
145+
* @since 2.7.13
146+
*/
147+
public enum ConfigurationScope {
148+
149+
/**
150+
* Only return configuration that has been applied directly applied. Often
151+
* referred to as 'configured' or 'assigned' configuration.
152+
*/
153+
DIRECT,
154+
155+
/**
156+
* May return configuration that has been applied to a parent logger. Often
157+
* referred to as 'effective' configuration.
158+
*/
159+
INHERITED
160+
161+
}
162+
163+
/**
164+
* Logger level configuration.
165+
*
166+
* @since 2.7.13
167+
*/
168+
public static final class LevelConfiguration {
169+
170+
private final String name;
171+
172+
private final LogLevel logLevel;
173+
174+
private LevelConfiguration(String name, LogLevel logLevel) {
175+
this.name = name;
176+
this.logLevel = logLevel;
177+
}
178+
179+
/**
180+
* Return the name of the level.
181+
* @return the level name
182+
*/
183+
public String getName() {
184+
return this.name;
185+
}
186+
187+
/**
188+
* Return the actual level value if possible.
189+
* @return the level value
190+
* @throws IllegalStateException if this is a {@link #isCustom() custom} level
191+
*/
192+
public LogLevel getLevel() {
193+
Assert.state(this.logLevel != null, "Unable to provide LogLevel for '" + this.name + "'");
194+
return this.logLevel;
195+
}
196+
197+
/**
198+
* Return if this is a custom level and cannot be represented by {@link LogLevel}.
199+
* @return if this is a custom level
200+
*/
201+
public boolean isCustom() {
202+
return this.logLevel == null;
203+
}
204+
205+
@Override
206+
public boolean equals(Object obj) {
207+
if (this == obj) {
208+
return true;
209+
}
210+
if (obj == null || getClass() != obj.getClass()) {
211+
return false;
212+
}
213+
LevelConfiguration other = (LevelConfiguration) obj;
214+
return this.logLevel == other.logLevel && ObjectUtils.nullSafeEquals(this.name, other.name);
215+
}
216+
217+
@Override
218+
public int hashCode() {
219+
return Objects.hash(this.logLevel, this.name);
220+
}
221+
222+
@Override
223+
public String toString() {
224+
return "LevelConfiguration [name=" + this.name + ", logLevel=" + this.logLevel + "]";
225+
}
226+
227+
/**
228+
* Create a new {@link LevelConfiguration} instance of the given {@link LogLevel}.
229+
* @param logLevel the log level
230+
* @return a new {@link LevelConfiguration} instance
231+
*/
232+
public static LevelConfiguration of(LogLevel logLevel) {
233+
Assert.notNull(logLevel, "LogLevel must not be null");
234+
return new LevelConfiguration(logLevel.name(), logLevel);
235+
}
236+
237+
/**
238+
* Create a new {@link LevelConfiguration} instance for a custom level name.
239+
* @param name the log level name
240+
* @return a new {@link LevelConfiguration} instance
241+
*/
242+
public static LevelConfiguration ofCustom(String name) {
243+
Assert.hasText(name, "Name must not be empty");
244+
return new LevelConfiguration(name, null);
245+
}
246+
106247
}
107248

108249
}

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@
2626
*
2727
* @author HaiTao Zhang
2828
* @author Phillip Webb
29-
* @since 2.2.0 #see {@link LoggerGroup}
29+
* @since 2.2.0
30+
* @see LoggerGroup
3031
*/
3132
public final class LoggerGroups implements Iterable<LoggerGroup> {
3233

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

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
import org.springframework.boot.logging.LogFile;
6060
import org.springframework.boot.logging.LogLevel;
6161
import org.springframework.boot.logging.LoggerConfiguration;
62+
import org.springframework.boot.logging.LoggerConfiguration.LevelConfiguration;
6263
import org.springframework.boot.logging.LoggingInitializationContext;
6364
import org.springframework.boot.logging.LoggingSystem;
6465
import org.springframework.boot.logging.LoggingSystemFactory;
@@ -432,13 +433,18 @@ private LoggerConfiguration convertLoggerConfig(String name, LoggerConfig logger
432433
if (loggerConfig == null) {
433434
return null;
434435
}
435-
LogLevel level = LEVELS.convertNativeToSystem(loggerConfig.getLevel());
436+
LevelConfiguration effectiveLevelConfiguration = getLevelConfiguration(loggerConfig.getLevel());
436437
if (!StringUtils.hasLength(name) || LogManager.ROOT_LOGGER_NAME.equals(name)) {
437438
name = ROOT_LOGGER_NAME;
438439
}
439-
boolean isLoggerConfigured = loggerConfig.getName().equals(name);
440-
LogLevel configuredLevel = (isLoggerConfigured) ? level : null;
441-
return new LoggerConfiguration(name, configuredLevel, level);
440+
boolean isAssigned = loggerConfig.getName().equals(name);
441+
LevelConfiguration assignedLevelConfiguration = (!isAssigned) ? null : effectiveLevelConfiguration;
442+
return new LoggerConfiguration(name, assignedLevelConfiguration, effectiveLevelConfiguration);
443+
}
444+
445+
private LevelConfiguration getLevelConfiguration(Level level) {
446+
LogLevel logLevel = LEVELS.convertNativeToSystem(level);
447+
return (logLevel != null) ? LevelConfiguration.of(logLevel) : LevelConfiguration.ofCustom(level.name());
442448
}
443449

444450
@Override

0 commit comments

Comments
 (0)