Index: log4j-api/src/main/java/org/apache/logging/log4j/FluentLogger.java
===================================================================
--- log4j-api/src/main/java/org/apache/logging/log4j/FluentLogger.java (revision 0)
+++ log4j-api/src/main/java/org/apache/logging/log4j/FluentLogger.java (working copy)
@@ -0,0 +1,193 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ * http://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.apache.logging.log4j;
+
+/**
+ * This interface serves as an extension of the {@link Logger} interface that supports a fluent logging style. To log
+ * fluently, you first call one of the seven methods that return a {@link MessageBuilder}. You can then call any of
+ * the methods on the builder to set the message, arguments, marker, and/or exception. Once you are finished with
+ * building the message, calling {@link MessageBuilder#log()} commits the message to the logger. You cannot call any
+ * methods on the builder after this (doing so will result in an {@link IllegalStateException}).
+ *
+ * Here are some sample uses of fluent logging:
+ *
+ * logger.info().message("Something interesting happened.").marker(myMarker).log();
+ * logger.error().message("An error occurred with argument {}.").argument(arg).exception(e).log();
+ * logger.trace().message("Entered HelloTest.main({}, {}, {})").arguments(arg1, arg2, arg3).log();
+ * logger.trace().message("Entered GoodbyeTest.main({}, {})").argument(arg1).argument(arg2).log();
+ *
+ *
+ * Implementations should eagerly determine eligibility of a logging message so that CPU cycles are not needlessly spent
+ * updating the message builder for no reason.
+ *
+ * @see MessageBuilder
+ */
+public interface FluentLogger extends Logger {
+ /**
+ * Returns a fluent message builder for trace messages, or a no-op message builder if trace messages are disabled
+ * for this logger.
+ *
+ * @return a fluent message builder.
+ * @see MessageBuilder
+ * @see org.apache.logging.log4j.spi.NullFluentMessageBuilder
+ */
+ MessageBuilder trace();
+
+ /**
+ * Returns a fluent message builder for debug messages, or a no-op message builder if debug messages are disabled
+ * for this logger.
+ *
+ * @return a fluent message builder.
+ * @see MessageBuilder
+ * @see org.apache.logging.log4j.spi.NullFluentMessageBuilder
+ */
+ MessageBuilder debug();
+
+ /**
+ * Returns a fluent message builder for info messages, or a no-op message builder if info messages are disabled
+ * for this logger.
+ *
+ * @return a fluent message builder.
+ * @see MessageBuilder
+ * @see org.apache.logging.log4j.spi.NullFluentMessageBuilder
+ */
+ MessageBuilder info();
+
+ /**
+ * Returns a fluent message builder for warn messages, or a no-op message builder if warn messages are disabled
+ * for this logger.
+ *
+ * @return a fluent message builder.
+ * @see MessageBuilder
+ * @see org.apache.logging.log4j.spi.NullFluentMessageBuilder
+ */
+ MessageBuilder warn();
+
+ /**
+ * Returns a fluent message builder for error messages, or a no-op message builder if error messages are disabled
+ * for this logger.
+ *
+ * @return a fluent message builder.
+ * @see MessageBuilder
+ * @see org.apache.logging.log4j.spi.NullFluentMessageBuilder
+ */
+ MessageBuilder error();
+
+ /**
+ * Returns a fluent message builder for fatal messages, or a no-op message builder if fatal messages are disabled
+ * for this logger.
+ *
+ * @return a fluent message builder.
+ * @see MessageBuilder
+ * @see org.apache.logging.log4j.spi.NullFluentMessageBuilder
+ */
+ MessageBuilder fatal();
+
+ /**
+ * Returns a fluent message builder for {@code level} messages, or a no-op message builder if {@code level} messages
+ * are disabled for this logger.
+ *
+ * @return a fluent message builder.
+ * @see MessageBuilder
+ * @see org.apache.logging.log4j.spi.NullFluentMessageBuilder
+ */
+ MessageBuilder log(Level level);
+
+ interface MessageBuilder {
+ /**
+ * Sets the message to the specified message. Overrides any previous values set by this or the other
+ * {@code message} methods.
+ *
+ * @param message The message
+ * @return this builder.
+ * @throws IllegalStateException if called after {@link #log}.
+ */
+ MessageBuilder message(String message);
+
+ /**
+ * Sets the message to the specified message. Overrides any previous values set by this or the other
+ * {@code message} methods.
+ *
+ * @param message The message
+ * @return this builder.
+ * @throws IllegalStateException if called after {@link #log}.
+ */
+ MessageBuilder message(Object message);
+
+ /**
+ * Sets the message template to the specified message with the given arguments. Overrides any previous values
+ * set by this or the other {@code message} methods. Instead of using this method, you could use one of the
+ * other methods and {@link #argument} or {@link #arguments}.
+ *
+ * @param template The message template
+ * @param arguments The arguments for the message
+ * @return this builder.
+ * @throws IllegalStateException if called after {@link #log}.
+ */
+ MessageBuilder message(String template, Object... arguments);
+
+ /**
+ * Sets the marker for this message. Overrides the marker set on any previous calls.
+ *
+ * @param marker The marker
+ * @return this builder.
+ * @throws IllegalStateException if called after {@link #log}.
+ */
+ MessageBuilder marker(Marker marker);
+
+ /**
+ * Sets the exception for this message. Overrides the exception set on any previous calls.
+ *
+ * @param exception The exception
+ * @return this builder.
+ * @throws IllegalStateException if called after {@link #log}.
+ */
+ MessageBuilder exception(Throwable exception);
+
+ /**
+ * Adds an argument to this message. Should not be used in conjunction with {@link #arguments},
+ * {@link #message(String, Object...)}, or {@link #message(Object)}. Throws an exception if called after
+ * {@link #message(Object)}.
+ *
+ * @param argumentToAdd The argument
+ * @return this builder.
+ * @throws IllegalStateException if called after {@link #log} or {@link #message(Object)}.
+ */
+ MessageBuilder argument(Object argumentToAdd);
+
+ /**
+ * Sets the arguments to this message. Overrides any arguments previously added by {@link #argument} or
+ * {@link #message(String, Object...)}, or {@link #message(Object)}. Throws an exception if called after
+ * {@link #message(Object)}.
+ *
+ * @param arguments The arguments
+ * @return this builder.
+ * @throws IllegalStateException if called after {@link #log} or {@link #message(Object)}.
+ */
+ MessageBuilder arguments(Object... arguments);
+
+ /**
+ * Commits the logging event to the underlying logger. Once this method is called, you cannot call any other
+ * methods on this builder. Additionally, implementations may pool and reuse builders, resulting in unexpected
+ * behavior if you hold on to an instance of this builder. Once you have called this method, you should discard
+ * the builder instance and never use it again.
+ *
+ * @throws IllegalStateException if called more than once.
+ */
+ void log();
+ }
+}
Index: log4j-api/src/main/java/org/apache/logging/log4j/spi/AbstractLogger.java
===================================================================
--- log4j-api/src/main/java/org/apache/logging/log4j/spi/AbstractLogger.java (revision 1561928)
+++ log4j-api/src/main/java/org/apache/logging/log4j/spi/AbstractLogger.java (working copy)
@@ -16,6 +16,7 @@
*/
package org.apache.logging.log4j.spi;
+import org.apache.logging.log4j.FluentLogger;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.Marker;
@@ -27,11 +28,13 @@
import org.apache.logging.log4j.status.StatusLogger;
import java.io.Serializable;
+import java.util.Queue;
+import java.util.concurrent.ConcurrentLinkedQueue;
/**
* Base implementation of a Logger. It is highly recommended that any Logger implementation extend this class.
*/
-public abstract class AbstractLogger implements Logger, Serializable {
+public abstract class AbstractLogger implements FluentLogger, Serializable {
private static final long serialVersionUID = 2L;
@@ -76,6 +79,11 @@
private static final String CATCHING = "catching";
+ private static final NullFluentMessageBuilder NULL_FLUENT_MESSAGE_BUILDER = new NullFluentMessageBuilder();
+
+ private static final Queue MESSAGE_BUILDERS =
+ new ConcurrentLinkedQueue();
+
/**
* Checks that the message factory a logger was created with is the same as the given messageFactory. If they are
* different log a warning to the {@linkplain StatusLogger}. A null MessageFactory translates to the default
@@ -367,6 +375,11 @@
}
}
+ @Override
+ public MessageBuilder debug() {
+ return this.getMessageBuilderForLevel(Level.DEBUG);
+ }
+
/**
* Logs entry to a method.
*/
@@ -606,6 +619,11 @@
}
}
+ @Override
+ public MessageBuilder error() {
+ return this.getMessageBuilderForLevel(Level.ERROR);
+ }
+
/**
* Logs exit from a method.
*/
@@ -829,6 +847,11 @@
}
}
+ @Override
+ public MessageBuilder fatal() {
+ return this.getMessageBuilderForLevel(Level.FATAL);
+ }
+
/**
* Gets the message factory.
*
@@ -1061,6 +1084,11 @@
}
}
+ @Override
+ public MessageBuilder info() {
+ return this.getMessageBuilderForLevel(Level.INFO);
+ }
+
/**
* Checks whether this Logger is enabled for the DEBUG Level.
*
@@ -1466,6 +1494,43 @@
}
/**
+ * Gets a fluent message builder for the given level. Returns the constant no-op message builder if logging is
+ * disabled for this level. Otherwise, borrows a builder from the pool (or creates a new one if the pool is empty),
+ * prepares it, and returns it.
+ *
+ * @param level The level to return a builder for
+ * @return the builder.
+ */
+ private MessageBuilder getMessageBuilderForLevel(Level level) {
+ if (!isEnabled(level)) { // if logging disabled for this level/logger, return the null builder
+ return NULL_FLUENT_MESSAGE_BUILDER;
+ }
+ PoolableFluentMessageBuilder builder = MESSAGE_BUILDERS.poll(); // get the oldest builder out of the pool
+ if (builder == null) {
+ builder = new PoolableFluentMessageBuilder(); // if no builders are available in the pool, create a new one
+ }
+ return builder.prepareForReturn(this, level);
+ }
+
+ /**
+ * Writes the message created by the message builder, then returns the builder to the pool. This method is only
+ * called from {@link PoolableFluentMessageBuilder}.
+ *
+ * @param builder The builder to return
+ * @param marker The event marker
+ * @param level The event level
+ * @param data The event message
+ * @param t The event exception
+ */
+ void commitFluentMessage(PoolableFluentMessageBuilder builder, Marker marker, Level level, Message data,
+ Throwable t) {
+ if (isEnabled(level, marker, data, t)) { // double-check, marker or message may have disabled logging
+ log(marker, FQCN, level, data, t);
+ }
+ MESSAGE_BUILDERS.add(builder); // add the builder back to the pool as the youngest builder
+ }
+
+ /**
* Logs a message with location information.
*
* @param marker The Marker
@@ -1770,6 +1835,11 @@
}
}
+ @Override
+ public MessageBuilder trace() {
+ return this.getMessageBuilderForLevel(Level.TRACE);
+ }
+
/**
* Logs a message with the specific Marker at the WARN level.
*
@@ -1964,4 +2034,8 @@
}
}
+ @Override
+ public MessageBuilder warn() {
+ return this.getMessageBuilderForLevel(Level.WARN);
+ }
}
Index: log4j-api/src/main/java/org/apache/logging/log4j/spi/NullFluentMessageBuilder.java
===================================================================
--- log4j-api/src/main/java/org/apache/logging/log4j/spi/NullFluentMessageBuilder.java (revision 0)
+++ log4j-api/src/main/java/org/apache/logging/log4j/spi/NullFluentMessageBuilder.java (working copy)
@@ -0,0 +1,52 @@
+package org.apache.logging.log4j.spi;
+
+import org.apache.logging.log4j.FluentLogger;
+import org.apache.logging.log4j.Marker;
+
+/**
+ * A no-op {@link FluentLogger.MessageBuilder MessageBuilder} that performs no operations if logging is disabled for
+ * the chosen {@link org.apache.logging.log4j.Level}.
+ *
+ * @see FluentLogger
+ */
+public class NullFluentMessageBuilder implements FluentLogger.MessageBuilder {
+ @Override
+ public FluentLogger.MessageBuilder message(String message) {
+ return this;
+ }
+
+ @Override
+ public FluentLogger.MessageBuilder message(Object message) {
+ return this;
+ }
+
+ @Override
+ public FluentLogger.MessageBuilder message(String template, Object... arguments) {
+ return this;
+ }
+
+ @Override
+ public FluentLogger.MessageBuilder marker(Marker marker) {
+ return this;
+ }
+
+ @Override
+ public FluentLogger.MessageBuilder exception(Throwable exception) {
+ return this;
+ }
+
+ @Override
+ public FluentLogger.MessageBuilder argument(Object argumentToAdd) {
+ return this;
+ }
+
+ @Override
+ public FluentLogger.MessageBuilder arguments(Object... arguments) {
+ return this;
+ }
+
+ @Override
+ public void log() {
+
+ }
+}
Index: log4j-api/src/main/java/org/apache/logging/log4j/spi/PoolableFluentMessageBuilder.java
===================================================================
--- log4j-api/src/main/java/org/apache/logging/log4j/spi/PoolableFluentMessageBuilder.java (revision 0)
+++ log4j-api/src/main/java/org/apache/logging/log4j/spi/PoolableFluentMessageBuilder.java (working copy)
@@ -0,0 +1,128 @@
+package org.apache.logging.log4j.spi;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.logging.log4j.FluentLogger;
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.Marker;
+import org.apache.logging.log4j.message.Message;
+
+final class PoolableFluentMessageBuilder implements FluentLogger.MessageBuilder {
+ private AbstractLogger logger;
+ private Level level;
+
+ private Object messageObject;
+ private String messageString;
+
+ private Object[] argumentArray;
+ private final List