First-class support for printf-style format strings in LogMessage
LogMessage is an abstract class now: with internal subclasses for Supplier bindings as well as printf-style format strings with a variable number of arguments (some fixed for efficiency, varargs array as fallback), created through corresponding static factory methods. Closes gh-22726
This commit is contained in:
parent
5616eb2e8e
commit
9080ae24f9
|
|
@ -180,7 +180,7 @@ public class LogAccessor {
|
|||
* @param messageSupplier a lazy supplier for the message to log
|
||||
*/
|
||||
public void fatal(Supplier<? extends CharSequence> messageSupplier) {
|
||||
this.log.fatal(new LogMessage(messageSupplier));
|
||||
this.log.fatal(LogMessage.lazy(messageSupplier));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -189,7 +189,7 @@ public class LogAccessor {
|
|||
* @param messageSupplier a lazy supplier for the message to log
|
||||
*/
|
||||
public void fatal(Throwable cause, Supplier<? extends CharSequence> messageSupplier) {
|
||||
this.log.fatal(new LogMessage(messageSupplier), cause);
|
||||
this.log.fatal(LogMessage.lazy(messageSupplier), cause);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -197,7 +197,7 @@ public class LogAccessor {
|
|||
* @param messageSupplier a lazy supplier for the message to log
|
||||
*/
|
||||
public void error(Supplier<? extends CharSequence> messageSupplier) {
|
||||
this.log.error(new LogMessage(messageSupplier));
|
||||
this.log.error(LogMessage.lazy(messageSupplier));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -206,7 +206,7 @@ public class LogAccessor {
|
|||
* @param messageSupplier a lazy supplier for the message to log
|
||||
*/
|
||||
public void error(Throwable cause, Supplier<? extends CharSequence> messageSupplier) {
|
||||
this.log.error(new LogMessage(messageSupplier), cause);
|
||||
this.log.error(LogMessage.lazy(messageSupplier), cause);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -214,7 +214,7 @@ public class LogAccessor {
|
|||
* @param messageSupplier a lazy supplier for the message to log
|
||||
*/
|
||||
public void warn(Supplier<? extends CharSequence> messageSupplier) {
|
||||
this.log.warn(new LogMessage(messageSupplier));
|
||||
this.log.warn(LogMessage.lazy(messageSupplier));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -223,7 +223,7 @@ public class LogAccessor {
|
|||
* @param messageSupplier a lazy supplier for the message to log
|
||||
*/
|
||||
public void warn(Throwable cause, Supplier<? extends CharSequence> messageSupplier) {
|
||||
this.log.warn(new LogMessage(messageSupplier), cause);
|
||||
this.log.warn(LogMessage.lazy(messageSupplier), cause);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -231,7 +231,7 @@ public class LogAccessor {
|
|||
* @param messageSupplier a lazy supplier for the message to log
|
||||
*/
|
||||
public void info(Supplier<? extends CharSequence> messageSupplier) {
|
||||
this.log.info(new LogMessage(messageSupplier));
|
||||
this.log.info(LogMessage.lazy(messageSupplier));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -240,7 +240,7 @@ public class LogAccessor {
|
|||
* @param messageSupplier a lazy supplier for the message to log
|
||||
*/
|
||||
public void info(Throwable cause, Supplier<? extends CharSequence> messageSupplier) {
|
||||
this.log.info(new LogMessage(messageSupplier), cause);
|
||||
this.log.info(LogMessage.lazy(messageSupplier), cause);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -248,7 +248,7 @@ public class LogAccessor {
|
|||
* @param messageSupplier a lazy supplier for the message to log
|
||||
*/
|
||||
public void debug(Supplier<? extends CharSequence> messageSupplier) {
|
||||
this.log.debug(new LogMessage(messageSupplier));
|
||||
this.log.debug(LogMessage.lazy(messageSupplier));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -257,7 +257,7 @@ public class LogAccessor {
|
|||
* @param messageSupplier a lazy supplier for the message to log
|
||||
*/
|
||||
public void debug(Throwable cause, Supplier<? extends CharSequence> messageSupplier) {
|
||||
this.log.debug(new LogMessage(messageSupplier), cause);
|
||||
this.log.debug(LogMessage.lazy(messageSupplier), cause);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -265,7 +265,7 @@ public class LogAccessor {
|
|||
* @param messageSupplier a lazy supplier for the message to log
|
||||
*/
|
||||
public void trace(Supplier<? extends CharSequence> messageSupplier) {
|
||||
this.log.trace(new LogMessage(messageSupplier));
|
||||
this.log.trace(LogMessage.lazy(messageSupplier));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -274,7 +274,7 @@ public class LogAccessor {
|
|||
* @param messageSupplier a lazy supplier for the message to log
|
||||
*/
|
||||
public void trace(Throwable cause, Supplier<? extends CharSequence> messageSupplier) {
|
||||
this.log.trace(new LogMessage(messageSupplier), cause);
|
||||
this.log.trace(LogMessage.lazy(messageSupplier), cause);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,11 +23,15 @@ import org.springframework.util.Assert;
|
|||
|
||||
/**
|
||||
* A simple log message type for use with Commons Logging, allowing
|
||||
* for convenient late resolution of a given {@link Supplier} instance
|
||||
* (typically bound to a Java 8 lambda expression) in {@link #toString()}.
|
||||
* for convenient lazy resolution of a given {@link Supplier} instance
|
||||
* (typically bound to a Java 8 lambda expression) or a printf-style
|
||||
* format string ({@link String#format})in its {@link #toString()}.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 5.2
|
||||
* @see #lazy(Supplier)
|
||||
* @see #format(String, Object)
|
||||
* @see #format(String, Object...)
|
||||
* @see org.apache.commons.logging.Log#fatal(Object)
|
||||
* @see org.apache.commons.logging.Log#error(Object)
|
||||
* @see org.apache.commons.logging.Log#warn(Object)
|
||||
|
|
@ -35,24 +39,26 @@ import org.springframework.util.Assert;
|
|||
* @see org.apache.commons.logging.Log#debug(Object)
|
||||
* @see org.apache.commons.logging.Log#trace(Object)
|
||||
*/
|
||||
public class LogMessage {
|
||||
|
||||
private final Supplier<? extends CharSequence> supplier;
|
||||
public abstract class LogMessage implements CharSequence {
|
||||
|
||||
@Nullable
|
||||
private String result;
|
||||
|
||||
|
||||
/**
|
||||
* Construct a new {@code LogMessage} for the given supplier.
|
||||
* @param supplier the lazily resolving supplier
|
||||
* (typically bound to a Java 8 lambda expression)
|
||||
*/
|
||||
public LogMessage(Supplier<? extends CharSequence> supplier) {
|
||||
Assert.notNull(supplier, "Supplier must not be null");
|
||||
this.supplier = supplier;
|
||||
@Override
|
||||
public int length() {
|
||||
return toString().length();
|
||||
}
|
||||
|
||||
@Override
|
||||
public char charAt(int index) {
|
||||
return toString().charAt(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence subSequence(int start, int end) {
|
||||
return toString().subSequence(start, end);
|
||||
}
|
||||
|
||||
/**
|
||||
* This will be called by the logging provider, potentially once
|
||||
|
|
@ -61,9 +67,202 @@ public class LogMessage {
|
|||
@Override
|
||||
public String toString() {
|
||||
if (this.result == null) {
|
||||
this.result = this.supplier.get().toString();
|
||||
this.result = buildString();
|
||||
}
|
||||
return this.result;
|
||||
}
|
||||
|
||||
abstract String buildString();
|
||||
|
||||
|
||||
/**
|
||||
* Build a lazy resolution message from the given supplier.
|
||||
* @param supplier the supplier (typically bound to a Java 8 lambda expression)
|
||||
* @see #toString()
|
||||
*/
|
||||
public static LogMessage lazy(Supplier<? extends CharSequence> supplier) {
|
||||
return new LazyMessage(supplier);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a formatted message from the given format string and argument.
|
||||
* @param format the format string (following {@link String#format} rules)
|
||||
* @param arg1 the argument
|
||||
* @see String#format(String, Object...)
|
||||
*/
|
||||
public static LogMessage format(String format, Object arg1) {
|
||||
return new FormatMessage1(format, arg1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a formatted message from the given format string and arguments.
|
||||
* @param format the format string (following {@link String#format} rules)
|
||||
* @param arg1 the first argument
|
||||
* @param arg2 the second argument
|
||||
* @see String#format(String, Object...)
|
||||
*/
|
||||
public static LogMessage format(String format, Object arg1, Object arg2) {
|
||||
return new FormatMessage2(format, arg1, arg2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a formatted message from the given format string and arguments.
|
||||
* @param format the format string (following {@link String#format} rules)
|
||||
* @param arg1 the first argument
|
||||
* @param arg2 the second argument
|
||||
* @param arg3 the third argument
|
||||
* @see String#format(String, Object...)
|
||||
*/
|
||||
public static LogMessage format(String format, Object arg1, Object arg2, Object arg3) {
|
||||
return new FormatMessage3(format, arg1, arg2, arg3);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a formatted message from the given format string and arguments.
|
||||
* @param format the format string (following {@link String#format} rules)
|
||||
* @param arg1 the first argument
|
||||
* @param arg2 the second argument
|
||||
* @param arg3 the third argument
|
||||
* @param arg4 the fourth argument
|
||||
* @see String#format(String, Object...)
|
||||
*/
|
||||
public static LogMessage format(String format, Object arg1, Object arg2, Object arg3, Object arg4) {
|
||||
return new FormatMessage4(format, arg1, arg2, arg3, arg4);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a formatted message from the given format string and varargs.
|
||||
* @param format the format string (following {@link String#format} rules)
|
||||
* @param args the varargs array (costly, prefer individual arguments)
|
||||
* @see String#format(String, Object...)
|
||||
*/
|
||||
public static LogMessage format(String format, Object... args) {
|
||||
return new FormatMessageX(format, args);
|
||||
}
|
||||
|
||||
|
||||
private static final class LazyMessage extends LogMessage {
|
||||
|
||||
private Supplier<? extends CharSequence> supplier;
|
||||
|
||||
LazyMessage(Supplier<? extends CharSequence> supplier) {
|
||||
Assert.notNull(supplier, "Supplier must not be null");
|
||||
this.supplier = supplier;
|
||||
}
|
||||
|
||||
@Override
|
||||
String buildString() {
|
||||
return this.supplier.get().toString();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static abstract class FormatMessage extends LogMessage {
|
||||
|
||||
protected final String format;
|
||||
|
||||
FormatMessage(String format) {
|
||||
Assert.notNull(format, "Format must not be null");
|
||||
this.format = format;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static final class FormatMessage1 extends FormatMessage {
|
||||
|
||||
private final Object arg1;
|
||||
|
||||
FormatMessage1(String format, Object arg1) {
|
||||
super(format);
|
||||
this.arg1 = arg1;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String buildString() {
|
||||
return String.format(this.format, this.arg1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static final class FormatMessage2 extends FormatMessage {
|
||||
|
||||
private final Object arg1;
|
||||
|
||||
private final Object arg2;
|
||||
|
||||
FormatMessage2(String format, Object arg1, Object arg2) {
|
||||
super(format);
|
||||
this.arg1 = arg1;
|
||||
this.arg2 = arg2;
|
||||
}
|
||||
|
||||
@Override
|
||||
String buildString() {
|
||||
return String.format(this.format, this.arg1, this.arg2);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static final class FormatMessage3 extends FormatMessage {
|
||||
|
||||
private final Object arg1;
|
||||
|
||||
private final Object arg2;
|
||||
|
||||
private final Object arg3;
|
||||
|
||||
FormatMessage3(String format, Object arg1, Object arg2, Object arg3) {
|
||||
super(format);
|
||||
this.arg1 = arg1;
|
||||
this.arg2 = arg2;
|
||||
this.arg3 = arg3;
|
||||
}
|
||||
|
||||
@Override
|
||||
String buildString() {
|
||||
return String.format(this.format, this.arg1, this.arg2, this.arg3);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static final class FormatMessage4 extends FormatMessage {
|
||||
|
||||
private final Object arg1;
|
||||
|
||||
private final Object arg2;
|
||||
|
||||
private final Object arg3;
|
||||
|
||||
private final Object arg4;
|
||||
|
||||
FormatMessage4(String format, Object arg1, Object arg2, Object arg3, Object arg4) {
|
||||
super(format);
|
||||
this.arg1 = arg1;
|
||||
this.arg2 = arg2;
|
||||
this.arg3 = arg3;
|
||||
this.arg4 = arg4;
|
||||
}
|
||||
|
||||
@Override
|
||||
String buildString() {
|
||||
return String.format(this.format, this.arg1, this.arg2, this.arg3, this.arg4);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static final class FormatMessageX extends FormatMessage {
|
||||
|
||||
private final Object[] args;
|
||||
|
||||
FormatMessageX(String format, Object... args) {
|
||||
super(format);
|
||||
this.args = args;
|
||||
}
|
||||
|
||||
@Override
|
||||
String buildString() {
|
||||
return String.format(this.format, this.args);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,9 +27,44 @@ import static org.junit.Assert.*;
|
|||
public class LogSupportTests {
|
||||
|
||||
@Test
|
||||
public void testLogMessage() {
|
||||
LogMessage msg = new LogMessage(() -> new StringBuilder("a"));
|
||||
assertEquals("a", msg.toString());
|
||||
public void testLogMessageWithSupplier() {
|
||||
LogMessage msg = LogMessage.lazy(() -> new StringBuilder("a").append(" b"));
|
||||
assertEquals("a b", msg.toString());
|
||||
assertSame(msg.toString(), msg.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLogMessageWithFormat1() {
|
||||
LogMessage msg = LogMessage.format("a %s", "b");
|
||||
assertEquals("a b", msg.toString());
|
||||
assertSame(msg.toString(), msg.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLogMessageWithFormat2() {
|
||||
LogMessage msg = LogMessage.format("a %s %s", "b", "c");
|
||||
assertEquals("a b c", msg.toString());
|
||||
assertSame(msg.toString(), msg.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLogMessageWithFormat3() {
|
||||
LogMessage msg = LogMessage.format("a %s %s %s", "b", "c", "d");
|
||||
assertEquals("a b c d", msg.toString());
|
||||
assertSame(msg.toString(), msg.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLogMessageWithFormat4() {
|
||||
LogMessage msg = LogMessage.format("a %s %s %s %s", "b", "c", "d", "e");
|
||||
assertEquals("a b c d e", msg.toString());
|
||||
assertSame(msg.toString(), msg.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLogMessageWithFormatX() {
|
||||
LogMessage msg = LogMessage.format("a %s %s %s %s %s", "b", "c", "d", "e", "f");
|
||||
assertEquals("a b c d e f", msg.toString());
|
||||
assertSame(msg.toString(), msg.toString());
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue