From 3b24612e45a14719006530779ce441c8531099ab Mon Sep 17 00:00:00 2001 From: Adrian Shum Date: Wed, 2 Feb 2022 22:29:24 +1100 Subject: [PATCH] SLF4J-256: Allow use of lambda to construct build event - Rename current LoggingEventBuilder to FluentApiStub to better describe its responsibility - Rename NOPLoggingEventBuilder to NopFluentApiStub to align with new interface - New interface of LoggingEventBuilder to act only as Fluent Builder of LoggingEvent, which is used by the lambda --- slf4j-api/src/main/java/org/slf4j/Logger.java | 83 +++++++++++++------ .../org/slf4j/helpers/SubstituteLogger.java | 57 ++++++++++--- .../slf4j/spi/DefaultLoggingEventBuilder.java | 31 +++++-- .../java/org/slf4j/spi/FluentLogApiStub.java | 62 ++++++++++++++ .../org/slf4j/spi/LoggingEventBuilder.java | 29 +++++-- ...ventBuilder.java => NopFluentApiStub.java} | 22 ++--- .../org/slf4j/basicTests/FluentAPIUsage.java | 9 ++ .../platform/logging/SLF4JPlatformLogger.java | 4 +- 8 files changed, 230 insertions(+), 67 deletions(-) create mode 100755 slf4j-api/src/main/java/org/slf4j/spi/FluentLogApiStub.java rename slf4j-api/src/main/java/org/slf4j/spi/{NOPLoggingEventBuilder.java => NopFluentApiStub.java} (58%) diff --git a/slf4j-api/src/main/java/org/slf4j/Logger.java b/slf4j-api/src/main/java/org/slf4j/Logger.java index df946afb2..ad2d08cb8 100644 --- a/slf4j-api/src/main/java/org/slf4j/Logger.java +++ b/slf4j-api/src/main/java/org/slf4j/Logger.java @@ -38,8 +38,11 @@ import org.slf4j.event.Level; import org.slf4j.spi.DefaultLoggingEventBuilder; +import org.slf4j.spi.FluentLogApiStub; import org.slf4j.spi.LoggingEventBuilder; -import org.slf4j.spi.NOPLoggingEventBuilder; +import org.slf4j.spi.NopFluentApiStub; + +import java.util.function.Consumer; /** * The org.slf4j.Logger interface is the main user entry point of SLF4J API. @@ -96,30 +99,43 @@ public interface Logger { public String getName(); /** - * Make a new {@link LoggingEventBuilder} instance as appropriate for this logger and the + * Make a new {@link FluentLogApiStub} instance as appropriate for this logger and the * desired {@link Level} passed as parameter. If this Logger is disabled for the given Level, then - * a {@link NOPLoggingEventBuilder} is returned. + * a {@link NopFluentApiStub} is returned. * * * @param level desired level for the event builder * @return a new {@link LoggingEventBuilder} instance as appropriate for this logger * @since 2.0 */ - default public LoggingEventBuilder makeLoggingEventBuilder(Level level) { + default public FluentLogApiStub makeLoggingEventBuilder(Level level) { if (isEnabledForLevel(level)) { return new DefaultLoggingEventBuilder(this, level); } else { - return NOPLoggingEventBuilder.singleton(); + return NopFluentApiStub.singleton(); } } + default public FluentLogApiStub atLevel(Level level) { + return makeLoggingEventBuilder(level); + } + + /** - * A convenient alias for {@link #makeLoggingEventBuilder}. - * + * Use provided LoggingEventBuilder consumer to construct the logging event, and log at provided level. + * + * + * logger.atDebug(log->log.message("Temperature rise from {} to {}") + * + * * @since 2.0 */ - default public LoggingEventBuilder atLevel(Level level) { - return makeLoggingEventBuilder(level); + default public void atLevel(Level level, Consumer eventBuilderConsumer) { + if (isEnabledForLevel(level)) { + DefaultLoggingEventBuilder eventBuilder= new DefaultLoggingEventBuilder(this, level); + eventBuilderConsumer.accept(eventBuilder); + eventBuilder.log(); + } } @@ -233,16 +249,19 @@ default public boolean isEnabledForLevel(Level level) { /** * Entry point for fluent-logging for {@link org.slf4j.event.Level#TRACE} level. * - * @return LoggingEventBuilder instance as appropriate for level TRACE + * @return FluentLogApiStub instance as appropriate for level TRACE * @since 2.0 */ - default public LoggingEventBuilder atTrace() { + default public FluentLogApiStub atTrace() { if (isTraceEnabled()) { return makeLoggingEventBuilder(TRACE); } else { - return NOPLoggingEventBuilder.singleton(); + return NopFluentApiStub.singleton(); } } + default public void atTrace(Consumer eventBuilderConsumer) { + this.atLevel(TRACE, eventBuilderConsumer); + } /** * Log a message with the specific Marker at the TRACE level. @@ -429,17 +448,21 @@ default public LoggingEventBuilder atTrace() { /** * Entry point for fluent-logging for {@link org.slf4j.event.Level#DEBUG} level. * - * @return LoggingEventBuilder instance as appropriate for level DEBUG + * @return FluentLogApiStub instance as appropriate for level DEBUG * @since 2.0 */ - default public LoggingEventBuilder atDebug() { + default public FluentLogApiStub atDebug() { if (isDebugEnabled()) { return makeLoggingEventBuilder(DEBUG); } else { - return NOPLoggingEventBuilder.singleton(); + return NopFluentApiStub.singleton(); } } + default public void atDebug(Consumer eventBuilderConsumer) { + this.atLevel(DEBUG, eventBuilderConsumer); + } + /** * Is the logger instance enabled for the INFO level? * @@ -568,16 +591,19 @@ default public LoggingEventBuilder atDebug() { /** * Entry point for fluent-logging for {@link org.slf4j.event.Level#INFO} level. * - * @return LoggingEventBuilder instance as appropriate for level INFO + * @return FluentLogApiStub instance as appropriate for level INFO * @since 2.0 */ - default public LoggingEventBuilder atInfo() { + default public FluentLogApiStub atInfo() { if (isInfoEnabled()) { return makeLoggingEventBuilder(INFO); } else { - return NOPLoggingEventBuilder.singleton(); + return NopFluentApiStub.singleton(); } } + default public void atInfo(Consumer eventBuilderConsumer) { + this.atLevel(INFO, eventBuilderConsumer); + } /** * Is the logger instance enabled for the WARN level? @@ -708,16 +734,19 @@ default public LoggingEventBuilder atInfo() { /** * Entry point for fluent-logging for {@link org.slf4j.event.Level#WARN} level. * - * @return LoggingEventBuilder instance as appropriate for level WARN + * @return FluentLogApiStub instance as appropriate for level WARN * @since 2.0 */ - default public LoggingEventBuilder atWarn() { + default public FluentLogApiStub atWarn() { if (isWarnEnabled()) { return makeLoggingEventBuilder(WARN); } else { - return NOPLoggingEventBuilder.singleton(); + return NopFluentApiStub.singleton(); } } + default public void atWarn(Consumer eventBuilderConsumer) { + this.atLevel(WARN, eventBuilderConsumer); + } /** * Is the logger instance enabled for the ERROR level? @@ -849,15 +878,21 @@ default public LoggingEventBuilder atWarn() { /** * Entry point for fluent-logging for {@link org.slf4j.event.Level#ERROR} level. * - * @return LoggingEventBuilder instance as appropriate for level ERROR + * @return FluentLogApiStub instance as appropriate for level ERROR * @since 2.0 */ - default public LoggingEventBuilder atError() { + default public FluentLogApiStub atError() { if (isErrorEnabled()) { return makeLoggingEventBuilder(ERROR); } else { - return NOPLoggingEventBuilder.singleton(); + return NopFluentApiStub.singleton(); } } + default public void atError(Consumer eventBuilderConsumer) { + this.atLevel(ERROR, eventBuilderConsumer); + } + + + } diff --git a/slf4j-api/src/main/java/org/slf4j/helpers/SubstituteLogger.java b/slf4j-api/src/main/java/org/slf4j/helpers/SubstituteLogger.java index 6c35def82..d6fce81a6 100644 --- a/slf4j-api/src/main/java/org/slf4j/helpers/SubstituteLogger.java +++ b/slf4j-api/src/main/java/org/slf4j/helpers/SubstituteLogger.java @@ -27,6 +27,7 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Queue; +import java.util.function.Consumer; import org.slf4j.Logger; import org.slf4j.Marker; @@ -34,6 +35,7 @@ import org.slf4j.event.Level; import org.slf4j.event.LoggingEvent; import org.slf4j.event.SubstituteLoggingEvent; +import org.slf4j.spi.FluentLogApiStub; import org.slf4j.spi.LoggingEventBuilder; /** @@ -69,15 +71,21 @@ public String getName() { } @Override - public LoggingEventBuilder makeLoggingEventBuilder(Level level) { + public FluentLogApiStub makeLoggingEventBuilder(Level level) { return delegate().makeLoggingEventBuilder(level); } @Override - public LoggingEventBuilder atLevel(Level level) { + public FluentLogApiStub atLevel(Level level) { return delegate().atLevel(level); } - + + @Override + public void atLevel(Level level, Consumer eventBuilderConsumer) { + delegate().atLevel(level, eventBuilderConsumer); + } + + @Override public boolean isEnabledForLevel(Level level) { return delegate().isEnabledForLevel(level); @@ -142,10 +150,15 @@ public void trace(Marker marker, String msg, Throwable t) { } @Override - public LoggingEventBuilder atTrace() { + public FluentLogApiStub atTrace() { return delegate().atTrace(); } - + + @Override + public void atTrace(Consumer eventBuilderConsumer) { + delegate().atTrace(eventBuilderConsumer); + } + @Override public boolean isDebugEnabled() { return delegate().isDebugEnabled(); @@ -207,10 +220,15 @@ public void debug(Marker marker, String msg, Throwable t) { } @Override - public LoggingEventBuilder atDebug() { + public FluentLogApiStub atDebug() { return delegate().atDebug(); } - + + @Override + public void atDebug(Consumer eventBuilderConsumer) { + delegate().atDebug(eventBuilderConsumer); + } + @Override public boolean isInfoEnabled() { return delegate().isInfoEnabled(); @@ -273,11 +291,15 @@ public void info(Marker marker, String msg, Throwable t) { } @Override - public LoggingEventBuilder atInfo() { + public FluentLogApiStub atInfo() { return delegate().atInfo(); } - + @Override + public void atInfo(Consumer eventBuilderConsumer) { + delegate().atInfo(eventBuilderConsumer); + } + @Override public boolean isWarnEnabled() { return delegate().isWarnEnabled(); @@ -338,11 +360,15 @@ public void warn(Marker marker, String msg, Throwable t) { } @Override - public LoggingEventBuilder atWarn() { + public FluentLogApiStub atWarn() { return delegate().atWarn(); } - + @Override + public void atWarn(Consumer eventBuilderConsumer) { + delegate().atWarn(eventBuilderConsumer); + } + @Override public boolean isErrorEnabled() { @@ -405,10 +431,15 @@ public void error(Marker marker, String msg, Throwable t) { } @Override - public LoggingEventBuilder atError() { + public FluentLogApiStub atError() { return delegate().atError(); } - + + @Override + public void atError(Consumer eventBuilderConsumer) { + delegate().atError(eventBuilderConsumer); + } + @Override public boolean equals(Object o) { if (this == o) diff --git a/slf4j-api/src/main/java/org/slf4j/spi/DefaultLoggingEventBuilder.java b/slf4j-api/src/main/java/org/slf4j/spi/DefaultLoggingEventBuilder.java index 5c2827faa..99d293e1c 100755 --- a/slf4j-api/src/main/java/org/slf4j/spi/DefaultLoggingEventBuilder.java +++ b/slf4j-api/src/main/java/org/slf4j/spi/DefaultLoggingEventBuilder.java @@ -9,7 +9,7 @@ import org.slf4j.event.Level; import org.slf4j.event.LoggingEvent; -public class DefaultLoggingEventBuilder implements LoggingEventBuilder, CallerBoundaryAware { +public class DefaultLoggingEventBuilder implements FluentLogApiStub, LoggingEventBuilder, CallerBoundaryAware { protected DefaultLoggingEvent loggingEvent; protected Logger logger; @@ -19,6 +19,12 @@ public DefaultLoggingEventBuilder(Logger logger, Level level) { loggingEvent = new DefaultLoggingEvent(level, logger); } + @Override + public DefaultLoggingEventBuilder setMessage(String message) { + loggingEvent.setMessage(message); + return this; + } + /** * Add a marker to the current logging event being built. * @@ -27,29 +33,34 @@ public DefaultLoggingEventBuilder(Logger logger, Level level) { * @param marker the marker to add */ @Override - public LoggingEventBuilder addMarker(Marker marker) { + public DefaultLoggingEventBuilder addMarker(Marker marker) { loggingEvent.addMarker(marker); return this; } @Override - public LoggingEventBuilder setCause(Throwable t) { + public DefaultLoggingEventBuilder setCause(Throwable t) { loggingEvent.setThrowable(t); return this; } @Override - public LoggingEventBuilder addArgument(Object p) { + public DefaultLoggingEventBuilder addArgument(Object p) { loggingEvent.addArgument(p); return this; } @Override - public LoggingEventBuilder addArgument(Supplier objectSupplier) { + public DefaultLoggingEventBuilder addArgument(Supplier objectSupplier) { loggingEvent.addArgument(objectSupplier.get()); return this; } + @Override + public LoggingEvent build() { + return this.loggingEvent; + } + @Override public void setCallerBoundary(String fqcn) { loggingEvent.setCallerBoundary(fqcn); @@ -92,7 +103,11 @@ public void log(Supplier messageSupplier) { log(messageSupplier.get()); } } - + + public void log() { + log(loggingEvent); + } + protected void log(LoggingEvent aLoggingEvent) { if (logger instanceof LoggingEventAware) { ((LoggingEventAware) logger).log(aLoggingEvent); @@ -183,13 +198,13 @@ private String mergeMarkersAndKeyValuePairs(LoggingEvent aLoggingEvent, String m @Override - public LoggingEventBuilder addKeyValue(String key, Object value) { + public DefaultLoggingEventBuilder addKeyValue(String key, Object value) { loggingEvent.addKeyValue(key, value); return this; } @Override - public LoggingEventBuilder addKeyValue(String key, Supplier value) { + public DefaultLoggingEventBuilder addKeyValue(String key, Supplier value) { loggingEvent.addKeyValue(key, value.get()); return this; } diff --git a/slf4j-api/src/main/java/org/slf4j/spi/FluentLogApiStub.java b/slf4j-api/src/main/java/org/slf4j/spi/FluentLogApiStub.java new file mode 100755 index 000000000..feca4f540 --- /dev/null +++ b/slf4j-api/src/main/java/org/slf4j/spi/FluentLogApiStub.java @@ -0,0 +1,62 @@ +/** + * Copyright (c) 2004-2021 QOS.ch + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +package org.slf4j.spi; + +import org.slf4j.Marker; + +import java.util.function.Supplier; + +/** + * This is main interface in slf4j's fluent API for creating logging events. + * + * @author Ceki Gülcü + * @since 2.0.0 + * + */ +public interface FluentLogApiStub { + + FluentLogApiStub setCause(Throwable cause); + + FluentLogApiStub addMarker(Marker marker); + + FluentLogApiStub addArgument(Object p); + + FluentLogApiStub addArgument(Supplier objectSupplier); + + FluentLogApiStub addKeyValue(String key, Object value); + + FluentLogApiStub addKeyValue(String key, Supplier value); + + void log(String message); + + void log(String message, Object arg); + + void log(String message, Object arg0, Object arg1); + + void log(String message, Object... args); + + void log(Supplier messageSupplier); + +} diff --git a/slf4j-api/src/main/java/org/slf4j/spi/LoggingEventBuilder.java b/slf4j-api/src/main/java/org/slf4j/spi/LoggingEventBuilder.java index fc42c1c1b..22f8e3866 100755 --- a/slf4j-api/src/main/java/org/slf4j/spi/LoggingEventBuilder.java +++ b/slf4j-api/src/main/java/org/slf4j/spi/LoggingEventBuilder.java @@ -27,6 +27,7 @@ import java.util.function.Supplier; import org.slf4j.Marker; +import org.slf4j.event.LoggingEvent; /** * This is main interface in slf4j's fluent API for creating logging events. @@ -37,26 +38,36 @@ */ public interface LoggingEventBuilder { + LoggingEventBuilder setMessage(String message); + + default public LoggingEventBuilder message(String message) { + return this.setMessage(message); + } + LoggingEventBuilder setCause(Throwable cause); + default public LoggingEventBuilder cause(Throwable cause) { + return this.setCause(cause); + } + LoggingEventBuilder addMarker(Marker marker); LoggingEventBuilder addArgument(Object p); + default public LoggingEventBuilder arg(Object p) { + return this.addArgument(p); + } + LoggingEventBuilder addArgument(Supplier objectSupplier); + default public LoggingEventBuilder arg(Supplier argSupplier) { + return this.addArgument(argSupplier); + } + LoggingEventBuilder addKeyValue(String key, Object value); LoggingEventBuilder addKeyValue(String key, Supplier value); - void log(String message); - - void log(String message, Object arg); - - void log(String message, Object arg0, Object arg1); - - void log(String message, Object... args); - - void log(Supplier messageSupplier); + LoggingEvent build(); } diff --git a/slf4j-api/src/main/java/org/slf4j/spi/NOPLoggingEventBuilder.java b/slf4j-api/src/main/java/org/slf4j/spi/NopFluentApiStub.java similarity index 58% rename from slf4j-api/src/main/java/org/slf4j/spi/NOPLoggingEventBuilder.java rename to slf4j-api/src/main/java/org/slf4j/spi/NopFluentApiStub.java index 487f47ce4..5e45dc8a7 100755 --- a/slf4j-api/src/main/java/org/slf4j/spi/NOPLoggingEventBuilder.java +++ b/slf4j-api/src/main/java/org/slf4j/spi/NopFluentApiStub.java @@ -5,7 +5,7 @@ import org.slf4j.Marker; /** - * A no-operation implementation of {@link LoggingEventBuilder}. + * A no-operation implementation of {@link FluentLogApiStub}. * As the name indicates, this implementation does nothing or alternatively returns * a singleton, i.e. the unique instance of this class. * @@ -13,45 +13,45 @@ * @since 2.0.0 * */ -public class NOPLoggingEventBuilder implements LoggingEventBuilder { +public class NopFluentApiStub implements FluentLogApiStub { - static final NOPLoggingEventBuilder SINGLETON = new NOPLoggingEventBuilder(); + static final NopFluentApiStub SINGLETON = new NopFluentApiStub(); - private NOPLoggingEventBuilder() { + private NopFluentApiStub() { } - public static LoggingEventBuilder singleton() { + public static NopFluentApiStub singleton() { return SINGLETON; } @Override - public LoggingEventBuilder addMarker(Marker marker) { + public NopFluentApiStub addMarker(Marker marker) { return singleton(); } @Override - public LoggingEventBuilder addArgument(Object p) { + public NopFluentApiStub addArgument(Object p) { return singleton(); } @Override - public LoggingEventBuilder addArgument(Supplier objectSupplier) { + public NopFluentApiStub addArgument(Supplier objectSupplier) { return singleton(); } @Override - public LoggingEventBuilder addKeyValue(String key, Object value) { + public NopFluentApiStub addKeyValue(String key, Object value) { return singleton(); } @Override - public LoggingEventBuilder addKeyValue(String key, Supplier value) { + public NopFluentApiStub addKeyValue(String key, Supplier value) { return singleton(); } @Override - public LoggingEventBuilder setCause(Throwable cause) { + public NopFluentApiStub setCause(Throwable cause) { return singleton(); } diff --git a/slf4j-api/src/test/java/org/slf4j/basicTests/FluentAPIUsage.java b/slf4j-api/src/test/java/org/slf4j/basicTests/FluentAPIUsage.java index cc8434a9f..e6be2311d 100755 --- a/slf4j-api/src/test/java/org/slf4j/basicTests/FluentAPIUsage.java +++ b/slf4j-api/src/test/java/org/slf4j/basicTests/FluentAPIUsage.java @@ -24,4 +24,13 @@ public void smokxce() { assertFalse(logger.isEnabledForLevel(Level.DEBUG)); } + @Test + public void fluentApiWithLambda() { + Logger logger = LoggerFactory.getLogger("fluentApiWithLambda"); + logger.atDebug(l-> l.setMessage("Hello {}") + .addArgument("world") + .setCause(new Throwable())); + + } + } diff --git a/slf4j-jdk-platform-logging/src/main/java/org/slf4j/jdk/platform/logging/SLF4JPlatformLogger.java b/slf4j-jdk-platform-logging/src/main/java/org/slf4j/jdk/platform/logging/SLF4JPlatformLogger.java index b79a85177..ce838bff4 100644 --- a/slf4j-jdk-platform-logging/src/main/java/org/slf4j/jdk/platform/logging/SLF4JPlatformLogger.java +++ b/slf4j-jdk-platform-logging/src/main/java/org/slf4j/jdk/platform/logging/SLF4JPlatformLogger.java @@ -31,7 +31,7 @@ import org.slf4j.Logger; import org.slf4j.spi.CallerBoundaryAware; -import org.slf4j.spi.LoggingEventBuilder; +import org.slf4j.spi.FluentLogApiStub; /** * Adapts {@link Logger} to {@link System.Logger}. @@ -132,7 +132,7 @@ private void log(Level jplLevel, ResourceBundle bundle, String msg, Throwable th private void performLog(org.slf4j.event.Level slf4jLevel, ResourceBundle bundle, String msg, Throwable thrown, Object... params) { String message = getResourceStringOrMessage(bundle, msg); - LoggingEventBuilder leb = slf4jLogger.makeLoggingEventBuilder(slf4jLevel); + FluentLogApiStub leb = slf4jLogger.atLevel(slf4jLevel); if (thrown != null) { leb = leb.setCause(thrown); }