Merge branch '7.0.x'

This commit is contained in:
Sam Brannen
2026-09-10 16:27:35 +02:00
2 changed files with 222 additions and 24 deletions
@@ -120,16 +120,20 @@ public class LogAccessor {
* @param message the message to log
*/
public void fatal(CharSequence message) {
this.log.fatal(message);
if (this.log.isFatalEnabled()) {
this.log.fatal(message);
}
}
/**
* Log an error with fatal log level.
* Log an exception with fatal log level.
* @param cause the exception to log
* @param message the message to log
*/
public void fatal(Throwable cause, CharSequence message) {
this.log.fatal(message, cause);
if (this.log.isFatalEnabled()) {
this.log.fatal(message, cause);
}
}
/**
@@ -137,16 +141,20 @@ public class LogAccessor {
* @param message the message to log
*/
public void error(CharSequence message) {
this.log.error(message);
if (this.log.isErrorEnabled()) {
this.log.error(message);
}
}
/**
* Log an error with error log level.
* Log an exception with error log level.
* @param cause the exception to log
* @param message the message to log
*/
public void error(Throwable cause, CharSequence message) {
this.log.error(message, cause);
if (this.log.isErrorEnabled()) {
this.log.error(message, cause);
}
}
/**
@@ -154,16 +162,20 @@ public class LogAccessor {
* @param message the message to log
*/
public void warn(CharSequence message) {
this.log.warn(message);
if (this.log.isWarnEnabled()) {
this.log.warn(message);
}
}
/**
* Log an error with warn log level.
* Log an exception with warn log level.
* @param cause the exception to log
* @param message the message to log
*/
public void warn(Throwable cause, CharSequence message) {
this.log.warn(message, cause);
if (this.log.isWarnEnabled()) {
this.log.warn(message, cause);
}
}
/**
@@ -171,16 +183,20 @@ public class LogAccessor {
* @param message the message to log
*/
public void info(CharSequence message) {
this.log.info(message);
if (this.log.isInfoEnabled()) {
this.log.info(message);
}
}
/**
* Log an error with info log level.
* Log an exception with info log level.
* @param cause the exception to log
* @param message the message to log
*/
public void info(Throwable cause, CharSequence message) {
this.log.info(message, cause);
if (this.log.isInfoEnabled()) {
this.log.info(message, cause);
}
}
/**
@@ -188,16 +204,20 @@ public class LogAccessor {
* @param message the message to log
*/
public void debug(CharSequence message) {
this.log.debug(message);
if (this.log.isDebugEnabled()) {
this.log.debug(message);
}
}
/**
* Log an error with debug log level.
* Log an exception with debug log level.
* @param cause the exception to log
* @param message the message to log
*/
public void debug(Throwable cause, CharSequence message) {
this.log.debug(message, cause);
if (this.log.isDebugEnabled()) {
this.log.debug(message, cause);
}
}
/**
@@ -205,16 +225,20 @@ public class LogAccessor {
* @param message the message to log
*/
public void trace(CharSequence message) {
this.log.trace(message);
if (this.log.isTraceEnabled()) {
this.log.trace(message);
}
}
/**
* Log an error with trace log level.
* Log an exception with trace log level.
* @param cause the exception to log
* @param message the message to log
*/
public void trace(Throwable cause, CharSequence message) {
this.log.trace(message, cause);
if (this.log.isTraceEnabled()) {
this.log.trace(message, cause);
}
}
@@ -231,7 +255,7 @@ public class LogAccessor {
}
/**
* Log an error with fatal log level.
* Log an exception with fatal log level.
* @param cause the exception to log
* @param messageSupplier a lazy supplier for the message to log
*/
@@ -252,7 +276,7 @@ public class LogAccessor {
}
/**
* Log an error with error log level.
* Log an exception with error log level.
* @param cause the exception to log
* @param messageSupplier a lazy supplier for the message to log
*/
@@ -273,7 +297,7 @@ public class LogAccessor {
}
/**
* Log an error with warn log level.
* Log an exception with warn log level.
* @param cause the exception to log
* @param messageSupplier a lazy supplier for the message to log
*/
@@ -294,7 +318,7 @@ public class LogAccessor {
}
/**
* Log an error with info log level.
* Log an exception with info log level.
* @param cause the exception to log
* @param messageSupplier a lazy supplier for the message to log
*/
@@ -315,7 +339,7 @@ public class LogAccessor {
}
/**
* Log an error with debug log level.
* Log an exception with debug log level.
* @param cause the exception to log
* @param messageSupplier a lazy supplier for the message to log
*/
@@ -336,7 +360,7 @@ public class LogAccessor {
}
/**
* Log an error with trace log level.
* Log an exception with trace log level.
* @param cause the exception to log
* @param messageSupplier a lazy supplier for the message to log
*/
@@ -0,0 +1,174 @@
/*
* Copyright 2002-present 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.core.log;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import org.apache.commons.logging.Log;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.FieldSource;
import org.mockito.stubbing.Answer;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.params.provider.Arguments.argumentSet;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
/**
* Tests for {@link LogAccessor}.
*
* @author Sam Brannen
* @since 7.0.10
*/
class LogAccessorTests {
private final Log log = mock();
private final LogAccessor logAccessor = new LogAccessor(this.log);
@BeforeEach
void renderMessagesWhenDelegatingToLog() {
// Simulate a Log implementation (such as commons-logging's SLF4J bridge) that renders the
// message unconditionally, in order to verify that LogAccessor itself guards against that.
Answer<Void> render = invocation -> {
invocation.getArgument(0).toString();
return null;
};
doAnswer(render).when(this.log).fatal(any());
doAnswer(render).when(this.log).fatal(any(), any());
doAnswer(render).when(this.log).error(any());
doAnswer(render).when(this.log).error(any(), any());
doAnswer(render).when(this.log).warn(any());
doAnswer(render).when(this.log).warn(any(), any());
doAnswer(render).when(this.log).info(any());
doAnswer(render).when(this.log).info(any(), any());
doAnswer(render).when(this.log).debug(any());
doAnswer(render).when(this.log).debug(any(), any());
doAnswer(render).when(this.log).trace(any());
doAnswer(render).when(this.log).trace(any(), any());
}
@ParameterizedTest
@FieldSource("levels")
void messageIsNotRenderedWhenLevelIsDisabled(LevelEnabler enabler, LogInvoker invoker) {
enabler.enable(this.log, false);
AtomicBoolean rendered = new AtomicBoolean();
invoker.invoke(this.logAccessor, lazyMessage(rendered));
assertThat(rendered).isFalse();
}
@ParameterizedTest
@FieldSource("levels")
void messageIsRenderedWhenLevelIsEnabled(LevelEnabler enabler, LogInvoker invoker) {
enabler.enable(this.log, true);
AtomicBoolean rendered = new AtomicBoolean();
invoker.invoke(this.logAccessor, lazyMessage(rendered));
assertThat(rendered).isTrue();
}
@ParameterizedTest
@FieldSource("levelsWithCause")
void messageWithCauseIsNotRenderedWhenLevelIsDisabled(LevelEnabler enabler, CauseLogInvoker invoker) {
enabler.enable(this.log, false);
AtomicBoolean rendered = new AtomicBoolean();
invoker.invoke(this.logAccessor, new RuntimeException(), lazyMessage(rendered));
assertThat(rendered).isFalse();
}
@ParameterizedTest
@FieldSource("levelsWithCause")
void messageWithCauseIsRenderedWhenLevelIsEnabled(LevelEnabler enabler, CauseLogInvoker invoker) {
enabler.enable(this.log, true);
AtomicBoolean rendered = new AtomicBoolean();
invoker.invoke(this.logAccessor, new RuntimeException(), lazyMessage(rendered));
assertThat(rendered).isTrue();
}
private static LogMessage lazyMessage(AtomicBoolean rendered) {
return LogMessage.of(() -> {
rendered.set(true);
return "message";
});
}
@SuppressWarnings("unused")
private static List<Arguments> levels = List.of(
argumentSet("fatal", (LevelEnabler) (log, enabled) -> when(log.isFatalEnabled()).thenReturn(enabled),
(LogInvoker) LogAccessor::fatal),
argumentSet("error", (LevelEnabler) (log, enabled) -> when(log.isErrorEnabled()).thenReturn(enabled),
(LogInvoker) LogAccessor::error),
argumentSet("warn", (LevelEnabler) (log, enabled) -> when(log.isWarnEnabled()).thenReturn(enabled),
(LogInvoker) LogAccessor::warn),
argumentSet("info", (LevelEnabler) (log, enabled) -> when(log.isInfoEnabled()).thenReturn(enabled),
(LogInvoker) LogAccessor::info),
argumentSet("debug", (LevelEnabler) (log, enabled) -> when(log.isDebugEnabled()).thenReturn(enabled),
(LogInvoker) LogAccessor::debug),
argumentSet("trace", (LevelEnabler) (log, enabled) -> when(log.isTraceEnabled()).thenReturn(enabled),
(LogInvoker) LogAccessor::trace)
);
@SuppressWarnings("unused")
private static List<Arguments> levelsWithCause = List.of(
argumentSet("fatal", (LevelEnabler) (log, enabled) -> when(log.isFatalEnabled()).thenReturn(enabled),
(CauseLogInvoker) LogAccessor::fatal),
argumentSet("error", (LevelEnabler) (log, enabled) -> when(log.isErrorEnabled()).thenReturn(enabled),
(CauseLogInvoker) LogAccessor::error),
argumentSet("warn", (LevelEnabler) (log, enabled) -> when(log.isWarnEnabled()).thenReturn(enabled),
(CauseLogInvoker) LogAccessor::warn),
argumentSet("info", (LevelEnabler) (log, enabled) -> when(log.isInfoEnabled()).thenReturn(enabled),
(CauseLogInvoker) LogAccessor::info),
argumentSet("debug", (LevelEnabler) (log, enabled) -> when(log.isDebugEnabled()).thenReturn(enabled),
(CauseLogInvoker) LogAccessor::debug),
argumentSet("trace", (LevelEnabler) (log, enabled) -> when(log.isTraceEnabled()).thenReturn(enabled),
(CauseLogInvoker) LogAccessor::trace)
);
@FunctionalInterface
private interface LevelEnabler {
void enable(Log log, boolean enabled);
}
@FunctionalInterface
private interface LogInvoker {
void invoke(LogAccessor logAccessor, CharSequence message);
}
@FunctionalInterface
private interface CauseLogInvoker {
void invoke(LogAccessor logAccessor, Throwable cause, CharSequence message);
}
}