From 4f75053cdfe39614fc60ecc609f3658cdd4840db Mon Sep 17 00:00:00 2001 From: Jeff Shaw Date: Fri, 23 Feb 2018 22:41:44 -0500 Subject: [PATCH 1/2] exposes the location part of the Message to loggers --- README.md | 18 +- .../java/org/apache/logging/log4j/Logger.java | 81 +++++++- .../log4j/message/AbstractMessageFactory.java | 136 +++++++++++-- .../message/DefaultFlowMessageFactory.java | 63 ++++-- .../log4j/message/FlowMessageFactory.java | 15 +- .../log4j/message/FormattedMessage.java | 119 ++++++++++- .../message/FormattedMessageFactory.java | 5 + .../log4j/message/LocalizedMessage.java | 156 +++++++++++--- .../message/LocalizedMessageFactory.java | 23 ++- .../logging/log4j/message/MapMessage.java | 31 +++ .../apache/logging/log4j/message/Message.java | 7 + .../logging/log4j/message/MessageFactory.java | 40 ++++ .../log4j/message/MessageFactory2.java | 178 ++++++++++++++++ .../log4j/message/MessageFormatMessage.java | 109 +++++++++- .../message/MessageFormatMessageFactory.java | 89 +------- .../log4j/message/ObjectArrayMessage.java | 17 ++ .../logging/log4j/message/ObjectMessage.java | 16 ++ .../log4j/message/ParameterizedMessage.java | 63 +++++- .../message/ParameterizedMessageFactory.java | 130 ++++++++++-- ...arameterizedNoReferenceMessageFactory.java | 33 ++- .../log4j/message/ReusableMessage.java | 2 + .../log4j/message/ReusableMessageFactory.java | 131 ++++++++++-- .../log4j/message/ReusableObjectMessage.java | 14 ++ .../message/ReusableParameterizedMessage.java | 108 +++++++--- .../log4j/message/ReusableSimpleMessage.java | 13 ++ .../logging/log4j/message/SimpleMessage.java | 31 +++ .../log4j/message/SimpleMessageFactory.java | 8 + .../log4j/message/StringFormattedMessage.java | 50 ++++- .../StringFormatterMessageFactory.java | 5 + .../log4j/message/StringMapMessage.java | 16 +- .../StructuredDataCollectionMessage.java | 11 + .../log4j/message/StructuredDataMessage.java | 132 +++++++++++- .../log4j/message/ThreadDumpMessage.java | 16 ++ .../logging/log4j/spi/AbstractLogger.java | 192 ++++++++++++------ .../log4j/spi/MessageFactory2Adapter.java | 70 +++++++ .../logging/log4j/AbstractLoggerTest.java | 11 +- .../logging/log4j/message/JsonMessage.java | 19 +- .../message/StringFormattedMessageTest.java | 21 +- .../logging/log4j/core/async/AsyncLogger.java | 11 +- .../log4j/core/async/RingBufferLogEvent.java | 7 + .../core/config/ConfigurationFactory.java | 6 +- .../log4j/core/impl/Log4jLogEvent.java | 3 + .../log4j/core/impl/MutableLogEvent.java | 7 + .../pattern/FileLocationPatternConverter.java | 1 - .../log4j/lookup/CustomMapMessage.java | 6 +- 45 files changed, 1906 insertions(+), 314 deletions(-) diff --git a/README.md b/README.md index e5ade8a1c62..6a58977d561 100644 --- a/README.md +++ b/README.md @@ -93,7 +93,23 @@ JIRA issue in the Pull Request. ## Building From Source -Log4j requires Apache Maven 3.x. To build from source and install to your local Maven repository, execute the following: +Log4j requires Apache Maven 3.x. Java 9 needs to be installed and present in your `~/.m2/toolchains.xml`. See the following for an example. + + ```$xml + + + jdk + + 9 + + + C:\Program Files\Java\jdk-9.0.1 + + + +``` + + To build from source and install to your local Maven repository, execute the following: ```sh mvn install diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/Logger.java b/log4j-api/src/main/java/org/apache/logging/log4j/Logger.java index 995c6df02f2..a1ae2fb7308 100644 --- a/log4j-api/src/main/java/org/apache/logging/log4j/Logger.java +++ b/log4j-api/src/main/java/org/apache/logging/log4j/Logger.java @@ -16,10 +16,7 @@ */ package org.apache.logging.log4j; -import org.apache.logging.log4j.message.EntryMessage; -import org.apache.logging.log4j.message.Message; -import org.apache.logging.log4j.message.MessageFactory; -import org.apache.logging.log4j.message.MessageFactory2; +import org.apache.logging.log4j.message.*; import org.apache.logging.log4j.util.MessageSupplier; import org.apache.logging.log4j.util.Supplier; @@ -84,6 +81,15 @@ public interface Logger { */ void catching(Level level, Throwable t); + /** + * Logs an exception or error that has been caught to a specific logging level, when the location + * of the log statement might be known at compile time. + * + * @param level The logging Level. + * @param t The Throwable. + */ + void catching(StackTraceElement source, Level level, Throwable t); + /** * Logs an exception or error that has been caught. Normally, one may wish to provide additional information with an * exception while logging it; in these cases, one would not use this method. In other cases where simply logging @@ -94,6 +100,17 @@ public interface Logger { */ void catching(Throwable t); + /** + * Logs an exception or error that has been caught, when the location of the log statement might be known at + * compile time.. Normally, one may wish to provide additional information with an exception while logging it; in + * these cases, one would not use this method. In other cases where simply logging the fact that an exception was + * swallowed somewhere (e.g., at the top of the stack trace in a {@code main()} method), this method is ideal for + * it. + * + * @param t The Throwable. + */ + void catching(StackTraceElement source, Throwable t); + /** * Logs a message with the specific Marker at the {@link Level#DEBUG DEBUG} level. * @@ -2995,6 +3012,21 @@ void log(Level level, String message, Object p0, Object p1, Object p2, Object p3 */ T throwing(Level level, T t); + /** + * Logs an exception or error to be thrown, when the location + * of the log statement might be known at compile time. This may be coded as: + * + *
+     * throw logger.throwing(source, Level.DEBUG, myException);
+     * 
+ * + * @param the Throwable type. + * @param level The logging Level. + * @param t The Throwable. + * @return the Throwable. + */ + T throwing(StackTraceElement source, Level level, T t); + /** * Logs an exception or error to be thrown. This may be coded as: * @@ -3008,6 +3040,20 @@ void log(Level level, String message, Object p0, Object p1, Object p2, Object p3 */ T throwing(T t); + /** + * Logs an exception or error to be thrown, when the location + * of the log statement might be known at compile time. This may be coded as: + * + *
+     * throw logger.throwing(source, myException);
+     * 
+ * + * @param the Throwable type. + * @param t The Throwable. + * @return the Throwable. + */ + T throwing(StackTraceElement source, T t); + /** * Logs a message with the specific Marker at the {@link Level#TRACE TRACE} level. * @@ -3549,6 +3595,8 @@ void trace(String message, Object p0, Object p1, Object p2, Object p3, Object p4 */ EntryMessage traceEntry(); + EntryMessage traceEntry(StackTraceElement source); + /** * Logs entry to a method along with its parameters. For example, * @@ -3575,6 +3623,8 @@ void trace(String message, Object p0, Object p1, Object p2, Object p3, Object p4 */ EntryMessage traceEntry(String format, Object... params); + EntryMessage traceEntry(StackTraceElement source, String format, Object... params); + /** * Logs entry to a method along with its parameters. For example, * @@ -3592,6 +3642,8 @@ void trace(String message, Object p0, Object p1, Object p2, Object p3, Object p4 */ EntryMessage traceEntry(Supplier... paramSuppliers); + EntryMessage traceEntry(StackTraceElement source, Supplier... paramSuppliers); + /** * Logs entry to a method along with its parameters. For example, * @@ -3610,6 +3662,8 @@ void trace(String message, Object p0, Object p1, Object p2, Object p3, Object p4 */ EntryMessage traceEntry(String format, Supplier... paramSuppliers); + EntryMessage traceEntry(StackTraceElement source, String format, Supplier... paramSuppliers); + /** * Logs entry to a method using a Message to describe the parameters. *
@@ -3633,6 +3687,8 @@ void trace(String message, Object p0, Object p1, Object p2, Object p3, Object p4
      */
     EntryMessage traceEntry(Message message);
 
+    EntryMessage traceEntry(StackTraceElement source, Message message);
+
     /**
      * Logs exit from a method. Used for methods that do not return anything.
      *
@@ -3640,6 +3696,13 @@ void trace(String message, Object p0, Object p1, Object p2, Object p3, Object p4
      */
     void traceExit();
 
+    /**
+     * Logs exit from a method. Used for methods that do not return anything.
+     *
+     * @since
+     */
+    void traceExit(StackTraceElement source);
+
     /**
      * Logs exiting from a method with the result. This may be coded as:
      *
@@ -3655,6 +3718,8 @@ void trace(String message, Object p0, Object p1, Object p2, Object p3, Object p4
      */
      R traceExit(R result);
 
+     R traceExit(StackTraceElement source, R result);
+
     /**
      * Logs exiting from a method with the result. This may be coded as:
      *
@@ -3671,6 +3736,8 @@ void trace(String message, Object p0, Object p1, Object p2, Object p3, Object p4
      */
      R traceExit(String format, R result);
 
+     R traceExit(StackTraceElement source, String format, R result);
+
     /**
      * Logs exiting from a method with no result. Allows custom formatting of the result. This may be coded as:
      *
@@ -3687,6 +3754,8 @@ void trace(String message, Object p0, Object p1, Object p2, Object p3, Object p4
      */
     void traceExit(EntryMessage message);
 
+    void traceExit(StackTraceElement source, EntryMessage message);
+
     /**
      * Logs exiting from a method with the result. Allows custom formatting of the result. This may be coded as:
      *
@@ -3707,6 +3776,8 @@ void trace(String message, Object p0, Object p1, Object p2, Object p3, Object p4
      */
      R traceExit(EntryMessage message, R result);
 
+     R traceExit(StackTraceElement source, EntryMessage message, R result);
+
     /**
      * Logs exiting from a method with the result. Allows custom formatting of the result. This may be coded as:
      *
@@ -3723,6 +3794,8 @@ void trace(String message, Object p0, Object p1, Object p2, Object p3, Object p4
      */
      R traceExit(Message message, R result);
 
+     R traceExit(StackTraceElement source, Message message, R result);
+
     /**
      * Logs a message with the specific Marker at the {@link Level#WARN WARN} level.
      *
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/message/AbstractMessageFactory.java b/log4j-api/src/main/java/org/apache/logging/log4j/message/AbstractMessageFactory.java
index 6429ed7852a..d42c4c8313c 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/message/AbstractMessageFactory.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/message/AbstractMessageFactory.java
@@ -36,7 +36,7 @@ public abstract class AbstractMessageFactory implements MessageFactory2, Seriali
 
     @Override
     public Message newMessage(final CharSequence message) {
-        return new SimpleMessage(message);
+        return newMessage((StackTraceElement) null, message);
     }
 
     /*
@@ -46,7 +46,7 @@ public Message newMessage(final CharSequence message) {
      */
     @Override
     public Message newMessage(final Object message) {
-        return new ObjectMessage(message);
+        return newMessage((StackTraceElement) null, message);
     }
 
     /*
@@ -56,7 +56,7 @@ public Message newMessage(final Object message) {
      */
     @Override
     public Message newMessage(final String message) {
-        return new SimpleMessage(message);
+        return newMessage((StackTraceElement) null, message);
     }
 
     /**
@@ -64,7 +64,7 @@ public Message newMessage(final String message) {
      */
     @Override
     public Message newMessage(final String message, final Object p0) {
-        return newMessage(message, new Object[] { p0 });
+        return newMessage((StackTraceElement) null, message, p0);
     }
 
     /**
@@ -72,7 +72,7 @@ public Message newMessage(final String message, final Object p0) {
      */
     @Override
     public Message newMessage(final String message, final Object p0, final Object p1) {
-        return newMessage(message, new Object[] { p0, p1 });
+        return newMessage((StackTraceElement) null, message, p0, p1);
     }
 
     /**
@@ -80,7 +80,7 @@ public Message newMessage(final String message, final Object p0, final Object p1
      */
     @Override
     public Message newMessage(final String message, final Object p0, final Object p1, final Object p2) {
-        return newMessage(message, new Object[] { p0, p1, p2 });
+        return newMessage((StackTraceElement) null, message, p0, p1, p2);
     }
 
     /**
@@ -88,7 +88,7 @@ public Message newMessage(final String message, final Object p0, final Object p1
      */
     @Override
     public Message newMessage(final String message, final Object p0, final Object p1, final Object p2, final Object p3) {
-        return newMessage(message, new Object[] { p0, p1, p2, p3 });
+        return newMessage((StackTraceElement) null, message, p0, p1, p2, p3);
     }
 
     /**
@@ -96,7 +96,7 @@ public Message newMessage(final String message, final Object p0, final Object p1
      */
     @Override
     public Message newMessage(final String message, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4) {
-        return newMessage(message, new Object[] { p0, p1, p2, p3, p4 });
+        return newMessage((StackTraceElement) null, message, p0, p1, p2, p3, p4);
     }
 
     /**
@@ -104,7 +104,7 @@ public Message newMessage(final String message, final Object p0, final Object p1
      */
     @Override
     public Message newMessage(final String message, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5) {
-        return newMessage(message, new Object[] { p0, p1, p2, p3, p4, p5 });
+        return newMessage((StackTraceElement) null, message, p0, p1, p2, p3, p4, p5);
     }
 
     /**
@@ -113,7 +113,7 @@ public Message newMessage(final String message, final Object p0, final Object p1
     @Override
     public Message newMessage(final String message, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5,
             final Object p6) {
-        return newMessage(message, new Object[] { p0, p1, p2, p3, p4, p5, p6 });
+        return newMessage((StackTraceElement) null, message, p0, p1, p2, p3, p4, p5, p6);
     }
 
     /**
@@ -122,7 +122,7 @@ public Message newMessage(final String message, final Object p0, final Object p1
     @Override
     public Message newMessage(final String message, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5,
             final Object p6, final Object p7) {
-        return newMessage(message, new Object[] { p0, p1, p2, p3, p4, p5, p6, p7 });
+        return newMessage((StackTraceElement) null, message, p0, p1, p2, p3, p4, p5, p6, p7);
     }
 
     /**
@@ -131,7 +131,7 @@ public Message newMessage(final String message, final Object p0, final Object p1
     @Override
     public Message newMessage(final String message, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5,
             final Object p6, final Object p7, final Object p8) {
-        return newMessage(message, new Object[] { p0, p1, p2, p3, p4, p5, p6, p7, p8 });
+        return newMessage((StackTraceElement) null, message, p0, p1, p2, p3, p4, p5, p6, p7, p8);
     }
 
     /**
@@ -140,7 +140,117 @@ public Message newMessage(final String message, final Object p0, final Object p1
     @Override
     public Message newMessage(final String message, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5,
             final Object p6, final Object p7, final Object p8, final Object p9) {
-        return newMessage(message, new Object[] { p0, p1, p2, p3, p4, p5, p6, p7, p8, p9 });
+        return newMessage(null, message, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9);
+    }
+
+
+    @Override
+    public Message newMessage(final StackTraceElement source, final CharSequence message) {
+        return new SimpleMessage(source, message);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.apache.logging.log4j.message.MessageFactory#newMessage(java.lang.StackTraceElement,java.lang.Object)
+     */
+    @Override
+    public Message newMessage(final StackTraceElement source, final Object message) {
+        return new ObjectMessage(source, message);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.apache.logging.log4j.message.MessageFactory#newMessage(java.lang.StackTraceElement,java.lang.String)
+     */
+    @Override
+    public Message newMessage(final StackTraceElement source, final String message) {
+        return new SimpleMessage(source, message);
+    }
+
+    /**
+     * @since
+     */
+    @Override
+    public Message newMessage(final StackTraceElement source, final String message, final Object p0) {
+        return newMessage(source, message, new Object[] { p0 });
+    }
+
+    /**
+     * @since
+     */
+    @Override
+    public Message newMessage(final StackTraceElement source, final String message, final Object p0, final Object p1) {
+        return newMessage(source, message, new Object[] { p0, p1 });
+    }
+
+    /**
+     * @since
+     */
+    @Override
+    public Message newMessage(final StackTraceElement source, final String message, final Object p0, final Object p1, final Object p2) {
+        return newMessage(source, message, new Object[] { p0, p1, p2 });
+    }
+
+    /**
+     * @since
+     */
+    @Override
+    public Message newMessage(final StackTraceElement source, final String message, final Object p0, final Object p1, final Object p2, final Object p3) {
+        return newMessage(source, message, new Object[] { p0, p1, p2, p3 });
+    }
+
+    /**
+     * @since
+     */
+    @Override
+    public Message newMessage(final StackTraceElement source, final String message, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4) {
+        return newMessage(source, message, new Object[] { p0, p1, p2, p3, p4 });
+    }
+
+    /**
+     * @since
+     */
+    @Override
+    public Message newMessage(final StackTraceElement source, final String message, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5) {
+        return newMessage(source, message, new Object[] { p0, p1, p2, p3, p4, p5 });
+    }
+
+    /**
+     * @since
+     */
+    @Override
+    public Message newMessage(final StackTraceElement source, final String message, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5,
+                              final Object p6) {
+        return newMessage(source, message, new Object[] { p0, p1, p2, p3, p4, p5, p6 });
+    }
+
+    /**
+     * @since
+     */
+    @Override
+    public Message newMessage(final StackTraceElement source, final String message, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5,
+                              final Object p6, final Object p7) {
+        return newMessage(source, message, new Object[] { p0, p1, p2, p3, p4, p5, p6, p7 });
+    }
+
+    /**
+     * @since
+     */
+    @Override
+    public Message newMessage(final StackTraceElement source, final String message, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5,
+                              final Object p6, final Object p7, final Object p8) {
+        return newMessage(source, message, new Object[] { p0, p1, p2, p3, p4, p5, p6, p7, p8 });
+    }
+
+    /**
+     * @since
+     */
+    @Override
+    public Message newMessage(final StackTraceElement source, final String message, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5,
+                              final Object p6, final Object p7, final Object p8, final Object p9) {
+        return newMessage(source, message, new Object[] { p0, p1, p2, p3, p4, p5, p6, p7, p8, p9 });
     }
 
 }
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/message/DefaultFlowMessageFactory.java b/log4j-api/src/main/java/org/apache/logging/log4j/message/DefaultFlowMessageFactory.java
index 5babca3251a..024b68dd961 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/message/DefaultFlowMessageFactory.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/message/DefaultFlowMessageFactory.java
@@ -53,10 +53,12 @@ public DefaultFlowMessageFactory(final String entryText, final String exitText)
     private static class AbstractFlowMessage implements FlowMessage {
 
         private static final long serialVersionUID = 1L;
+        private final StackTraceElement source;
         private final Message message;
         private final String text;
 
-        AbstractFlowMessage(final String text, final Message message) {
+        AbstractFlowMessage(final StackTraceElement source, final String text, final Message message) {
+            this.source = source;
             this.message = message;
             this.text = text;
         }
@@ -102,14 +104,19 @@ public Message getMessage() {
         public String getText() {
             return text;
         }
+
+        @Override
+        public StackTraceElement getSource() {
+            return source;
+        }
     }
 
     private static final class SimpleEntryMessage extends AbstractFlowMessage implements EntryMessage {
 
         private static final long serialVersionUID = 1L;
 
-        SimpleEntryMessage(final String entryText, final Message message) {
-            super(entryText, message);
+        SimpleEntryMessage(final StackTraceElement source, final String entryText, final Message message) {
+            super(source, entryText, message);
         }
 
     }
@@ -121,20 +128,20 @@ private static final class SimpleExitMessage extends AbstractFlowMessage impleme
         private final Object result;
         private final boolean isVoid;
 
-        SimpleExitMessage(final String exitText, final EntryMessage message) {
-            super(exitText, message.getMessage());
+        SimpleExitMessage(final StackTraceElement source, final String exitText, final EntryMessage message) {
+            super(source, exitText, message.getMessage());
             this.result = null;
             isVoid = true;
         }
 
-        SimpleExitMessage(final String exitText, final Object result, final EntryMessage message) {
-            super(exitText, message.getMessage());
+        SimpleExitMessage(final StackTraceElement source, final String exitText, final Object result, final EntryMessage message) {
+            super(source, exitText, message.getMessage());
             this.result = result;
             isVoid = false;
         }
 
-        SimpleExitMessage(final String exitText, final Object result, final Message message) {
-            super(exitText, message);
+        SimpleExitMessage(final StackTraceElement source, final String exitText, final Object result, final Message message) {
+            super(source, exitText, message);
             this.result = result;
             isVoid = false;
         }
@@ -171,15 +178,20 @@ public String getExitText() {
      * @see org.apache.logging.log4j.message.MessageFactory#newEntryMessage(org.apache.logging.log4j.message.Message)
      */
     @Override
-    public EntryMessage newEntryMessage(final Message message) {
-        return new SimpleEntryMessage(entryText, makeImmutable(message));
+    public EntryMessage newEntryMessage(final StackTraceElement source, final Message message) {
+        return new SimpleEntryMessage(source, entryText, makeImmutable(message));
+    }
+
+    @Override
+    public EntryMessage newEntryMessage(Message message) {
+        return newEntryMessage(null, message);
     }
 
     private Message makeImmutable(final Message message) {
         if (!(message instanceof ReusableMessage)) {
             return message;
         }
-        return new SimpleMessage(message.getFormattedMessage());
+        return new SimpleMessage(message.getSource(), message.getFormattedMessage());
     }
 
     /*
@@ -188,8 +200,13 @@ private Message makeImmutable(final Message message) {
      * @see org.apache.logging.log4j.message.FlowMessageFactory#newExitMessage(org.apache.logging.log4j.message.EntryMessage)
      */
     @Override
-    public ExitMessage newExitMessage(final EntryMessage message) {
-        return new SimpleExitMessage(exitText, message);
+    public ExitMessage newExitMessage(final StackTraceElement source, final EntryMessage message) {
+        return new SimpleExitMessage(source, exitText, message);
+    }
+
+    @Override
+    public ExitMessage newExitMessage(EntryMessage message) {
+        return newExitMessage(null, message);
     }
 
     /*
@@ -198,8 +215,13 @@ public ExitMessage newExitMessage(final EntryMessage message) {
      * @see org.apache.logging.log4j.message.FlowMessageFactory#newExitMessage(java.lang.Object, org.apache.logging.log4j.message.EntryMessage)
      */
     @Override
-    public ExitMessage newExitMessage(final Object result, final EntryMessage message) {
-        return new SimpleExitMessage(exitText, result, message);
+    public ExitMessage newExitMessage(final StackTraceElement source, final Object result, final EntryMessage message) {
+        return new SimpleExitMessage(source, exitText, result, message);
+    }
+
+    @Override
+    public ExitMessage newExitMessage(Object result, Message message) {
+        return newExitMessage(null, result, message);
     }
 
     /*
@@ -208,7 +230,12 @@ public ExitMessage newExitMessage(final Object result, final EntryMessage messag
      * @see org.apache.logging.log4j.message.FlowMessageFactory#newExitMessage(java.lang.Object, org.apache.logging.log4j.message.Message)
      */
     @Override
-    public ExitMessage newExitMessage(final Object result, final Message message) {
-        return new SimpleExitMessage(exitText, result, message);
+    public ExitMessage newExitMessage(final StackTraceElement source, final Object result, final Message message) {
+        return new SimpleExitMessage(source, exitText, result, message);
+    }
+
+    @Override
+    public ExitMessage newExitMessage(Object result, EntryMessage message) {
+        return newExitMessage(null, result, message);
     }
 }
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/message/FlowMessageFactory.java b/log4j-api/src/main/java/org/apache/logging/log4j/message/FlowMessageFactory.java
index 56ce7e71331..83f8a24e06f 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/message/FlowMessageFactory.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/message/FlowMessageFactory.java
@@ -21,38 +21,51 @@
  * @since 2.6
  */
 public interface FlowMessageFactory {
-    
+
     /**
      * Creates a new entry message based on an existing message.
      *
+     * @param source the location of the log statement if known at compile time.
      * @param message the original message
      * @return the new entry message
      */
+    EntryMessage newEntryMessage(StackTraceElement source, Message message);
+
     EntryMessage newEntryMessage(Message message);
 
     /**
      * Creates a new exit message based on a return value and an existing message.
      *
+     * @param source the location of the log statement if known at compile time.
      * @param result the return value.
      * @param message the original message
      * @return the new exit message
      */
+    ExitMessage newExitMessage(StackTraceElement source, Object result, Message message);
+
     ExitMessage newExitMessage(Object result, Message message);
 
     /**
      * Creates a new exit message based on no return value and an existing entry message.
      *
+     * @param source the location of the log statement if known at compile time.
      * @param message the original entry message
      * @return the new exit message
      */
+    ExitMessage newExitMessage(StackTraceElement source, EntryMessage message);
+
     ExitMessage newExitMessage(EntryMessage message);
 
     /**
      * Creates a new exit message based on a return value and an existing entry message.
      *
+     * @param source the location of the log statement if known at compile time.
      * @param result the return value.
      * @param message the original entry message
      * @return the new exit message
      */
+    ExitMessage newExitMessage(StackTraceElement source, Object result, EntryMessage message);
+
     ExitMessage newExitMessage(Object result, EntryMessage message);
+
 }
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/message/FormattedMessage.java b/log4j-api/src/main/java/org/apache/logging/log4j/message/FormattedMessage.java
index a13fd99a113..9ed24380bda 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/message/FormattedMessage.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/message/FormattedMessage.java
@@ -43,6 +43,7 @@ public class FormattedMessage implements Message {
     private final Throwable throwable;
     private Message message;
     private final Locale locale;
+    private final StackTraceElement source;
     
     /**
      * Constructs with a locale, a pattern and a single parameter.
@@ -52,7 +53,18 @@ public class FormattedMessage implements Message {
      * @since 2.6
      */
     public FormattedMessage(final Locale locale, final String messagePattern, final Object arg) {
-        this(locale, messagePattern, new Object[] { arg }, null);
+        this((StackTraceElement) null, locale, messagePattern, arg, (Throwable) null);
+    }
+
+    /**
+     * Constructs with a locale, a pattern and a single parameter.
+     * @param locale The locale
+     * @param messagePattern The message pattern.
+     * @param arg The parameter.
+     * @since 2.6
+     */
+    public FormattedMessage(final StackTraceElement source, final Locale locale, final String messagePattern, final Object arg) {
+        this(source, locale, messagePattern, source, new Object[] { arg }, null);
     }
 
     /**
@@ -67,6 +79,18 @@ public FormattedMessage(final Locale locale, final String messagePattern, final
         this(locale, messagePattern, new Object[] { arg1, arg2 });
     }
 
+    /**
+     * Constructs with a locale, a pattern and two parameters.
+     * @param locale The locale
+     * @param messagePattern The message pattern.
+     * @param arg1 The first parameter.
+     * @param arg2 The second parameter.
+     * @since 2.6
+     */
+    public FormattedMessage(final StackTraceElement source, final Locale locale, final String messagePattern, final Object arg1, final Object arg2) {
+        this(source, locale, messagePattern, new Object[] { arg1, arg2 });
+    }
+
     /**
      * Constructs with a locale, a pattern and a parameter array.
      * @param locale The locale
@@ -75,7 +99,18 @@ public FormattedMessage(final Locale locale, final String messagePattern, final
      * @since 2.6
      */
     public FormattedMessage(final Locale locale, final String messagePattern, final Object... arguments) {
-        this(locale, messagePattern, arguments, null);
+        this((StackTraceElement) null, locale, messagePattern, arguments, null);
+    }
+
+    /**
+     * Constructs with a locale, a pattern and a parameter array.
+     * @param locale The locale
+     * @param messagePattern The message pattern.
+     * @param arguments The parameter.
+     * @since 2.6
+     */
+    public FormattedMessage(StackTraceElement source, final Locale locale, final String messagePattern, final Object... arguments) {
+        this(source, locale, messagePattern, arguments, (Throwable) null);
     }
 
     /**
@@ -87,6 +122,19 @@ public FormattedMessage(final Locale locale, final String messagePattern, final
      * @since 2.6
      */
     public FormattedMessage(final Locale locale, final String messagePattern, final Object[] arguments, final Throwable throwable) {
+        this((StackTraceElement) null, locale, messagePattern, arguments, throwable);
+    }
+
+    /**
+     * Constructs with a locale, a pattern, a parameter array, and a throwable.
+     * @param locale The Locale
+     * @param messagePattern The message pattern.
+     * @param arguments The parameter.
+     * @param throwable The throwable
+     * @since 2.6
+     */
+    public FormattedMessage(final StackTraceElement source, final Locale locale, final String messagePattern, final Object[] arguments, final Throwable throwable) {
+        this.source = source;
         this.locale = locale;
         this.messagePattern = messagePattern;
         this.argArray = arguments;
@@ -99,7 +147,16 @@ public FormattedMessage(final Locale locale, final String messagePattern, final
      * @param arg The parameter.
      */
     public FormattedMessage(final String messagePattern, final Object arg) {
-        this(messagePattern, new Object[] { arg }, null);
+        this((StackTraceElement) null, messagePattern, new Object[] { arg }, null);
+    }
+
+    /**
+     * Constructs with a pattern and a single parameter.
+     * @param messagePattern The message pattern.
+     * @param arg The parameter.
+     */
+    public FormattedMessage(final StackTraceElement source, final String messagePattern, final Object arg) {
+        this(source, messagePattern, new Object[] { arg }, null);
     }
 
     /**
@@ -112,6 +169,16 @@ public FormattedMessage(final String messagePattern, final Object arg1, final Ob
         this(messagePattern, new Object[] { arg1, arg2 });
     }
 
+    /**
+     * Constructs with a pattern and two parameters.
+     * @param messagePattern The message pattern.
+     * @param arg1 The first parameter.
+     * @param arg2 The second parameter.
+     */
+    public FormattedMessage(final StackTraceElement source, final String messagePattern, final Object arg1, final Object arg2) {
+        this(source, messagePattern, new Object[] { arg1, arg2 });
+    }
+
     /**
      * Constructs with a pattern and a parameter array.
      * @param messagePattern The message pattern.
@@ -121,6 +188,15 @@ public FormattedMessage(final String messagePattern, final Object... arguments)
         this(messagePattern, arguments, null);
     }
 
+    /**
+     * Constructs with a pattern and a parameter array.
+     * @param messagePattern The message pattern.
+     * @param arguments The parameter.
+     */
+    public FormattedMessage(final StackTraceElement source, final String messagePattern, final Object... arguments) {
+        this(source, messagePattern, arguments, null);
+    }
+
     /**
      * Constructs with a pattern, a parameter array, and a throwable.
      * @param messagePattern The message pattern.
@@ -128,13 +204,23 @@ public FormattedMessage(final String messagePattern, final Object... arguments)
      * @param throwable The throwable
      */
     public FormattedMessage(final String messagePattern, final Object[] arguments, final Throwable throwable) {
+        this((StackTraceElement) null, messagePattern, arguments, throwable);
+    }
+
+    /**
+     * Constructs with a pattern, a parameter array, and a throwable.
+     * @param messagePattern The message pattern.
+     * @param arguments The parameter.
+     * @param throwable The throwable
+     */
+    public FormattedMessage(final StackTraceElement source, final String messagePattern, final Object[] arguments, final Throwable throwable) {
         this.locale = Locale.getDefault(Locale.Category.FORMAT);
         this.messagePattern = messagePattern;
         this.argArray = arguments;
         this.throwable = throwable;
+        this.source = source;
     }
 
-
     @Override
     public boolean equals(final Object o) {
         if (this == o) {
@@ -152,6 +238,9 @@ public boolean equals(final Object o) {
         if (!Arrays.equals(stringArgs, that.stringArgs)) {
             return false;
         }
+        if (source != null ? !source.equals(that.source) : that.source != null) {
+            return false;
+        }
 
         return true;
     }
@@ -173,26 +262,26 @@ public String getFormat() {
     public String getFormattedMessage() {
         if (formattedMessage == null) {
             if (message == null) {
-                message = getMessage(messagePattern, argArray, throwable);
+                message = getMessage(source, messagePattern, argArray, throwable);
             }
             formattedMessage = message.getFormattedMessage();
         }
         return formattedMessage;
     }
 
-    protected Message getMessage(final String msgPattern, final Object[] args, final Throwable aThrowable) {
+    protected Message getMessage(final StackTraceElement source, final String msgPattern, final Object[] args, final Throwable aThrowable) {
         try {
             final MessageFormat format = new MessageFormat(msgPattern);
             final Format[] formats = format.getFormats();
             if (formats != null && formats.length > 0) {
-                return new MessageFormatMessage(locale, msgPattern, args);
+                return new MessageFormatMessage(source, locale, msgPattern, args);
             }
         } catch (final Exception ignored) {
             // Obviously, the message is not a proper pattern for MessageFormat.
         }
         try {
             if (MSG_PATTERN.matcher(msgPattern).find()) {
-                return new StringFormattedMessage(locale, msgPattern, args);
+                return new StringFormattedMessage(source, locale, msgPattern, args);
             }
         } catch (final Exception ignored) {
             // Also not properly formatted.
@@ -218,11 +307,15 @@ public Throwable getThrowable() {
             return throwable;
         }
         if (message == null) {
-            message = getMessage(messagePattern, argArray, null);
+            message = getMessage(source, messagePattern, argArray, null);
         }
         return message.getThrowable();
     }
 
+    @Override
+    public StackTraceElement getSource() {
+        return source;
+    }
 
     @Override
     public int hashCode() {
@@ -261,5 +354,13 @@ private void writeObject(final ObjectOutputStream out) throws IOException {
             out.writeUTF(string);
             ++i;
         }
+        boolean hasSource = source != null;
+        out.writeBoolean(hasSource);
+        if (hasSource) {
+            MessageFormatMessage.writeNullableString(out, source.getFileName());
+            MessageFormatMessage.writeNullableString(out, source.getFileName());
+            MessageFormatMessage.writeNullableString(out, source.getFileName());
+            MessageFormatMessage.writeNullableInt(out, source.getLineNumber());
+        }
     }
 }
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/message/FormattedMessageFactory.java b/log4j-api/src/main/java/org/apache/logging/log4j/message/FormattedMessageFactory.java
index 805e24b68e2..21c25bdefeb 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/message/FormattedMessageFactory.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/message/FormattedMessageFactory.java
@@ -133,4 +133,9 @@ public Message newMessage(final String message, final Object p0, final Object p1
             final Object p6, final Object p7, final Object p8, final Object p9) {
         return new FormattedMessage(message, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9);
     }
+
+    @Override
+    public Message newMessage(StackTraceElement source, String message, Object... params) {
+        return new FormattedMessage(source, message, params);
+    }
 }
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/message/LocalizedMessage.java b/log4j-api/src/main/java/org/apache/logging/log4j/message/LocalizedMessage.java
index caf04f728c3..0b23b084552 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/message/LocalizedMessage.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/message/LocalizedMessage.java
@@ -52,6 +52,7 @@ public class LocalizedMessage implements Message, LoggerNameAwareMessage {
     private transient Object[] argArray;
     private String formattedMessage;
     private transient Throwable throwable;
+    private StackTraceElement source;
 
     /**
      * Constructor with message pattern and arguments.
@@ -60,95 +61,187 @@ public class LocalizedMessage implements Message, LoggerNameAwareMessage {
      * @param arguments the argument array to be converted.
      */
     public LocalizedMessage(final String messagePattern, final Object[] arguments) {
-        this((ResourceBundle) null, (Locale) null, messagePattern, arguments);
+        this(null, (ResourceBundle) null, (Locale) null, messagePattern, arguments);
     }
 
     public LocalizedMessage(final String baseName, final String key, final Object[] arguments) {
-        this(baseName, (Locale) null, key, arguments);
+        this(null, baseName, (Locale) null, key, arguments);
     }
 
     public LocalizedMessage(final ResourceBundle bundle, final String key, final Object[] arguments) {
-        this(bundle, (Locale) null, key, arguments);
+        this(null, bundle, (Locale) null, key, arguments);
     }
 
     public LocalizedMessage(final String baseName, final Locale locale, final String key, final Object[] arguments) {
-        this.key = key;
-        this.argArray = arguments;
-        this.throwable = null;
-        this.baseName = baseName;
-        this.resourceBundle = null;
-        this.locale = locale;
+        this(null, baseName, locale, key, arguments);
     }
 
     public LocalizedMessage(final ResourceBundle bundle, final Locale locale, final String key,
             final Object[] arguments) {
-        this.key = key;
-        this.argArray = arguments;
-        this.throwable = null;
-        this.baseName = null;
-        this.resourceBundle = bundle;
-        this.locale = locale;
+        this(null, bundle, locale, key, arguments);
     }
 
     public LocalizedMessage(final Locale locale, final String key, final Object[] arguments) {
-        this((ResourceBundle) null, locale, key, arguments);
+        this(null, (ResourceBundle) null, locale, key, arguments);
     }
 
     public LocalizedMessage(final String messagePattern, final Object arg) {
-        this((ResourceBundle) null, (Locale) null, messagePattern, new Object[] {arg});
+        this(null, (ResourceBundle) null, (Locale) null, messagePattern, new Object[] {arg});
     }
 
     public LocalizedMessage(final String baseName, final String key, final Object arg) {
-        this(baseName, (Locale) null, key, new Object[] {arg});
+        this(null, baseName, (Locale) null, key, new Object[] {arg});
     }
 
     /**
      * @since 2.8
      */
     public LocalizedMessage(final ResourceBundle bundle, final String key) {
-        this(bundle, (Locale) null, key, new Object[] {});
+        this(null, bundle, (Locale) null, key, new Object[] {});
     }
 
     public LocalizedMessage(final ResourceBundle bundle, final String key, final Object arg) {
-        this(bundle, (Locale) null, key, new Object[] {arg});
+        this(null, bundle, (Locale) null, key, new Object[] {arg});
     }
 
     public LocalizedMessage(final String baseName, final Locale locale, final String key, final Object arg) {
-        this(baseName, locale, key, new Object[] {arg});
+        this(null, baseName, locale, key, new Object[] {arg});
     }
 
     public LocalizedMessage(final ResourceBundle bundle, final Locale locale, final String key, final Object arg) {
-        this(bundle, locale, key, new Object[] {arg});
+        this(null, bundle, locale, key, new Object[] {arg});
     }
 
     public LocalizedMessage(final Locale locale, final String key, final Object arg) {
-        this((ResourceBundle) null, locale, key, new Object[] {arg});
+        this(null, (ResourceBundle) null, locale, key, new Object[] {arg});
     }
 
     public LocalizedMessage(final String messagePattern, final Object arg1, final Object arg2) {
-        this((ResourceBundle) null, (Locale) null, messagePattern, new Object[] {arg1, arg2});
+        this(null, (ResourceBundle) null, (Locale) null, messagePattern, new Object[] {arg1, arg2});
     }
 
     public LocalizedMessage(final String baseName, final String key, final Object arg1, final Object arg2) {
-        this(baseName, (Locale) null, key, new Object[] {arg1, arg2});
+        this(null, baseName, (Locale) null, key, new Object[] {arg1, arg2});
     }
 
     public LocalizedMessage(final ResourceBundle bundle, final String key, final Object arg1, final Object arg2) {
-        this(bundle, (Locale) null, key, new Object[] {arg1, arg2});
+        this(null, bundle, (Locale) null, key, new Object[] {arg1, arg2});
     }
 
     public LocalizedMessage(final String baseName, final Locale locale, final String key, final Object arg1,
             final Object arg2) {
-        this(baseName, locale, key, new Object[] {arg1, arg2});
+        this(null, baseName, locale, key, new Object[] {arg1, arg2});
     }
 
     public LocalizedMessage(final ResourceBundle bundle, final Locale locale, final String key, final Object arg1,
             final Object arg2) {
-        this(bundle, locale, key, new Object[] {arg1, arg2});
+        this(null, bundle, locale, key, new Object[] {arg1, arg2});
     }
 
     public LocalizedMessage(final Locale locale, final String key, final Object arg1, final Object arg2) {
-        this((ResourceBundle) null, locale, key, new Object[] {arg1, arg2});
+        this(null, (ResourceBundle) null, locale, key, new Object[] {arg1, arg2});
+    }
+
+    /**
+     * Constructor with message pattern and arguments, when the location
+     * of the log statement might be known at compile time.
+     *
+     * @param source the location of the log statement, or null
+     * @param messagePattern the message pattern that to be checked for placeholders.
+     * @param arguments the argument array to be converted.
+     */
+    public LocalizedMessage(final StackTraceElement source, final String messagePattern, final Object[] arguments) {
+        this(source, (ResourceBundle) null, (Locale) null, messagePattern, arguments);
+    }
+
+    public LocalizedMessage(final StackTraceElement source, final String baseName, final String key, final Object[] arguments) {
+        this(source, baseName, (Locale) null, key, arguments);
+    }
+
+    public LocalizedMessage(final StackTraceElement source, final ResourceBundle bundle, final String key, final Object[] arguments) {
+        this(source, bundle, (Locale) null, key, arguments);
+    }
+
+    public LocalizedMessage(final StackTraceElement source, final String baseName, final Locale locale, final String key, final Object[] arguments) {
+        this.key = key;
+        this.argArray = arguments;
+        this.throwable = null;
+        this.baseName = baseName;
+        this.resourceBundle = null;
+        this.locale = locale;
+        this.source = source;
+    }
+
+    public LocalizedMessage(final StackTraceElement source, final ResourceBundle bundle, final Locale locale, final String key,
+                            final Object[] arguments) {
+        this.key = key;
+        this.argArray = arguments;
+        this.throwable = null;
+        this.baseName = null;
+        this.resourceBundle = bundle;
+        this.locale = locale;
+        this.source = source;
+    }
+
+    public LocalizedMessage(final StackTraceElement source, final Locale locale, final String key, final Object[] arguments) {
+        this(source, (ResourceBundle) null, locale, key, arguments);
+    }
+
+    public LocalizedMessage(final StackTraceElement source, final String messagePattern, final Object arg) {
+        this(source, (ResourceBundle) null, (Locale) null, messagePattern, new Object[] {arg});
+    }
+
+    public LocalizedMessage(final StackTraceElement source, final String baseName, final String key, final Object arg) {
+        this(source, baseName, (Locale) null, key, new Object[] {arg});
+    }
+
+    /**
+     * @since
+     */
+    public LocalizedMessage(final StackTraceElement source, final ResourceBundle bundle, final String key) {
+        this(source, bundle, (Locale) null, key, new Object[] {});
+    }
+
+    public LocalizedMessage(final StackTraceElement source, final ResourceBundle bundle, final String key, final Object arg) {
+        this(source, bundle, (Locale) null, key, new Object[] {arg});
+    }
+
+    public LocalizedMessage(final StackTraceElement source, final String baseName, final Locale locale, final String key, final Object arg) {
+        this(source, baseName, locale, key, new Object[] {arg});
+    }
+
+    public LocalizedMessage(final StackTraceElement source, final ResourceBundle bundle, final Locale locale, final String key, final Object arg) {
+        this(source, bundle, locale, key, new Object[] {arg});
+    }
+
+    public LocalizedMessage(final StackTraceElement source, final Locale locale, final String key, final Object arg) {
+        this(source, (ResourceBundle) null, locale, key, new Object[] {arg});
+    }
+
+    public LocalizedMessage(final StackTraceElement source, final String messagePattern, final Object arg1, final Object arg2) {
+        this(source, (ResourceBundle) null, (Locale) null, messagePattern, new Object[] {arg1, arg2});
+    }
+
+    public LocalizedMessage(final StackTraceElement source, final String baseName, final String key, final Object arg1, final Object arg2) {
+        this(source, baseName, (Locale) null, key, new Object[] {arg1, arg2});
+    }
+
+    public LocalizedMessage(final StackTraceElement source, final ResourceBundle bundle, final String key, final Object arg1, final Object arg2) {
+        this(source, bundle, (Locale) null, key, new Object[] {arg1, arg2});
+    }
+
+    public LocalizedMessage(final StackTraceElement source, final String baseName, final Locale locale, final String key, final Object arg1,
+                            final Object arg2) {
+        this(source, baseName, locale, key, new Object[] {arg1, arg2});
+    }
+
+    public LocalizedMessage(final StackTraceElement source, final ResourceBundle bundle, final Locale locale, final String key, final Object arg1,
+                            final Object arg2) {
+        this(source, bundle, locale, key, new Object[] {arg1, arg2});
+    }
+
+    public LocalizedMessage(final StackTraceElement source, final Locale locale, final String key, final Object arg1, final Object arg2) {
+        this(source, (ResourceBundle) null, locale, key, new Object[] {arg1, arg2});
     }
 
     /**
@@ -216,6 +309,11 @@ public Throwable getThrowable() {
         return throwable;
     }
 
+    @Override
+    public StackTraceElement getSource() {
+        return source;
+    }
+
     /**
      * Override this to use a ResourceBundle.Control in Java 6
      * 
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/message/LocalizedMessageFactory.java b/log4j-api/src/main/java/org/apache/logging/log4j/message/LocalizedMessageFactory.java
index b7e9803509b..123eb338c18 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/message/LocalizedMessageFactory.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/message/LocalizedMessageFactory.java
@@ -73,7 +73,7 @@ public Message newMessage(final String key) {
         }
         return new LocalizedMessage(resourceBundle, key);
     }
-    
+
     /**
      * Creates {@link LocalizedMessage} instances.
      *
@@ -85,10 +85,25 @@ public Message newMessage(final String key) {
      */
     @Override
     public Message newMessage(final String key, final Object... params) {
+        return newMessage((StackTraceElement) null, key, params);
+    }
+
+    /**
+     * Creates {@link LocalizedMessage} instances, when the location
+     * of the log statement might be known at compile time.
+     *
+     * @param source the location of the log statement, or null
+     * @param message The key String, used as a message if the key is absent.
+     * @param params The parameters for the message at the given key.
+     * @return The LocalizedMessage.
+     *
+     * @see org.apache.logging.log4j.message.MessageFactory#newMessage(String, Object...)
+     */
+    @Override
+    public Message newMessage(StackTraceElement source, String message, Object... params) {
         if (resourceBundle == null) {
-            return new LocalizedMessage(baseName, key, params);
+            return new LocalizedMessage(source, baseName, message, params);
         }
-        return new LocalizedMessage(resourceBundle, key, params);
+        return new LocalizedMessage(source, resourceBundle, message, params);
     }
-
 }
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/message/MapMessage.java b/log4j-api/src/main/java/org/apache/logging/log4j/message/MapMessage.java
index 38319d23da6..5ac12305a73 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/message/MapMessage.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/message/MapMessage.java
@@ -91,12 +91,19 @@ public static String[] names() {
     }
 
     private final IndexedStringMap data;
+    private final StackTraceElement source;
 
     /**
      * Constructs a new instance.
      */
     public MapMessage() {
         this.data = new SortedArrayStringMap();
+        source = null;
+    }
+
+    public MapMessage(StackTraceElement source) {
+        this.data = new SortedArrayStringMap();
+        this.source = source;
     }
 
     /**
@@ -105,7 +112,17 @@ public MapMessage() {
      * @param  initialCapacity the initial capacity.
      */
     public MapMessage(final int initialCapacity) {
+        this(null, initialCapacity);
+    }
+
+    /**
+     * Constructs a new instance.
+     *
+     * @param  initialCapacity the initial capacity.
+     */
+    public MapMessage(final StackTraceElement source, final int initialCapacity) {
         this.data = new SortedArrayStringMap(initialCapacity);
+        this.source = source;
     }
 
     /**
@@ -113,7 +130,16 @@ public MapMessage(final int initialCapacity) {
      * @param map The Map.
      */
     public MapMessage(final Map map) {
+        this(null, map);
+    }
+
+    /**
+     * Constructs a new instance based on an existing Map.
+     * @param map The Map.
+     */
+    public MapMessage(final StackTraceElement source, final Map map) {
         this.data = new SortedArrayStringMap(map);
+        this.source = source;
     }
 
     @Override
@@ -157,6 +183,11 @@ public Map getData() {
         return Collections.unmodifiableMap(result);
     }
 
+    @Override
+    public StackTraceElement getSource() {
+        return source;
+    }
+
     /**
      * Returns a read-only view of the message data.
      * @return the read-only message data.
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/message/Message.java b/log4j-api/src/main/java/org/apache/logging/log4j/message/Message.java
index 42dc9d5d9a6..2ae3d520085 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/message/Message.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/message/Message.java
@@ -94,4 +94,11 @@ public interface Message extends Serializable {
      * @return the throwable or null.
      */
     Throwable getThrowable();
+
+    /**
+     * A StackTraceElement providing the location of the log statement.
+     * @return a StackTraceElement with the log statement's location, or null.
+     */
+    StackTraceElement getSource();
+
 }
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/message/MessageFactory.java b/log4j-api/src/main/java/org/apache/logging/log4j/message/MessageFactory.java
index 009d5a62cb1..1c39940a5e9 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/message/MessageFactory.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/message/MessageFactory.java
@@ -33,6 +33,18 @@ public interface MessageFactory {
      */
     Message newMessage(Object message);
 
+    /**
+     * Creates a new message based on an Object, when the location
+     * of the log statement might be known at compile time.
+     *
+     * @param source
+     *            the location of the log statement, or null
+     * @param message
+     *            a message object
+     * @return a new message
+     */
+    Message newMessage(StackTraceElement source, Object message);
+
     /**
      * Creates a new message based on a String.
      *
@@ -42,6 +54,18 @@ public interface MessageFactory {
      */
     Message newMessage(String message);
 
+    /**
+     * Creates a new message based on a String, when the location
+     * of the log statement might be known at compile time.
+     *
+     * @param source
+     *            the location of the log statement, or null
+     * @param message
+     *            a message String
+     * @return a new message
+     */
+    Message newMessage(StackTraceElement source, String message);
+
     /**
      * Creates a new parameterized message.
      *
@@ -54,4 +78,20 @@ public interface MessageFactory {
      * @see StringFormatterMessageFactory
      */
     Message newMessage(String message, Object... params);
+
+    /**
+     * Creates a new parameterized message, when the location
+     * of the log statement might be known at compile time.
+     *
+     * @param source
+     *            the location of the log statement, or null
+     * @param message
+     *            a message template, the kind of message template depends on the implementation.
+     * @param params
+     *            the message parameters
+     * @return a new message
+     * @see ParameterizedMessageFactory
+     * @see StringFormatterMessageFactory
+     */
+    Message newMessage(StackTraceElement source, String message, Object... params);
 }
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/message/MessageFactory2.java b/log4j-api/src/main/java/org/apache/logging/log4j/message/MessageFactory2.java
index 33004dd8c73..85c8d12773c 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/message/MessageFactory2.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/message/MessageFactory2.java
@@ -178,4 +178,182 @@ Message newMessage(String message, Object p0, Object p1, Object p2, Object p3, O
      */
     Message newMessage(String message, Object p0, Object p1, Object p2, Object p3, Object p4, Object p5, Object p6,
             Object p7, Object p8, Object p9);
+
+
+    /**
+     * Creates a new message for the specified CharSequence, when the location
+     * of the log statement might be known at compile time.
+     * @param source the location of the log statement, or null
+     * @param charSequence the (potentially mutable) CharSequence
+     * @return a new message for the specified CharSequence
+     */
+    Message newMessage(StackTraceElement source, CharSequence charSequence);
+
+    /**
+     * Creates a new parameterized message, when the location
+     * of the log statement might be known at compile time.
+     *
+     * @param source the location of the log statement, or null
+     * @param message a message template, the kind of message template depends on the implementation.
+     * @param p0 a message parameter
+     * @return a new message
+     * @see ParameterizedMessageFactory
+     */
+    Message newMessage(StackTraceElement source, String message, Object p0);
+
+    /**
+     * Creates a new parameterized message, when the location
+     * of the log statement might be known at compile time.
+     *
+     * @param source the location of the log statement, or null
+     * @param message a message template, the kind of message template depends on the implementation.
+     * @param p0 a message parameter
+     * @param p1 a message parameter
+     * @return a new message
+     * @see ParameterizedMessageFactory
+     */
+    Message newMessage(StackTraceElement source, String message, Object p0, Object p1);
+
+    /**
+     * Creates a new parameterized message, when the location
+     * of the log statement might be known at compile time.
+     *
+     * @param source the location of the log statement, or null
+     * @param message a message template, the kind of message template depends on the implementation.
+     * @param p0 a message parameter
+     * @param p1 a message parameter
+     * @param p2 a message parameter
+     * @return a new message
+     * @see ParameterizedMessageFactory
+     */
+    Message newMessage(StackTraceElement source, String message, Object p0, Object p1, Object p2);
+
+    /**
+     * Creates a new parameterized message, when the location
+     * of the log statement might be known at compile time.
+     *
+     * @param source the location of the log statement, or null
+     * @param message a message template, the kind of message template depends on the implementation.
+     * @param p0 a message parameter
+     * @param p1 a message parameter
+     * @param p2 a message parameter
+     * @param p3 a message parameter
+     * @return a new message
+     * @see ParameterizedMessageFactory
+     */
+    Message newMessage(StackTraceElement source, String message, Object p0, Object p1, Object p2, Object p3);
+
+    /**
+     * Creates a new parameterized message, when the location
+     * of the log statement might be known at compile time.
+     *
+     * @param source the location of the log statement, or null
+     * @param message a message template, the kind of message template depends on the implementation.
+     * @param p0 a message parameter
+     * @param p1 a message parameter
+     * @param p2 a message parameter
+     * @param p3 a message parameter
+     * @param p4 a message parameter
+     * @return a new message
+     * @see ParameterizedMessageFactory
+     */
+    Message newMessage(StackTraceElement source, String message, Object p0, Object p1, Object p2, Object p3, Object p4);
+
+    /**
+     * Creates a new parameterized message, when the location
+     * of the log statement might be known at compile time.
+     *
+     * @param source the location of the log statement, or null
+     * @param message a message template, the kind of message template depends on the implementation.
+     * @param p0 a message parameter
+     * @param p1 a message parameter
+     * @param p2 a message parameter
+     * @param p3 a message parameter
+     * @param p4 a message parameter
+     * @param p5 a message parameter
+     * @return a new message
+     * @see ParameterizedMessageFactory
+     */
+    Message newMessage(StackTraceElement source, String message, Object p0, Object p1, Object p2, Object p3, Object p4, Object p5);
+
+    /**
+     * Creates a new parameterized message, when the location
+     * of the log statement might be known at compile time.
+     *
+     * @param source the location of the log statement, or null
+     * @param message a message template, the kind of message template depends on the implementation.
+     * @param p0 a message parameter
+     * @param p1 a message parameter
+     * @param p2 a message parameter
+     * @param p3 a message parameter
+     * @param p4 a message parameter
+     * @param p5 a message parameter
+     * @param p6 a message parameter
+     * @return a new message
+     * @see ParameterizedMessageFactory
+     */
+    Message newMessage(StackTraceElement source, String message, Object p0, Object p1, Object p2, Object p3, Object p4, Object p5, Object p6);
+
+    /**
+     * Creates a new parameterized message, when the location
+     * of the log statement might be known at compile time.
+     *
+     * @param source the location of the log statement, or null
+     * @param message a message template, the kind of message template depends on the implementation.
+     * @param p0 a message parameter
+     * @param p1 a message parameter
+     * @param p2 a message parameter
+     * @param p3 a message parameter
+     * @param p4 a message parameter
+     * @param p5 a message parameter
+     * @param p6 a message parameter
+     * @param p7 a message parameter
+     * @return a new message
+     * @see ParameterizedMessageFactory
+     */
+    Message newMessage(StackTraceElement source, String message, Object p0, Object p1, Object p2, Object p3, Object p4, Object p5, Object p6,
+                       Object p7);
+
+    /**
+     * Creates a new parameterized message, when the location
+     * of the log statement might be known at compile time.
+     *
+     * @param source the location of the log statement, or null
+     * @param message a message template, the kind of message template depends on the implementation.
+     * @param p0 a message parameter
+     * @param p1 a message parameter
+     * @param p2 a message parameter
+     * @param p3 a message parameter
+     * @param p4 a message parameter
+     * @param p5 a message parameter
+     * @param p6 a message parameter
+     * @param p7 a message parameter
+     * @param p8 a message parameter
+     * @return a new message
+     * @see ParameterizedMessageFactory
+     */
+    Message newMessage(StackTraceElement source, String message, Object p0, Object p1, Object p2, Object p3, Object p4, Object p5, Object p6,
+                       Object p7, Object p8);
+
+    /**
+     * Creates a new parameterized message, when the location
+     * of the log statement might be known at compile time.
+     *
+     * @param source the location of the log statement, or null
+     * @param message a message template, the kind of message template depends on the implementation.
+     * @param p0 a message parameter
+     * @param p1 a message parameter
+     * @param p2 a message parameter
+     * @param p3 a message parameter
+     * @param p4 a message parameter
+     * @param p5 a message parameter
+     * @param p6 a message parameter
+     * @param p7 a message parameter
+     * @param p8 a message parameter
+     * @param p9 a message parameter
+     * @return a new message
+     * @see ParameterizedMessageFactory
+     */
+    Message newMessage(StackTraceElement source, String message, Object p0, Object p1, Object p2, Object p3, Object p4, Object p5, Object p6,
+                       Object p7, Object p8, Object p9);
 }
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/message/MessageFormatMessage.java b/log4j-api/src/main/java/org/apache/logging/log4j/message/MessageFormatMessage.java
index b34a72d57ee..7bd1f0a79d3 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/message/MessageFormatMessage.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/message/MessageFormatMessage.java
@@ -24,6 +24,7 @@
 import java.util.Arrays;
 import java.util.IllegalFormatException;
 import java.util.Locale;
+import java.util.Objects;
 
 import org.apache.logging.log4j.Logger;
 import org.apache.logging.log4j.status.StatusLogger;
@@ -33,12 +34,14 @@
  *
  * @serial In version 2.1, due to a bug in the serialization format, the serialization format was changed along with
  * its {@code serialVersionUID} value.
+ * @serial In version , due to the addition of {@code source}, the serialization format was changed along with its
+ * {@code serialVersionUID} value.
  */
 public class MessageFormatMessage implements Message {
 
     private static final Logger LOGGER = StatusLogger.getLogger();
 
-    private static final long serialVersionUID = 1L;
+    private static final long serialVersionUID = 2L;
 
     private static final int HASHVAL = 31;
 
@@ -48,6 +51,7 @@ public class MessageFormatMessage implements Message {
     private transient String formattedMessage;
     private transient Throwable throwable;
     private final Locale locale;
+    private StackTraceElement source;
 
     /**
      * Constructs a message.
@@ -58,6 +62,19 @@ public class MessageFormatMessage implements Message {
      * @since 2.6
      */
     public MessageFormatMessage(final Locale locale, final String messagePattern, final Object... parameters) {
+        this(null, locale, messagePattern, parameters);
+    }
+
+    /**
+     * Constructs a message.
+     *
+     * @param locale the locale for this message format
+     * @param messagePattern the pattern for this message format
+     * @param parameters The objects to format
+     * @since 2.6
+     */
+    public MessageFormatMessage(StackTraceElement source, final Locale locale, final String messagePattern, final Object... parameters) {
+        this.source = source;
         this.locale = locale;
         this.messagePattern = messagePattern;
         this.parameters = parameters;
@@ -65,6 +82,11 @@ public MessageFormatMessage(final Locale locale, final String messagePattern, fi
         if (length > 0 && parameters[length - 1] instanceof Throwable) {
             this.throwable = (Throwable) parameters[length - 1];
         }
+        if (length > 0 && parameters[length - 1] instanceof StackTraceElement) {
+            this.source = (StackTraceElement) parameters[length - 1];
+        } else if (length > 1 && parameters[length - 2] instanceof StackTraceElement) {
+            this.source = (StackTraceElement) parameters[length - 2];
+        }
     }
 
     /**
@@ -74,7 +96,17 @@ public MessageFormatMessage(final Locale locale, final String messagePattern, fi
      * @param parameters The objects to format
      */
     public MessageFormatMessage(final String messagePattern, final Object... parameters) {
-        this(Locale.getDefault(Locale.Category.FORMAT), messagePattern, parameters);
+        this((StackTraceElement) null, messagePattern, parameters);
+    }
+
+    /**
+     * Constructs a message.
+     *
+     * @param messagePattern the pattern for this message format
+     * @param parameters The objects to format
+     */
+    public MessageFormatMessage(final StackTraceElement source, final String messagePattern, final Object... parameters) {
+        this(source, Locale.getDefault(Locale.Category.FORMAT), messagePattern, parameters);
     }
 
     /**
@@ -131,20 +163,21 @@ public boolean equals(final Object o) {
 
         final MessageFormatMessage that = (MessageFormatMessage) o;
 
-        if (messagePattern != null ? !messagePattern.equals(that.messagePattern) : that.messagePattern != null) {
+        if (!Objects.equals(messagePattern, that.messagePattern) || !Objects.equals(source, that.source)) {
             return false;
         }
+
         return Arrays.equals(serializedParameters, that.serializedParameters);
     }
 
     @Override
     public int hashCode() {
-        int result = messagePattern != null ? messagePattern.hashCode() : 0;
+        int result = Objects.hashCode(messagePattern);
         result = HASHVAL * result + (serializedParameters != null ? Arrays.hashCode(serializedParameters) : 0);
+        result = HASHVAL * result + Objects.hashCode(source);
         return result;
     }
 
-
     @Override
     public String toString() {
         return getFormattedMessage();
@@ -163,6 +196,66 @@ private void writeObject(final ObjectOutputStream out) throws IOException {
                 out.writeUTF(serializedParameters[i]);
             }
         }
+        out.writeBoolean(source != null);
+        writeSource(out, source);
+    }
+
+    static void writeSource(final ObjectOutputStream out, StackTraceElement source) throws IOException {
+        boolean hasSource = source != null;
+        out.writeBoolean(hasSource);
+        if (hasSource) {
+            writeNullableString(out, source.getFileName());
+            writeNullableString(out, source.getClassName());
+            writeNullableString(out, source.getMethodName());
+            writeNullableInt(out, source.getLineNumber());
+        }
+    }
+
+    static StackTraceElement readSource(final ObjectInputStream in) throws IOException {
+        boolean hasSource = in.readBoolean();
+        StackTraceElement source = null;
+        if (hasSource) {
+            String fileName = readNullableString(in);
+            String className = readNullableString(in);
+            String methodName = readNullableString(in);
+            Integer lineNumber = readNullableInt(in);
+            source = new StackTraceElement(className, methodName, fileName, lineNumber);
+        }
+        return source;
+    }
+
+    static void writeNullableString(final ObjectOutputStream out, String s) throws IOException {
+        boolean hasValue = s != null;
+        out.writeBoolean(hasValue);
+        if (hasValue) {
+            out.writeUTF(s);
+        }
+    }
+
+    static String readNullableString(final ObjectInputStream in) throws IOException {
+        boolean hasValue = in.readBoolean();
+        String result = null;
+        if (hasValue) {
+            result = in.readUTF();
+        }
+        return result;
+    }
+
+    static void writeNullableInt(final ObjectOutputStream out, Integer i) throws IOException {
+        boolean hasValue = i != null;
+        out.writeBoolean(hasValue);
+        if (hasValue) {
+            out.writeInt(i);
+        }
+    }
+
+    static Integer readNullableInt(final ObjectInputStream in) throws IOException {
+        boolean hasvalue = in.readBoolean();
+        Integer result = null;
+        if (hasvalue) {
+            result = in.readInt();
+        }
+        return result;
     }
 
     private void readObject(final ObjectInputStream in) throws IOException {
@@ -175,6 +268,12 @@ private void readObject(final ObjectInputStream in) throws IOException {
         for (int i = 0; i < length; ++i) {
             serializedParameters[i] = in.readUTF();
         }
+        source = readSource(in);
+    }
+
+    @Override
+    public StackTraceElement getSource() {
+        return source;
     }
 
     /**
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/message/MessageFormatMessageFactory.java b/log4j-api/src/main/java/org/apache/logging/log4j/message/MessageFormatMessageFactory.java
index f75e68ba5a2..d900166ece3 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/message/MessageFormatMessageFactory.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/message/MessageFormatMessageFactory.java
@@ -49,86 +49,19 @@ public Message newMessage(final String message, final Object... params) {
     }
 
     /**
-     * @since 2.6.1
-     */
-    @Override
-    public Message newMessage(final String message, final Object p0) {
-        return new MessageFormatMessage(message, p0);
-    }
-
-    /**
-     * @since 2.6.1
-     */
-    @Override
-    public Message newMessage(final String message, final Object p0, final Object p1) {
-        return new MessageFormatMessage(message, p0, p1);
-    }
-
-    /**
-     * @since 2.6.1
-     */
-    @Override
-    public Message newMessage(final String message, final Object p0, final Object p1, final Object p2) {
-        return new MessageFormatMessage(message, p0, p1, p2);
-    }
-
-    /**
-     * @since 2.6.1
-     */
-    @Override
-    public Message newMessage(final String message, final Object p0, final Object p1, final Object p2, final Object p3) {
-        return new MessageFormatMessage(message, p0, p1, p2, p3);
-    }
-
-    /**
-     * @since 2.6.1
-     */
-    @Override
-    public Message newMessage(final String message, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4) {
-        return new MessageFormatMessage(message, p0, p1, p2, p3, p4);
-    }
-
-    /**
-     * @since 2.6.1
-     */
-    @Override
-    public Message newMessage(final String message, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5) {
-        return new MessageFormatMessage(message, p0, p1, p2, p3, p4, p5);
-    }
-
-    /**
-     * @since 2.6.1
-     */
-    @Override
-    public Message newMessage(final String message, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5,
-            final Object p6) {
-        return new MessageFormatMessage(message, p0, p1, p2, p3, p4, p5, p6);
-    }
-
-    /**
-     * @since 2.6.1
-     */
-    @Override
-    public Message newMessage(final String message, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5,
-            final Object p6, final Object p7) {
-        return new MessageFormatMessage(message, p0, p1, p2, p3, p4, p5, p6, p7);
-    }
-
-    /**
-     * @since 2.6.1
+     * Creates {@link org.apache.logging.log4j.message.StringFormattedMessage} instances, when the location
+     * of the log statement might be known at compile time.
+     *
+     * @param source the location of the log statement, or null
+     * @param message The message pattern.
+     * @param params Parameters to the message.
+     * @return The Message.
+     *
+     * @see org.apache.logging.log4j.message.MessageFactory#newMessage(String, Object...)
      */
     @Override
-    public Message newMessage(final String message, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5,
-            final Object p6, final Object p7, final Object p8) {
-        return new MessageFormatMessage(message, p0, p1, p2, p3, p4, p5, p6, p7, p8);
+    public Message newMessage(StackTraceElement source, String message, Object... params) {
+        return new MessageFormatMessage(source, message, params);
     }
 
-    /**
-     * @since 2.6.1
-     */
-    @Override
-    public Message newMessage(final String message, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5,
-            final Object p6, final Object p7, final Object p8, final Object p9) {
-        return new MessageFormatMessage(message, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9);
-    }
 }
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/message/ObjectArrayMessage.java b/log4j-api/src/main/java/org/apache/logging/log4j/message/ObjectArrayMessage.java
index dc11a5673b6..10c72b2b964 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/message/ObjectArrayMessage.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/message/ObjectArrayMessage.java
@@ -40,6 +40,7 @@ public final class ObjectArrayMessage implements Message {
 
     private transient Object[] array;
     private transient String arrayString;
+    private transient StackTraceElement source;
 
     /**
      * Creates the ObjectMessage.
@@ -48,7 +49,18 @@ public final class ObjectArrayMessage implements Message {
      *            The Object to format.
      */
     public ObjectArrayMessage(final Object... obj) {
+        this(null, obj);
+    }
+
+    /**
+     * Creates the ObjectMessage.
+     *
+     * @param obj
+     *            The Object to format.
+     */
+    public ObjectArrayMessage(final StackTraceElement source, final Object... obj) {
         this.array = obj == null ? EMPTY_OBJECT_ARRAY : obj;
+        this.source = source;
     }
 
     private boolean equalObjectsOrStrings(final Object[] left, final Object[] right) {
@@ -112,6 +124,11 @@ public Throwable getThrowable() {
         return null;
     }
 
+    @Override
+    public StackTraceElement getSource() {
+        return source;
+    }
+
     @Override
     public int hashCode() {
         return Arrays.hashCode(array);
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/message/ObjectMessage.java b/log4j-api/src/main/java/org/apache/logging/log4j/message/ObjectMessage.java
index ecd874ce630..31e05dec679 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/message/ObjectMessage.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/message/ObjectMessage.java
@@ -33,6 +33,7 @@ public class ObjectMessage implements Message, StringBuilderFormattable {
 
     private transient Object obj;
     private transient String objectString;
+    private transient StackTraceElement source;
 
     /**
      * Creates the ObjectMessage.
@@ -40,7 +41,17 @@ public class ObjectMessage implements Message, StringBuilderFormattable {
      * @param obj The Object to format.
      */
     public ObjectMessage(final Object obj) {
+        this(null, obj);
+    }
+
+    /**
+     * Creates the ObjectMessage.
+     *
+     * @param obj The Object to format.
+     */
+    public ObjectMessage(final StackTraceElement source, final Object obj) {
         this.obj = obj == null ? "null" : obj;
+        this.source = source;
     }
 
     /**
@@ -146,4 +157,9 @@ private void readObject(final ObjectInputStream in) throws IOException, ClassNot
     public Throwable getThrowable() {
         return obj instanceof Throwable ? (Throwable) obj : null;
     }
+
+    @Override
+    public StackTraceElement getSource() {
+        return source;
+    }
 }
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/message/ParameterizedMessage.java b/log4j-api/src/main/java/org/apache/logging/log4j/message/ParameterizedMessage.java
index c4a1bbfadb2..2dba5e1fe50 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/message/ParameterizedMessage.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/message/ParameterizedMessage.java
@@ -78,6 +78,7 @@ public class ParameterizedMessage implements Message, StringBuilderFormattable {
     private transient Throwable throwable;
     private int[] indices;
     private int usedCount;
+    private StackTraceElement source;
 
     /**
      * Creates a parameterized message.
@@ -89,9 +90,7 @@ public class ParameterizedMessage implements Message, StringBuilderFormattable {
      */
     @Deprecated
     public ParameterizedMessage(final String messagePattern, final String[] arguments, final Throwable throwable) {
-        this.argArray = arguments;
-        this.throwable = throwable;
-        init(messagePattern);
+        this(null, messagePattern, arguments, throwable);
     }
 
     /**
@@ -102,6 +101,18 @@ public ParameterizedMessage(final String messagePattern, final String[] argument
      * @param throwable A Throwable.
      */
     public ParameterizedMessage(final String messagePattern, final Object[] arguments, final Throwable throwable) {
+        this(null, messagePattern, arguments, throwable);
+    }
+
+    /**
+     * Creates a parameterized message.
+     * @param messagePattern The message "format" string. This will be a String containing "{}" placeholders
+     * where parameters should be substituted.
+     * @param arguments The arguments for substitution.
+     * @param throwable A Throwable.
+     */
+    public ParameterizedMessage(StackTraceElement source, final String messagePattern, final Object[] arguments, final Throwable throwable) {
+        this.source = source;
         this.argArray = arguments;
         this.throwable = throwable;
         init(messagePattern);
@@ -119,8 +130,24 @@ public ParameterizedMessage(final String messagePattern, final Object[] argument
      * @param arguments      the argument array to be converted.
      */
     public ParameterizedMessage(final String messagePattern, final Object... arguments) {
+        this((StackTraceElement) null, messagePattern, arguments);
+    }
+
+    /**
+     * Constructs a ParameterizedMessage which contains the arguments converted to String as well as an optional
+     * Throwable.
+     *
+     * 

If the last argument is a Throwable and is NOT used up by a placeholder in the message pattern it is returned + * in {@link #getThrowable()} and won't be contained in the created String[]. + * If it is used up {@link #getThrowable()} will return null even if the last argument was a Throwable!

+ * + * @param messagePattern the message pattern that to be checked for placeholders. + * @param arguments the argument array to be converted. + */ + public ParameterizedMessage(final StackTraceElement source, final String messagePattern, final Object... arguments) { this.argArray = arguments; init(messagePattern); + this.source = source; } /** @@ -129,7 +156,16 @@ public ParameterizedMessage(final String messagePattern, final Object... argumen * @param arg The parameter. */ public ParameterizedMessage(final String messagePattern, final Object arg) { - this(messagePattern, new Object[]{arg}); + this((StackTraceElement) null, messagePattern, new Object[]{arg}); + } + + /** + * Constructor with a pattern and a single parameter. + * @param messagePattern The message pattern. + * @param arg The parameter. + */ + public ParameterizedMessage(final StackTraceElement source, final String messagePattern, final Object arg) { + this(source, messagePattern, new Object[]{arg}); } /** @@ -139,7 +175,17 @@ public ParameterizedMessage(final String messagePattern, final Object arg) { * @param arg1 The second parameter. */ public ParameterizedMessage(final String messagePattern, final Object arg0, final Object arg1) { - this(messagePattern, new Object[]{arg0, arg1}); + this((StackTraceElement) null, messagePattern, new Object[]{arg0, arg1}); + } + + /** + * Constructor with a pattern and two parameters. + * @param messagePattern The message pattern. + * @param arg0 The first parameter. + * @param arg1 The second parameter. + */ + public ParameterizedMessage(final StackTraceElement source, final String messagePattern, final Object arg0, final Object arg1) { + this(source, messagePattern, new Object[]{arg0, arg1}); } private void init(final String messagePattern) { @@ -192,6 +238,11 @@ public Throwable getThrowable() { return throwable; } + @Override + public StackTraceElement getSource() { + return source; + } + /** * Returns the formatted message. * @return the formatted message. @@ -328,7 +379,7 @@ public static String identityToString(final Object obj) { @Override public String toString() { - return "ParameterizedMessage[messagePattern=" + messagePattern + ", stringArgs=" + + return "ParameterizedMessage[source=" + source + ", messagePattern=" + messagePattern + ", stringArgs=" + Arrays.toString(argArray) + ", throwable=" + throwable + ']'; } } diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/message/ParameterizedMessageFactory.java b/log4j-api/src/main/java/org/apache/logging/log4j/message/ParameterizedMessageFactory.java index 5878cac9ad2..3826522c4ec 100644 --- a/log4j-api/src/main/java/org/apache/logging/log4j/message/ParameterizedMessageFactory.java +++ b/log4j-api/src/main/java/org/apache/logging/log4j/message/ParameterizedMessageFactory.java @@ -49,6 +49,106 @@ public ParameterizedMessageFactory() { super(); } + /** + * Creates {@link ParameterizedMessage} instances, when the location + * of the log statement might be known at compile time. + * + * @param source the location of the log statement, or null + * @param message The message pattern. + * @param params The message parameters. + * @return The Message. + * + * @see MessageFactory#newMessage(String, Object...) + */ + @Override + public Message newMessage(final StackTraceElement source, final String message, final Object... params) { + return new ParameterizedMessage(source, message, params); + } + + /** + * @since + */ + @Override + public Message newMessage(final StackTraceElement source, final String message, final Object p0) { + return new ParameterizedMessage(source, message, p0); + } + + /** + * @since + */ + @Override + public Message newMessage(final StackTraceElement source, final String message, final Object p0, final Object p1) { + return new ParameterizedMessage(source, message, p0, p1); + } + + /** + * @since + */ + @Override + public Message newMessage(final StackTraceElement source, final String message, final Object p0, final Object p1, final Object p2) { + return new ParameterizedMessage(source, message, p0, p1, p2); + } + + /** + * @since + */ + @Override + public Message newMessage(final StackTraceElement source, final String message, final Object p0, final Object p1, final Object p2, final Object p3) { + return new ParameterizedMessage(source, message, p0, p1, p2, p3); + } + + /** + * @since + */ + @Override + public Message newMessage(final StackTraceElement source, final String message, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4) { + return new ParameterizedMessage(source, message, p0, p1, p2, p3, p4); + } + + /** + * @since + */ + @Override + public Message newMessage(final StackTraceElement source, final String message, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5) { + return new ParameterizedMessage(source, message, p0, p1, p2, p3, p4, p5); + } + + /** + * @since + */ + @Override + public Message newMessage(final StackTraceElement source, final String message, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5, + final Object p6) { + return new ParameterizedMessage(source, message, p0, p1, p2, p3, p4, p5, p6); + } + + /** + * @since + */ + @Override + public Message newMessage(final StackTraceElement source, final String message, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5, + final Object p6, final Object p7) { + return new ParameterizedMessage(source, message, p0, p1, p2, p3, p4, p5, p6, p7); + } + + /** + * @since + */ + @Override + public Message newMessage(final StackTraceElement source, final String message, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5, + final Object p6, final Object p7, final Object p8) { + return new ParameterizedMessage(source, message, p0, p1, p2, p3, p4, p5, p6, p7, p8); + } + + /** + * @since + */ + @Override + public Message newMessage(final StackTraceElement source, final String message, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5, + final Object p6, final Object p7, final Object p8, final Object p9) { + return new ParameterizedMessage(source, message, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9); + } + /** * Creates {@link ParameterizedMessage} instances. * @@ -60,7 +160,7 @@ public ParameterizedMessageFactory() { */ @Override public Message newMessage(final String message, final Object... params) { - return new ParameterizedMessage(message, params); + return newMessage((StackTraceElement) null, message, params); } /** @@ -68,7 +168,7 @@ public Message newMessage(final String message, final Object... params) { */ @Override public Message newMessage(final String message, final Object p0) { - return new ParameterizedMessage(message, p0); + return newMessage((StackTraceElement) null, message, p0); } /** @@ -76,7 +176,7 @@ public Message newMessage(final String message, final Object p0) { */ @Override public Message newMessage(final String message, final Object p0, final Object p1) { - return new ParameterizedMessage(message, p0, p1); + return newMessage((StackTraceElement) null, message, p0, p1); } /** @@ -84,7 +184,7 @@ public Message newMessage(final String message, final Object p0, final Object p1 */ @Override public Message newMessage(final String message, final Object p0, final Object p1, final Object p2) { - return new ParameterizedMessage(message, p0, p1, p2); + return newMessage((StackTraceElement) null, message, p0, p1, p2); } /** @@ -92,7 +192,7 @@ public Message newMessage(final String message, final Object p0, final Object p1 */ @Override public Message newMessage(final String message, final Object p0, final Object p1, final Object p2, final Object p3) { - return new ParameterizedMessage(message, p0, p1, p2, p3); + return newMessage((StackTraceElement) null, message, p0, p1, p2, p3); } /** @@ -100,7 +200,7 @@ public Message newMessage(final String message, final Object p0, final Object p1 */ @Override public Message newMessage(final String message, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4) { - return new ParameterizedMessage(message, p0, p1, p2, p3, p4); + return newMessage((StackTraceElement) null, message, p0, p1, p2, p3, p4); } /** @@ -108,7 +208,7 @@ public Message newMessage(final String message, final Object p0, final Object p1 */ @Override public Message newMessage(final String message, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5) { - return new ParameterizedMessage(message, p0, p1, p2, p3, p4, p5); + return newMessage((StackTraceElement) null, message, p0, p1, p2, p3, p4, p5); } /** @@ -116,8 +216,8 @@ public Message newMessage(final String message, final Object p0, final Object p1 */ @Override public Message newMessage(final String message, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5, - final Object p6) { - return new ParameterizedMessage(message, p0, p1, p2, p3, p4, p5, p6); + final Object p6) { + return newMessage((StackTraceElement) null, message, p0, p1, p2, p3, p4, p5, p6); } /** @@ -125,8 +225,8 @@ public Message newMessage(final String message, final Object p0, final Object p1 */ @Override public Message newMessage(final String message, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5, - final Object p6, final Object p7) { - return new ParameterizedMessage(message, p0, p1, p2, p3, p4, p5, p6, p7); + final Object p6, final Object p7) { + return newMessage((StackTraceElement) null, message, p0, p1, p2, p3, p4, p5, p6, p7); } /** @@ -134,8 +234,8 @@ public Message newMessage(final String message, final Object p0, final Object p1 */ @Override public Message newMessage(final String message, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5, - final Object p6, final Object p7, final Object p8) { - return new ParameterizedMessage(message, p0, p1, p2, p3, p4, p5, p6, p7, p8); + final Object p6, final Object p7, final Object p8) { + return newMessage((StackTraceElement) null, message, p0, p1, p2, p3, p4, p5, p6, p7, p8); } /** @@ -143,7 +243,7 @@ public Message newMessage(final String message, final Object p0, final Object p1 */ @Override public Message newMessage(final String message, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5, - final Object p6, final Object p7, final Object p8, final Object p9) { - return new ParameterizedMessage(message, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9); + final Object p6, final Object p7, final Object p8, final Object p9) { + return newMessage((StackTraceElement) null, message, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9); } } diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/message/ParameterizedNoReferenceMessageFactory.java b/log4j-api/src/main/java/org/apache/logging/log4j/message/ParameterizedNoReferenceMessageFactory.java index fdf397bc503..3283e9715d7 100644 --- a/log4j-api/src/main/java/org/apache/logging/log4j/message/ParameterizedNoReferenceMessageFactory.java +++ b/log4j-api/src/main/java/org/apache/logging/log4j/message/ParameterizedNoReferenceMessageFactory.java @@ -49,10 +49,16 @@ static class StatusMessage implements Message { private static final long serialVersionUID = 4199272162767841280L; private final String formattedMessage; private final Throwable throwable; + private final StackTraceElement source; public StatusMessage(final String formattedMessage, final Throwable throwable) { + this(null, formattedMessage, throwable); + } + + public StatusMessage(StackTraceElement source,final String formattedMessage, final Throwable throwable) { this.formattedMessage = formattedMessage; this.throwable = throwable; + this.source = source; } @Override @@ -74,6 +80,11 @@ public Object[] getParameters() { public Throwable getThrowable() { return throwable; } + + @Override + public StackTraceElement getSource() { + return source; + } } /** @@ -99,10 +110,26 @@ public ParameterizedNoReferenceMessageFactory() { */ @Override public Message newMessage(final String message, final Object... params) { + return newMessage((StackTraceElement) null, message, params); + } + + /** + * Creates {@link SimpleMessage} instances containing the formatted parameterized message string, when the location + * of the log statement might be known at compile time. + * + * @param source the location of the log statement, or null + * @param message The message pattern. + * @param params The message parameters. + * @return The Message. + * + * @see MessageFactory#newMessage(StackTraceElement, String, Object...) + */ + @Override + public Message newMessage(StackTraceElement source, String message, Object... params) { if (params == null) { - return new SimpleMessage(message); + return new SimpleMessage(source, message); } - final ParameterizedMessage msg = new ParameterizedMessage(message, params); - return new StatusMessage(msg.getFormattedMessage(), msg.getThrowable()); + final ParameterizedMessage msg = new ParameterizedMessage(source, message, params); + return new StatusMessage(source, msg.getFormattedMessage(), msg.getThrowable()); } } diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/message/ReusableMessage.java b/log4j-api/src/main/java/org/apache/logging/log4j/message/ReusableMessage.java index 82e40acb100..c44736e5855 100644 --- a/log4j-api/src/main/java/org/apache/logging/log4j/message/ReusableMessage.java +++ b/log4j-api/src/main/java/org/apache/logging/log4j/message/ReusableMessage.java @@ -52,6 +52,8 @@ public interface ReusableMessage extends Message, StringBuilderFormattable { */ Object[] swapParameters(Object[] emptyReplacement); + StackTraceElement swapSource(StackTraceElement source); + /** * Returns the number of parameters that was used to initialize this reusable message for the current content. *

diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/message/ReusableMessageFactory.java b/log4j-api/src/main/java/org/apache/logging/log4j/message/ReusableMessageFactory.java index d75ca01f9a0..abb76f1d293 100644 --- a/log4j-api/src/main/java/org/apache/logging/log4j/message/ReusableMessageFactory.java +++ b/log4j-api/src/main/java/org/apache/logging/log4j/message/ReusableMessageFactory.java @@ -90,12 +90,90 @@ public static void release(final Message message) { // LOG4J2-1583 } @Override - public Message newMessage(final CharSequence charSequence) { + public Message newMessage(final StackTraceElement source, final CharSequence charSequence) { final ReusableSimpleMessage result = getSimple(); result.set(charSequence); + result.swapSource(source); return result; } + @Override + public Message newMessage(CharSequence charSequence) { + return newMessage((StackTraceElement) null, charSequence); + } + + /** + * Creates {@link ReusableParameterizedMessage} instances, when the location + * of the log statement might be known at compile time. + * + * @param message The message pattern. + * @param params The message parameters. + * @return The Message. + * + * @see MessageFactory#newMessage(String, Object...) + */ + @Override + public Message newMessage(final StackTraceElement source, final String message, final Object... params) { + return getParameterized().set(source, message, params); + } + + @Override + public Message newMessage(final StackTraceElement source, final String message, final Object p0) { + return getParameterized().set(source, message, p0); + } + + @Override + public Message newMessage(final StackTraceElement source, final String message, final Object p0, final Object p1) { + return getParameterized().set(source, message, p0, p1); + } + + @Override + public Message newMessage(final StackTraceElement source, final String message, final Object p0, final Object p1, final Object p2) { + return getParameterized().set(source, message, p0, p1, p2); + } + + @Override + public Message newMessage(final StackTraceElement source, final String message, final Object p0, final Object p1, final Object p2, + final Object p3) { + return getParameterized().set(source, message, p0, p1, p2, p3); + } + + @Override + public Message newMessage(final StackTraceElement source, final String message, final Object p0, final Object p1, final Object p2, final Object p3, + final Object p4) { + return getParameterized().set(source, message, p0, p1, p2, p3, p4); + } + + @Override + public Message newMessage(final StackTraceElement source, final String message, final Object p0, final Object p1, final Object p2, final Object p3, + final Object p4, final Object p5) { + return getParameterized().set(source, message, p0, p1, p2, p3, p4, p5); + } + + @Override + public Message newMessage(final StackTraceElement source, final String message, final Object p0, final Object p1, final Object p2, final Object p3, + final Object p4, final Object p5, final Object p6) { + return getParameterized().set(source, message, p0, p1, p2, p3, p4, p5, p6); + } + + @Override + public Message newMessage(final StackTraceElement source, final String message, final Object p0, final Object p1, final Object p2, final Object p3, + final Object p4, final Object p5, final Object p6, final Object p7) { + return getParameterized().set(source, message, p0, p1, p2, p3, p4, p5, p6, p7); + } + + @Override + public Message newMessage(final StackTraceElement source, final String message, final Object p0, final Object p1, final Object p2, final Object p3, + final Object p4, final Object p5, final Object p6, final Object p7, final Object p8) { + return getParameterized().set(source, message, p0, p1, p2, p3, p4, p5, p6, p7, p8); + } + + @Override + public Message newMessage(final StackTraceElement source, final String message, final Object p0, final Object p1, final Object p2, final Object p3, + final Object p4, final Object p5, final Object p6, final Object p7, final Object p8, final Object p9) { + return getParameterized().set(source, message, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9); + } + /** * Creates {@link ReusableParameterizedMessage} instances. * @@ -107,64 +185,64 @@ public Message newMessage(final CharSequence charSequence) { */ @Override public Message newMessage(final String message, final Object... params) { - return getParameterized().set(message, params); + return newMessage((StackTraceElement) null, message, params); } @Override public Message newMessage(final String message, final Object p0) { - return getParameterized().set(message, p0); + return newMessage((StackTraceElement) null, message, p0); } @Override public Message newMessage(final String message, final Object p0, final Object p1) { - return getParameterized().set(message, p0, p1); + return newMessage((StackTraceElement) null, message, p0, p1); } @Override public Message newMessage(final String message, final Object p0, final Object p1, final Object p2) { - return getParameterized().set(message, p0, p1, p2); + return newMessage((StackTraceElement) null, message, p0, p1, p2); } @Override public Message newMessage(final String message, final Object p0, final Object p1, final Object p2, - final Object p3) { - return getParameterized().set(message, p0, p1, p2, p3); + final Object p3) { + return newMessage((StackTraceElement) null, message, p0, p1, p2, p3); } @Override public Message newMessage(final String message, final Object p0, final Object p1, final Object p2, final Object p3, - final Object p4) { - return getParameterized().set(message, p0, p1, p2, p3, p4); + final Object p4) { + return newMessage((StackTraceElement) null, message, p0, p1, p2, p3, p4); } @Override public Message newMessage(final String message, final Object p0, final Object p1, final Object p2, final Object p3, - final Object p4, final Object p5) { - return getParameterized().set(message, p0, p1, p2, p3, p4, p5); + final Object p4, final Object p5) { + return newMessage((StackTraceElement) null, message, p0, p1, p2, p3, p4, p5); } @Override public Message newMessage(final String message, final Object p0, final Object p1, final Object p2, final Object p3, - final Object p4, final Object p5, final Object p6) { - return getParameterized().set(message, p0, p1, p2, p3, p4, p5, p6); + final Object p4, final Object p5, final Object p6) { + return newMessage((StackTraceElement) null, message, p0, p1, p2, p3, p4, p5, p6); } @Override public Message newMessage(final String message, final Object p0, final Object p1, final Object p2, final Object p3, - final Object p4, final Object p5, final Object p6, final Object p7) { - return getParameterized().set(message, p0, p1, p2, p3, p4, p5, p6, p7); + final Object p4, final Object p5, final Object p6, final Object p7) { + return newMessage((StackTraceElement) null, message, p0, p1, p2, p3, p4, p5, p6, p7); } @Override public Message newMessage(final String message, final Object p0, final Object p1, final Object p2, final Object p3, - final Object p4, final Object p5, final Object p6, final Object p7, final Object p8) { - return getParameterized().set(message, p0, p1, p2, p3, p4, p5, p6, p7, p8); + final Object p4, final Object p5, final Object p6, final Object p7, final Object p8) { + return newMessage((StackTraceElement) null, message, p0, p1, p2, p3, p4, p5, p6, p7, p8); } @Override public Message newMessage(final String message, final Object p0, final Object p1, final Object p2, final Object p3, - final Object p4, final Object p5, final Object p6, final Object p7, final Object p8, final Object p9) { - return getParameterized().set(message, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9); + final Object p4, final Object p5, final Object p6, final Object p7, final Object p8, final Object p9) { + return newMessage((StackTraceElement) null, message, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9); } /** @@ -176,12 +254,17 @@ public Message newMessage(final String message, final Object p0, final Object p1 * @see MessageFactory#newMessage(String) */ @Override - public Message newMessage(final String message) { + public Message newMessage(final StackTraceElement source, final String message) { final ReusableSimpleMessage result = getSimple(); result.set(message); + result.swapSource(source); return result; } + @Override + public Message newMessage(String message) { + return newMessage((StackTraceElement) null, message); + } /** * Creates {@link ReusableObjectMessage} instances. @@ -192,9 +275,15 @@ public Message newMessage(final String message) { * @see MessageFactory#newMessage(Object) */ @Override - public Message newMessage(final Object message) { + public Message newMessage(final StackTraceElement source, final Object message) { final ReusableObjectMessage result = getObject(); result.set(message); + result.swapSource(source); return result; } + + @Override + public Message newMessage(Object message) { + return newMessage((StackTraceElement) null, message); + } } diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/message/ReusableObjectMessage.java b/log4j-api/src/main/java/org/apache/logging/log4j/message/ReusableObjectMessage.java index c4ffa22c105..a3f7d78c641 100644 --- a/log4j-api/src/main/java/org/apache/logging/log4j/message/ReusableObjectMessage.java +++ b/log4j-api/src/main/java/org/apache/logging/log4j/message/ReusableObjectMessage.java @@ -28,6 +28,8 @@ public class ReusableObjectMessage implements ReusableMessage { private static final long serialVersionUID = 6922476812535519960L; private transient Object obj; + private transient String objectString; + private StackTraceElement source; public void set(final Object object) { this.obj = object; @@ -103,6 +105,18 @@ public Object[] swapParameters(final Object[] emptyReplacement) { return emptyReplacement; } + @Override + public StackTraceElement swapSource(StackTraceElement source) { + StackTraceElement originalSource = this.source; + this.source = source; + return originalSource; + } + + @Override + public StackTraceElement getSource() { + return source; + } + /** * This message does not have any parameters so this method always returns zero. * @return 0 (zero) diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/message/ReusableParameterizedMessage.java b/log4j-api/src/main/java/org/apache/logging/log4j/message/ReusableParameterizedMessage.java index daf299482c8..5039bbd6f19 100644 --- a/log4j-api/src/main/java/org/apache/logging/log4j/message/ReusableParameterizedMessage.java +++ b/log4j-api/src/main/java/org/apache/logging/log4j/message/ReusableParameterizedMessage.java @@ -44,6 +44,7 @@ public class ReusableParameterizedMessage implements ReusableMessage { private transient Object[] varargs; private transient Object[] params = new Object[MAX_PARMS]; private transient Throwable throwable; + private transient StackTraceElement source; transient boolean reserved = false; // LOG4J2-1583 prevent scrambled logs with nested logging calls /** @@ -109,8 +110,9 @@ public Message memento() { return new ParameterizedMessage(messagePattern, getTrimmedParams()); } - private void init(final String messagePattern, final int argCount, final Object[] paramArray) { + private void init(final StackTraceElement source, final String messagePattern, final int argCount, final Object[] paramArray) { this.varargs = null; + this.source = source; this.messagePattern = messagePattern; this.argCount = argCount; final int placeholderCount = count(messagePattern, indices); @@ -135,64 +137,64 @@ private void initThrowable(final Object[] params, final int argCount, final int } } - ReusableParameterizedMessage set(final String messagePattern, final Object... arguments) { - init(messagePattern, arguments == null ? 0 : arguments.length, arguments); + ReusableParameterizedMessage set(final StackTraceElement source, final String messagePattern, final Object... arguments) { + init(source, messagePattern, arguments == null ? 0 : arguments.length, arguments); varargs = arguments; return this; } - ReusableParameterizedMessage set(final String messagePattern, final Object p0) { + ReusableParameterizedMessage set(final StackTraceElement source, final String messagePattern, final Object p0) { params[0] = p0; - init(messagePattern, 1, params); + init(source, messagePattern, 1, params); return this; } - ReusableParameterizedMessage set(final String messagePattern, final Object p0, final Object p1) { + ReusableParameterizedMessage set(final StackTraceElement source, final String messagePattern, final Object p0, final Object p1) { params[0] = p0; params[1] = p1; - init(messagePattern, 2, params); + init(source, messagePattern, 2, params); return this; } - ReusableParameterizedMessage set(final String messagePattern, final Object p0, final Object p1, final Object p2) { + ReusableParameterizedMessage set(final StackTraceElement source, final String messagePattern, final Object p0, final Object p1, final Object p2) { params[0] = p0; params[1] = p1; params[2] = p2; - init(messagePattern, 3, params); + init(source, messagePattern, 3, params); return this; } - ReusableParameterizedMessage set(final String messagePattern, final Object p0, final Object p1, final Object p2, final Object p3) { + ReusableParameterizedMessage set(final StackTraceElement source, final String messagePattern, final Object p0, final Object p1, final Object p2, final Object p3) { params[0] = p0; params[1] = p1; params[2] = p2; params[3] = p3; - init(messagePattern, 4, params); + init(source, messagePattern, 4, params); return this; } - ReusableParameterizedMessage set(final String messagePattern, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4) { + ReusableParameterizedMessage set(final StackTraceElement source, final String messagePattern, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4) { params[0] = p0; params[1] = p1; params[2] = p2; params[3] = p3; params[4] = p4; - init(messagePattern, 5, params); + init(source, messagePattern, 5, params); return this; } - ReusableParameterizedMessage set(final String messagePattern, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5) { + ReusableParameterizedMessage set(final StackTraceElement source, final String messagePattern, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5) { params[0] = p0; params[1] = p1; params[2] = p2; params[3] = p3; params[4] = p4; params[5] = p5; - init(messagePattern, 6, params); + init(source, messagePattern, 6, params); return this; } - ReusableParameterizedMessage set(final String messagePattern, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5, + ReusableParameterizedMessage set(final StackTraceElement source, final String messagePattern, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5, final Object p6) { params[0] = p0; params[1] = p1; @@ -201,11 +203,11 @@ ReusableParameterizedMessage set(final String messagePattern, final Object p0, f params[4] = p4; params[5] = p5; params[6] = p6; - init(messagePattern, 7, params); + init(source, messagePattern, 7, params); return this; } - ReusableParameterizedMessage set(final String messagePattern, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5, + ReusableParameterizedMessage set(final StackTraceElement source, final String messagePattern, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5, final Object p6, final Object p7) { params[0] = p0; params[1] = p1; @@ -215,11 +217,11 @@ ReusableParameterizedMessage set(final String messagePattern, final Object p0, f params[5] = p5; params[6] = p6; params[7] = p7; - init(messagePattern, 8, params); + init(source, messagePattern, 8, params); return this; } - ReusableParameterizedMessage set(final String messagePattern, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5, + ReusableParameterizedMessage set(final StackTraceElement source, final String messagePattern, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5, final Object p6, final Object p7, final Object p8) { params[0] = p0; params[1] = p1; @@ -230,11 +232,11 @@ ReusableParameterizedMessage set(final String messagePattern, final Object p0, f params[6] = p6; params[7] = p7; params[8] = p8; - init(messagePattern, 9, params); + init(source, messagePattern, 9, params); return this; } - ReusableParameterizedMessage set(final String messagePattern, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5, + ReusableParameterizedMessage set(final StackTraceElement source, final String messagePattern, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5, final Object p6, final Object p7, final Object p8, final Object p9) { params[0] = p0; params[1] = p1; @@ -246,10 +248,70 @@ ReusableParameterizedMessage set(final String messagePattern, final Object p0, f params[7] = p7; params[8] = p8; params[9] = p9; - init(messagePattern, 10, params); + init(source, messagePattern, 10, params); return this; } + ReusableParameterizedMessage set(final String messagePattern, final Object... arguments) { + return set((StackTraceElement) null, messagePattern, arguments); + } + + ReusableParameterizedMessage set(final String messagePattern, final Object p0) { + return set((StackTraceElement) null, messagePattern, p0); + } + + ReusableParameterizedMessage set(final String messagePattern, final Object p0, final Object p1) { + return set((StackTraceElement) null, messagePattern, p0, p1); + } + + ReusableParameterizedMessage set(final String messagePattern, final Object p0, final Object p1, final Object p2) { + return set((StackTraceElement) null, messagePattern, p0, p1, p2); + } + + ReusableParameterizedMessage set(final String messagePattern, final Object p0, final Object p1, final Object p2, final Object p3) { + return set((StackTraceElement) null, messagePattern, p0, p1, p2, p3); + } + + ReusableParameterizedMessage set(final String messagePattern, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4) { + return set((StackTraceElement) null, messagePattern, p0, p1, p2, p3, p4); + } + + ReusableParameterizedMessage set(final String messagePattern, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5) { + return set((StackTraceElement) null, messagePattern, p0, p1, p2, p3, p4, p5); + } + + ReusableParameterizedMessage set(final String messagePattern, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5, + final Object p6) { + return set((StackTraceElement) null, messagePattern, p0, p1, p2, p3, p4, p5, p6); + } + + ReusableParameterizedMessage set(final String messagePattern, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5, + final Object p6, final Object p7) { + return set((StackTraceElement) null, messagePattern, p0, p1, p2, p3, p4, p5, p6, p7); + } + + ReusableParameterizedMessage set(final String messagePattern, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5, + final Object p6, final Object p7, final Object p8) { + return set((StackTraceElement) null, messagePattern, p0, p1, p2, p3, p4, p5, p6, p7, p8); + } + + ReusableParameterizedMessage set(final String messagePattern, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5, + final Object p6, final Object p7, final Object p8, final Object p9) { + return set((StackTraceElement) null, messagePattern, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9); + } + + @Override + public StackTraceElement getSource() { + return source; + } + + @Override + public StackTraceElement swapSource(StackTraceElement source) { + StackTraceElement originalSource = this.source; + this.source = source; + return originalSource; + } + /** * Returns the message pattern. * @return the message pattern. diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/message/ReusableSimpleMessage.java b/log4j-api/src/main/java/org/apache/logging/log4j/message/ReusableSimpleMessage.java index 905b694d30e..269b0f19152 100644 --- a/log4j-api/src/main/java/org/apache/logging/log4j/message/ReusableSimpleMessage.java +++ b/log4j-api/src/main/java/org/apache/logging/log4j/message/ReusableSimpleMessage.java @@ -27,6 +27,7 @@ public class ReusableSimpleMessage implements ReusableMessage, CharSequence { private static final long serialVersionUID = -9199974506498249809L; private static Object[] EMPTY_PARAMS = new Object[0]; private CharSequence charSequence; + private StackTraceElement source; public void set(final String message) { this.charSequence = message; @@ -61,6 +62,18 @@ public void formatTo(final StringBuilder buffer) { buffer.append(charSequence); } + @Override + public StackTraceElement getSource() { + return source; + } + + @Override + public StackTraceElement swapSource(StackTraceElement source) { + StackTraceElement originalSource = this.source; + this.source = source; + return originalSource; + } + /** * This message does not have any parameters, so this method returns the specified array. * @param emptyReplacement the parameter array to return diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/message/SimpleMessage.java b/log4j-api/src/main/java/org/apache/logging/log4j/message/SimpleMessage.java index 8a2fc7219ea..edc933a6194 100644 --- a/log4j-api/src/main/java/org/apache/logging/log4j/message/SimpleMessage.java +++ b/log4j-api/src/main/java/org/apache/logging/log4j/message/SimpleMessage.java @@ -29,6 +29,7 @@ public class SimpleMessage implements Message, StringBuilderFormattable, CharSeq private String message; private transient CharSequence charSequence; + private StackTraceElement source; /** * Basic constructor. @@ -42,8 +43,20 @@ public SimpleMessage() { * @param message The String message. */ public SimpleMessage(final String message) { + this(null, message); + } + + /** + * Constructor that includes the message, when the location + * of the log statement might be known at compile time. + * + * @param source the location of the log statement, or null + * @param message The String message. + */ + public SimpleMessage(final StackTraceElement source, final String message) { this.message = message; this.charSequence = message; + this.source = source; } /** @@ -51,8 +64,21 @@ public SimpleMessage(final String message) { * @param charSequence The CharSequence message. */ public SimpleMessage(final CharSequence charSequence) { + // this.message = String.valueOf(charSequence); // postponed until getFormattedMessage + this(null, charSequence); + } + + /** + * Constructor that includes the message, when the location + * of the log statement might be known at compile time. + * + * @param source the location of the log statement, or null + * @param charSequence The CharSequence message. + */ + public SimpleMessage(final StackTraceElement source, final CharSequence charSequence) { // this.message = String.valueOf(charSequence); // postponed until getFormattedMessage this.charSequence = charSequence; + this.source = source; } /** @@ -149,4 +175,9 @@ private void readObject(final ObjectInputStream in) throws IOException, ClassNot in.defaultReadObject(); charSequence = message; } + + @Override + public StackTraceElement getSource() { + return source; + } } diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/message/SimpleMessageFactory.java b/log4j-api/src/main/java/org/apache/logging/log4j/message/SimpleMessageFactory.java index e85b8c0f219..28747416bba 100644 --- a/log4j-api/src/main/java/org/apache/logging/log4j/message/SimpleMessageFactory.java +++ b/log4j-api/src/main/java/org/apache/logging/log4j/message/SimpleMessageFactory.java @@ -144,4 +144,12 @@ public Message newMessage(final String message, final Object p0, final Object p1 final Object p6, final Object p7, final Object p8, final Object p9) { return new SimpleMessage(message); } + + /** + * @since + */ + @Override + public Message newMessage(StackTraceElement source, String message, Object... params) { + return new SimpleMessage(source, message); + } } diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/message/StringFormattedMessage.java b/log4j-api/src/main/java/org/apache/logging/log4j/message/StringFormattedMessage.java index a99e2efae9b..28fc7e3b923 100644 --- a/log4j-api/src/main/java/org/apache/logging/log4j/message/StringFormattedMessage.java +++ b/log4j-api/src/main/java/org/apache/logging/log4j/message/StringFormattedMessage.java @@ -22,6 +22,7 @@ import java.util.Arrays; import java.util.IllegalFormatException; import java.util.Locale; +import java.util.Objects; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.status.StatusLogger; @@ -49,33 +50,62 @@ public class StringFormattedMessage implements Message { private transient String formattedMessage; private transient Throwable throwable; private final Locale locale; + private StackTraceElement source; /** - * Constructs a message. - * + * Constructs a message, when the location + * of the log statement might be known at compile time. + * + * @param source the location of the log statement, or null * @param locale the locale for this message format * @param messagePattern the pattern for this message format * @param arguments The objects to format * @since 2.6 */ - public StringFormattedMessage(final Locale locale, final String messagePattern, final Object... arguments) { + public StringFormattedMessage(final StackTraceElement source, final Locale locale, final String messagePattern, final Object... arguments) { this.locale = locale; this.messagePattern = messagePattern; this.argArray = arguments; if (arguments != null && arguments.length > 0 && arguments[arguments.length - 1] instanceof Throwable) { this.throwable = (Throwable) arguments[arguments.length - 1]; } + this.source = source; } /** * Constructs a message. - * + * + * @param locale the locale for this message format + * @param messagePattern the pattern for this message format + * @param arguments The objects to format + * @since 2.6 + */ + public StringFormattedMessage(final Locale locale, final String messagePattern, final Object... arguments) { + this(null, locale, messagePattern, arguments); + } + + /** + * Constructs a message, when the location + * of the log statement might be known at compile time. + * + * @param source the location of the log statement, or null + * @param messagePattern the pattern for this message format + * @param arguments The objects to format + * @since 2.6 + */ + public StringFormattedMessage(final StackTraceElement source, final String messagePattern, final Object... arguments) { + this(source, Locale.getDefault(Locale.Category.FORMAT), messagePattern, arguments); + } + + /** + * Constructs a message. + * * @param messagePattern the pattern for this message format * @param arguments The objects to format * @since 2.6 */ public StringFormattedMessage(final String messagePattern, final Object... arguments) { - this(Locale.getDefault(Locale.Category.FORMAT), messagePattern, arguments); + this(null, Locale.getDefault(Locale.Category.FORMAT), messagePattern, arguments); } /** @@ -111,6 +141,11 @@ public Object[] getParameters() { return stringArgs; } + @Override + public StackTraceElement getSource() { + return source; + } + protected String formatMessage(final String msgPattern, final Object... args) { try { return String.format(locale, msgPattern, args); @@ -131,7 +166,7 @@ public boolean equals(final Object o) { final StringFormattedMessage that = (StringFormattedMessage) o; - if (messagePattern != null ? !messagePattern.equals(that.messagePattern) : that.messagePattern != null) { + if (!Objects.equals(messagePattern, that.messagePattern) || !Objects.equals(source, that.source)) { return false; } @@ -140,8 +175,9 @@ public boolean equals(final Object o) { @Override public int hashCode() { - int result = messagePattern != null ? messagePattern.hashCode() : 0; + int result = Objects.hashCode(messagePattern); result = HASHVAL * result + (stringArgs != null ? Arrays.hashCode(stringArgs) : 0); + result = HASHVAL * result + Objects.hashCode(source); return result; } diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/message/StringFormatterMessageFactory.java b/log4j-api/src/main/java/org/apache/logging/log4j/message/StringFormatterMessageFactory.java index b6554d54f4e..6b6b2e5d892 100644 --- a/log4j-api/src/main/java/org/apache/logging/log4j/message/StringFormatterMessageFactory.java +++ b/log4j-api/src/main/java/org/apache/logging/log4j/message/StringFormatterMessageFactory.java @@ -145,4 +145,9 @@ public Message newMessage(final String message, final Object p0, final Object p1 final Object p6, final Object p7, final Object p8, final Object p9) { return new StringFormattedMessage(message, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9); } + + @Override + public Message newMessage(StackTraceElement source, String message, Object... params) { + return new StringFormattedMessage(source, message, params); + } } diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/message/StringMapMessage.java b/log4j-api/src/main/java/org/apache/logging/log4j/message/StringMapMessage.java index 9d05ce05b3c..77b02f2b2b3 100644 --- a/log4j-api/src/main/java/org/apache/logging/log4j/message/StringMapMessage.java +++ b/log4j-api/src/main/java/org/apache/logging/log4j/message/StringMapMessage.java @@ -39,6 +39,10 @@ public StringMapMessage() { super(); } + public StringMapMessage(final StackTraceElement source) { + super(source); + } + /** * Constructs a new instance. * @@ -46,7 +50,11 @@ public StringMapMessage() { * the initial capacity. */ public StringMapMessage(final int initialCapacity) { - super(initialCapacity); + this(null, initialCapacity); + } + + public StringMapMessage(final StackTraceElement source, final int initialCapacity) { + super(source, initialCapacity); } /** @@ -56,7 +64,11 @@ public StringMapMessage(final int initialCapacity) { * The Map. */ public StringMapMessage(final Map map) { - super(map); + this(null, map); + } + + public StringMapMessage(StackTraceElement source, final Map map) { + super(source, map); } /** diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/message/StructuredDataCollectionMessage.java b/log4j-api/src/main/java/org/apache/logging/log4j/message/StructuredDataCollectionMessage.java index e58aed37450..dbcff003af3 100644 --- a/log4j-api/src/main/java/org/apache/logging/log4j/message/StructuredDataCollectionMessage.java +++ b/log4j-api/src/main/java/org/apache/logging/log4j/message/StructuredDataCollectionMessage.java @@ -99,4 +99,15 @@ public Throwable getThrowable() { } return null; } + + @Override + public StackTraceElement getSource() { + for (StructuredDataMessage msg : structuredDataMessageList) { + StackTraceElement t = msg.getSource(); + if (t != null) { + return t; + } + } + return null; + } } diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/message/StructuredDataMessage.java b/log4j-api/src/main/java/org/apache/logging/log4j/message/StructuredDataMessage.java index 32fa2a5b953..bcc0c65dc72 100644 --- a/log4j-api/src/main/java/org/apache/logging/log4j/message/StructuredDataMessage.java +++ b/log4j-api/src/main/java/org/apache/logging/log4j/message/StructuredDataMessage.java @@ -68,6 +68,16 @@ public StructuredDataMessage(final String id, final String msg, final String typ this(id, msg, type, MAX_LENGTH); } + /** + * Creates a StructuredDataMessage using an ID (max 32 characters), message, and type (max 32 characters). + * @param id The String id. + * @param msg The message. + * @param type The message type. + */ + public StructuredDataMessage(final String id, final String msg, final String type, StackTraceElement source) { + this(id, msg, type, MAX_LENGTH, source); + } + /** * Creates a StructuredDataMessage using an ID (user specified max characters), message, and type (user specified * maximum number of characters). @@ -83,7 +93,24 @@ public StructuredDataMessage(final String id, final String msg, final String typ this.type = type; this.maxLength = maxLength; } - + + /** + * Creates a StructuredDataMessage using an ID (user specified max characters), message, and type (user specified + * maximum number of characters). + * @param id The String id. + * @param msg The message. + * @param type The message type. + * @param maxLength The maximum length of keys; + * @since 2.9 + */ + public StructuredDataMessage(final String id, final String msg, final String type, final int maxLength, final StackTraceElement source) { + super(source); + this.id = new StructuredDataId(id, null, null, maxLength); + this.message = msg; + this.type = type; + this.maxLength = maxLength; + } + /** * Creates a StructuredDataMessage using an ID (max 32 characters), message, type (max 32 characters), and an * initial map of structured data to include. @@ -97,6 +124,19 @@ public StructuredDataMessage(final String id, final String msg, final String typ this(id, msg, type, data, MAX_LENGTH); } + /** + * Creates a StructuredDataMessage using an ID (max 32 characters), message, type (max 32 characters), and an + * initial map of structured data to include. + * @param id The String id. + * @param msg The message. + * @param type The message type. + * @param data The StructuredData map. + */ + public StructuredDataMessage(final String id, final String msg, final String type, + final Map data, StackTraceElement source) { + this(id, msg, type, data, MAX_LENGTH, source); + } + /** * Creates a StructuredDataMessage using an (user specified max characters), message, and type (user specified * maximum number of characters, and an initial map of structured data to include. @@ -116,6 +156,25 @@ public StructuredDataMessage(final String id, final String msg, final String typ this.maxLength = maxLength; } + /** + * Creates a StructuredDataMessage using an (user specified max characters), message, and type (user specified + * maximum number of characters, and an initial map of structured data to include. + * @param id The String id. + * @param msg The message. + * @param type The message type. + * @param data The StructuredData map. + * @param maxLength The maximum length of keys; + * @since 2.9 + */ + public StructuredDataMessage(final String id, final String msg, final String type, + final Map data, final int maxLength, StackTraceElement source) { + super(source, data); + this.id = new StructuredDataId(id, null, null, maxLength); + this.message = msg; + this.type = type; + this.maxLength = maxLength; + } + /** * Creates a StructuredDataMessage using a StructuredDataId, message, and type (max 32 characters). * @param id The StructuredDataId. @@ -126,6 +185,16 @@ public StructuredDataMessage(final StructuredDataId id, final String msg, final this(id, msg, type, MAX_LENGTH); } + /** + * Creates a StructuredDataMessage using a StructuredDataId, message, and type (max 32 characters). + * @param id The StructuredDataId. + * @param msg The message. + * @param type The message type. + */ + public StructuredDataMessage(final StructuredDataId id, final String msg, final String type, StackTraceElement source) { + this(id, msg, type, MAX_LENGTH, source); + } + /** * Creates a StructuredDataMessage using a StructuredDataId, message, and type (max 32 characters). * @param id The StructuredDataId. @@ -141,6 +210,22 @@ public StructuredDataMessage(final StructuredDataId id, final String msg, final this.maxLength = maxLength; } + /** + * Creates a StructuredDataMessage using a StructuredDataId, message, and type (max 32 characters). + * @param id The StructuredDataId. + * @param msg The message. + * @param type The message type. + * @param maxLength The maximum length of keys; + * @since 2.9 + */ + public StructuredDataMessage(final StructuredDataId id, final String msg, final String type, final int maxLength, StackTraceElement source) { + super(source); + this.id = id; + this.message = msg; + this.type = type; + this.maxLength = maxLength; + } + /** * Creates a StructuredDataMessage using a StructuredDataId, message, type (max 32 characters), and an initial map * of structured data to include. @@ -154,6 +239,19 @@ public StructuredDataMessage(final StructuredDataId id, final String msg, final this(id, msg, type, data, MAX_LENGTH); } + /** + * Creates a StructuredDataMessage using a StructuredDataId, message, type (max 32 characters), and an initial map + * of structured data to include. + * @param id The StructuredDataId. + * @param msg The message. + * @param type The message type. + * @param data The StructuredData map. + */ + public StructuredDataMessage(final StructuredDataId id, final String msg, final String type, + final Map data, StackTraceElement source) { + this(id, msg, type, data, MAX_LENGTH, source); + } + /** * Creates a StructuredDataMessage using a StructuredDataId, message, type (max 32 characters), and an initial map * of structured data to include. @@ -173,6 +271,25 @@ public StructuredDataMessage(final StructuredDataId id, final String msg, final this.maxLength = maxLength; } + /** + * Creates a StructuredDataMessage using a StructuredDataId, message, type (max 32 characters), and an initial map + * of structured data to include. + * @param id The StructuredDataId. + * @param msg The message. + * @param type The message type. + * @param data The StructuredData map. + * @param maxLength The maximum length of keys; + * @since 2.9 + */ + public StructuredDataMessage(final StructuredDataId id, final String msg, final String type, + final Map data, final int maxLength, StackTraceElement source) { + super(source, data); + this.id = id; + this.message = msg; + this.type = type; + this.maxLength = maxLength; + } + /** * Constructor based on a StructuredDataMessage. @@ -187,6 +304,19 @@ private StructuredDataMessage(final StructuredDataMessage msg, final Map map, StackTraceElement source) { + super(source, map); + this.id = msg.id; + this.message = msg.message; + this.type = msg.type; + this.maxLength = MAX_LENGTH; + } + /** * Basic constructor. */ diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/message/ThreadDumpMessage.java b/log4j-api/src/main/java/org/apache/logging/log4j/message/ThreadDumpMessage.java index 2229948ed40..65d4a82bf64 100644 --- a/log4j-api/src/main/java/org/apache/logging/log4j/message/ThreadDumpMessage.java +++ b/log4j-api/src/main/java/org/apache/logging/log4j/message/ThreadDumpMessage.java @@ -40,17 +40,28 @@ public class ThreadDumpMessage implements Message, StringBuilderFormattable { private volatile Map threads; private final String title; private String formattedMessage; + private StackTraceElement source; /** * Generate a ThreadDumpMessage with a title. * @param title The title. */ public ThreadDumpMessage(final String title) { + this((StackTraceElement) null, title); + } + + public ThreadDumpMessage(StackTraceElement source, final String title) { this.title = title == null ? Strings.EMPTY : title; threads = getFactory().createThreadInfo(); + this.source = source; } private ThreadDumpMessage(final String formattedMsg, final String title) { + this(null, formattedMsg, title); + } + + private ThreadDumpMessage(StackTraceElement source, final String formattedMsg, final String title) { + this.source = source; this.formattedMessage = formattedMsg; this.title = title == null ? Strings.EMPTY : title; } @@ -193,6 +204,11 @@ public Map createThreadInfo() { } } + @Override + public StackTraceElement getSource() { + return source; + } + /** * Always returns null. * diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/spi/AbstractLogger.java b/log4j-api/src/main/java/org/apache/logging/log4j/spi/AbstractLogger.java index 012dccae0d8..f9a390571c6 100644 --- a/log4j-api/src/main/java/org/apache/logging/log4j/spi/AbstractLogger.java +++ b/log4j-api/src/main/java/org/apache/logging/log4j/spi/AbstractLogger.java @@ -164,7 +164,12 @@ public static void checkMessageFactory(final ExtendedLogger logger, final Messag @Override public void catching(final Level level, final Throwable t) { - catching(FQCN, level, t); + catching(null, FQCN, level, t); + } + + @Override + public void catching(StackTraceElement source, Level level, Throwable t) { + catching(source, FQCN, level, t); } /** @@ -174,21 +179,26 @@ public void catching(final Level level, final Throwable t) { * @param level The logging level. * @param t The Throwable. */ - protected void catching(final String fqcn, final Level level, final Throwable t) { + protected void catching(final StackTraceElement source, final String fqcn, final Level level, final Throwable t) { if (isEnabled(level, CATCHING_MARKER, (Object) null, null)) { - logMessageSafely(fqcn, level, CATCHING_MARKER, catchingMsg(t), t); + logMessageSafely(fqcn, level, CATCHING_MARKER, catchingMsg(source, t), t); } } @Override public void catching(final Throwable t) { + catching((StackTraceElement) null, t); + } + + @Override + public void catching(StackTraceElement source, Throwable t) { if (isEnabled(Level.ERROR, CATCHING_MARKER, (Object) null, null)) { - logMessageSafely(FQCN, Level.ERROR, CATCHING_MARKER, catchingMsg(t), t); + logMessageSafely(FQCN, Level.ERROR, CATCHING_MARKER, catchingMsg(source, t), t); } } - protected Message catchingMsg(final Throwable t) { - return messageFactory.newMessage(CATCHING); + protected Message catchingMsg(final StackTraceElement source, final Throwable t) { + return messageFactory.newMessage(source, CATCHING); } private static Class createClassForProperty(final String property, @@ -505,10 +515,10 @@ public void debug(final String message, final Object p0, final Object p1, final * @param format Format String for the parameters. * @param paramSuppliers The Suppliers of the parameters. */ - protected EntryMessage enter(final String fqcn, final String format, final Supplier... paramSuppliers) { + protected EntryMessage enter(final StackTraceElement source, final String fqcn, final String format, final Supplier... paramSuppliers) { EntryMessage entryMsg = null; if (isEnabled(Level.TRACE, ENTRY_MARKER, (Object) null, null)) { - logMessageSafely(fqcn, Level.TRACE, ENTRY_MARKER, entryMsg = entryMsg(format, paramSuppliers), null); + logMessageSafely(fqcn, Level.TRACE, ENTRY_MARKER, entryMsg = entryMsg(source, format, paramSuppliers), null); } return entryMsg; } @@ -521,10 +531,10 @@ protected EntryMessage enter(final String fqcn, final String format, final Suppl * @param paramSuppliers The parameters to the method. */ @Deprecated - protected EntryMessage enter(final String fqcn, final String format, final MessageSupplier... paramSuppliers) { + protected EntryMessage enter(final StackTraceElement source, final String fqcn, final String format, final MessageSupplier... paramSuppliers) { EntryMessage entryMsg = null; if (isEnabled(Level.TRACE, ENTRY_MARKER, (Object) null, null)) { - logMessageSafely(fqcn, Level.TRACE, ENTRY_MARKER, entryMsg = entryMsg(format, paramSuppliers), null); + logMessageSafely(fqcn, Level.TRACE, ENTRY_MARKER, entryMsg = entryMsg(source, format, paramSuppliers), null); } return entryMsg; } @@ -536,10 +546,10 @@ protected EntryMessage enter(final String fqcn, final String format, final Messa * @param format The format String for the parameters. * @param params The parameters to the method. */ - protected EntryMessage enter(final String fqcn, final String format, final Object... params) { + protected EntryMessage enter(final StackTraceElement source, final String fqcn, final String format, final Object... params) { EntryMessage entryMsg = null; if (isEnabled(Level.TRACE, ENTRY_MARKER, (Object) null, null)) { - logMessageSafely(fqcn, Level.TRACE, ENTRY_MARKER, entryMsg = entryMsg(format, params), null); + logMessageSafely(fqcn, Level.TRACE, ENTRY_MARKER, entryMsg = entryMsg(source, format, params), null); } return entryMsg; } @@ -554,8 +564,7 @@ protected EntryMessage enter(final String fqcn, final String format, final Objec protected EntryMessage enter(final String fqcn, final MessageSupplier msgSupplier) { EntryMessage message = null; if (isEnabled(Level.TRACE, ENTRY_MARKER, (Object) null, null)) { - logMessageSafely(fqcn, Level.TRACE, ENTRY_MARKER, message = flowMessageFactory.newEntryMessage( - msgSupplier.get()), null); + logMessageSafely(fqcn, Level.TRACE, ENTRY_MARKER, message = flowMessageFactory.newEntryMessage(null, msgSupplier.get()), null); } return message; } @@ -569,10 +578,10 @@ protected EntryMessage enter(final String fqcn, final MessageSupplier msgSupplie * the Message. * @since 2.6 */ - protected EntryMessage enter(final String fqcn, final Message message) { + protected EntryMessage enter(final StackTraceElement source, final String fqcn, final Message message) { EntryMessage flowMessage = null; if (isEnabled(Level.TRACE, ENTRY_MARKER, (Object) null, null)) { - logMessageSafely(fqcn, Level.TRACE, ENTRY_MARKER, flowMessage = flowMessageFactory.newEntryMessage(message), + logMessageSafely(fqcn, Level.TRACE, ENTRY_MARKER, flowMessage = flowMessageFactory.newEntryMessage(source, message), null); } return flowMessage; @@ -581,12 +590,12 @@ protected EntryMessage enter(final String fqcn, final Message message) { @Deprecated @Override public void entry() { - entry(FQCN, (Object[]) null); + entry(null, FQCN, (Object[]) null); } @Override public void entry(final Object... params) { - entry(FQCN, params); + entry(null, FQCN, params); } /** @@ -595,26 +604,26 @@ public void entry(final Object... params) { * @param fqcn The fully qualified class name of the caller. * @param params The parameters to the method. */ - protected void entry(final String fqcn, final Object... params) { + protected void entry(final StackTraceElement source, final String fqcn, final Object... params) { if (isEnabled(Level.TRACE, ENTRY_MARKER, (Object) null, null)) { if (params == null) { - logMessageSafely(fqcn, Level.TRACE, ENTRY_MARKER, entryMsg(null, (Supplier[]) null), null); + logMessageSafely(fqcn, Level.TRACE, ENTRY_MARKER, entryMsg(source, null, (Supplier[]) null), null); } else { - logMessageSafely(fqcn, Level.TRACE, ENTRY_MARKER, entryMsg(null, params), null); + logMessageSafely(fqcn, Level.TRACE, ENTRY_MARKER, entryMsg(source, null, params), null); } } } - protected EntryMessage entryMsg(final String format, final Object... params) { + protected EntryMessage entryMsg(final StackTraceElement source, final String format, final Object... params) { final int count = params == null ? 0 : params.length; if (count == 0) { if (Strings.isEmpty(format)) { - return flowMessageFactory.newEntryMessage(null); + return flowMessageFactory.newEntryMessage(source, null); } - return flowMessageFactory.newEntryMessage(new SimpleMessage(format)); + return flowMessageFactory.newEntryMessage(source, new SimpleMessage(format)); } if (format != null) { - return flowMessageFactory.newEntryMessage(new ParameterizedMessage(format, params)); + return flowMessageFactory.newEntryMessage(source, new ParameterizedMessage(format, params)); } final StringBuilder sb = new StringBuilder(); sb.append("params("); @@ -626,20 +635,20 @@ protected EntryMessage entryMsg(final String format, final Object... params) { sb.append(parm instanceof Message ? ((Message) parm).getFormattedMessage() : String.valueOf(parm)); } sb.append(')'); - return flowMessageFactory.newEntryMessage(new SimpleMessage(sb)); + return flowMessageFactory.newEntryMessage(source, new SimpleMessage(sb)); } - protected EntryMessage entryMsg(final String format, final MessageSupplier... paramSuppliers) { + protected EntryMessage entryMsg(final StackTraceElement source, final String format, final MessageSupplier... paramSuppliers) { final int count = paramSuppliers == null ? 0 : paramSuppliers.length; final Object[] params = new Object[count]; for (int i = 0; i < count; i++) { params[i] = paramSuppliers[i].get(); params[i] = params[i] != null ? ((Message) params[i]).getFormattedMessage() : null; } - return entryMsg(format, params); + return entryMsg(source, format, params); } - protected EntryMessage entryMsg(final String format, final Supplier... paramSuppliers) { + protected EntryMessage entryMsg(final StackTraceElement source, final String format, final Supplier... paramSuppliers) { final int count = paramSuppliers == null ? 0 : paramSuppliers.length; final Object[] params = new Object[count]; for (int i = 0; i < count; i++) { @@ -648,7 +657,7 @@ protected EntryMessage entryMsg(final String format, final Supplier... paramS params[i] = ((Message) params[i]).getFormattedMessage(); } } - return entryMsg(format, params); + return entryMsg(source, format, params); } @Override @@ -911,26 +920,27 @@ public void error(final String message, final Object p0, final Object p1, final @Deprecated @Override public void exit() { - exit(FQCN, (Object) null); + exit(null, FQCN, (Object) null); } @Deprecated @Override public R exit(final R result) { - return exit(FQCN, result); + return exit(null, FQCN, result); } /** * Logs exiting from a method with the result and location information. * + * @param source The location of the log statement if known at comile time. * @param fqcn The fully qualified class name of the caller. * @param The type of the parameter and object being returned. * @param result The result being returned from the method call. * @return the return value passed to this method. */ - protected R exit(final String fqcn, final R result) { + protected R exit(final StackTraceElement source, final String fqcn, final R result) { if (isEnabled(Level.TRACE, EXIT_MARKER, (CharSequence) null, null)) { - logMessageSafely(fqcn, Level.TRACE, EXIT_MARKER, exitMsg(null, result), null); + logMessageSafely(fqcn, Level.TRACE, EXIT_MARKER, exitMsg(source, null, result), null); } return result; } @@ -938,29 +948,30 @@ protected R exit(final String fqcn, final R result) { /** * Logs exiting from a method with the result and location information. * + * @param source The location of the log statement if known at comile time. * @param fqcn The fully qualified class name of the caller. * @param The type of the parameter and object being returned. * @param result The result being returned from the method call. * @return the return value passed to this method. */ - protected R exit(final String fqcn, final String format, final R result) { + protected R exit(final StackTraceElement source, final String fqcn, final String format, final R result) { if (isEnabled(Level.TRACE, EXIT_MARKER, (CharSequence) null, null)) { - logMessageSafely(fqcn, Level.TRACE, EXIT_MARKER, exitMsg(format, result), null); + logMessageSafely(fqcn, Level.TRACE, EXIT_MARKER, exitMsg(source, format, result), null); } return result; } - protected Message exitMsg(final String format, final Object result) { + protected Message exitMsg(final StackTraceElement source, final String format, final Object result) { if (result == null) { if (format == null) { - return messageFactory.newMessage("Exit"); + return messageFactory.newMessage(source, "Exit"); } - return messageFactory.newMessage("Exit: " + format); + return messageFactory.newMessage(source, "Exit: " + format); } if (format == null) { - return messageFactory.newMessage("Exit with(" + result + ')'); + return messageFactory.newMessage(source, "Exit with(" + result + ')'); } - return messageFactory.newMessage("Exit: " + format, result); + return messageFactory.newMessage(source, "Exit: " + format, result); } @@ -2194,12 +2205,22 @@ private void handleLogMessageException(final Exception exception, final String f @Override public T throwing(final T t) { - return throwing(FQCN, Level.ERROR, t); + return throwing((StackTraceElement) null, t); + } + + @Override + public T throwing(StackTraceElement source, T t) { + return throwing(source, FQCN, Level.ERROR, t); } @Override public T throwing(final Level level, final T t) { - return throwing(FQCN, level, t); + return throwing((StackTraceElement) null, level, t); + } + + @Override + public T throwing(StackTraceElement source, Level level, T t) { + return throwing(source, FQCN, level, t); } /** @@ -2211,15 +2232,15 @@ public T throwing(final Level level, final T t) { * @param t The Throwable. * @return the Throwable. */ - protected T throwing(final String fqcn, final Level level, final T t) { + protected T throwing(final StackTraceElement source, final String fqcn, final Level level, final T t) { if (isEnabled(level, THROWING_MARKER, (Object) null, null)) { - logMessageSafely(fqcn, level, THROWING_MARKER, throwingMsg(t), t); + logMessageSafely(fqcn, level, THROWING_MARKER, throwingMsg(source, t), t); } return t; } - protected Message throwingMsg(final Throwable t) { - return messageFactory.newMessage(THROWING); + protected Message throwingMsg(final StackTraceElement source, final Throwable t) { + return messageFactory.newMessage(source, THROWING); } @Override @@ -2479,66 +2500,121 @@ public void trace(final String message, final Object p0, final Object p1, final @Override public EntryMessage traceEntry() { - return enter(FQCN, null, (Object[]) null); + return traceEntry((StackTraceElement) null); + } + + @Override + public EntryMessage traceEntry(final StackTraceElement source) { + return enter(source, FQCN, null, (Object[]) null); } @Override public EntryMessage traceEntry(final String format, final Object... params) { - return enter(FQCN, format, params); + return traceEntry(null, format, params); + } + + @Override + public EntryMessage traceEntry(final StackTraceElement source, final String format, final Object... params) { + return enter(source, FQCN, format, params); } @Override public EntryMessage traceEntry(final Supplier... paramSuppliers) { - return enter(FQCN, null, paramSuppliers); + return traceEntry(null, null, paramSuppliers); + } + + @Override + public EntryMessage traceEntry(final StackTraceElement source, final Supplier... paramSuppliers) { + return enter(source, FQCN, null, paramSuppliers); } @Override public EntryMessage traceEntry(final String format, final Supplier... paramSuppliers) { - return enter(FQCN, format, paramSuppliers); + return traceEntry(null, format, paramSuppliers); + } + + @Override + public EntryMessage traceEntry(final StackTraceElement source, final String format, final Supplier... paramSuppliers) { + return enter(source, FQCN, format, paramSuppliers); } @Override public EntryMessage traceEntry(final Message message) { - return enter(FQCN, message); + return traceEntry(null, message); + } + + @Override + public EntryMessage traceEntry(final StackTraceElement source, final Message message) { + return enter(source, FQCN, message); } @Override public void traceExit() { - exit(FQCN, null, null); + traceExit((StackTraceElement) null); + } + + @Override + public void traceExit(final StackTraceElement source) { + exit(source, FQCN, null, null); } @Override public R traceExit(final R result) { - return exit(FQCN, null, result); + return traceExit((StackTraceElement) null, result); + } + + @Override + public R traceExit(final StackTraceElement source, final R result) { + return exit(source, FQCN, null, result); } @Override public R traceExit(final String format, final R result) { - return exit(FQCN, format, result); + return traceExit(null, format, result); + } + + @Override + public R traceExit(final StackTraceElement source, final String format, final R result) { + return exit(source, FQCN, format, result); } @Override public void traceExit(final EntryMessage message) { + traceExit((StackTraceElement) null, message); + } + + @Override + public void traceExit(final StackTraceElement source, final EntryMessage message) { // If the message is null, traceEnter returned null because flow logging was disabled, we can optimize out calling isEnabled(). if (message != null && isEnabled(Level.TRACE, EXIT_MARKER, message, null)) { - logMessageSafely(FQCN, Level.TRACE, EXIT_MARKER, flowMessageFactory.newExitMessage(message), null); + logMessageSafely(FQCN, Level.TRACE, EXIT_MARKER, flowMessageFactory.newExitMessage(source, message), null); } } @Override public R traceExit(final EntryMessage message, final R result) { + return traceExit(null, message, result); + } + + @Override + public R traceExit(final StackTraceElement source, final EntryMessage message, final R result) { // If the message is null, traceEnter returned null because flow logging was disabled, we can optimize out calling isEnabled(). if (message != null && isEnabled(Level.TRACE, EXIT_MARKER, message, null)) { - logMessageSafely(FQCN, Level.TRACE, EXIT_MARKER, flowMessageFactory.newExitMessage(result, message), null); + logMessageSafely(FQCN, Level.TRACE, EXIT_MARKER, flowMessageFactory.newExitMessage(source, result, message), null); } return result; } @Override public R traceExit(final Message message, final R result) { + return traceExit(null, message, result); + } + + @Override + public R traceExit(final StackTraceElement source, final Message message, final R result) { // If the message is null, traceEnter returned null because flow logging was disabled, we can optimize out calling isEnabled(). if (message != null && isEnabled(Level.TRACE, EXIT_MARKER, message, null)) { - logMessageSafely(FQCN, Level.TRACE, EXIT_MARKER, flowMessageFactory.newExitMessage(result, message), null); + logMessageSafely(FQCN, Level.TRACE, EXIT_MARKER, flowMessageFactory.newExitMessage(source, result, message), null); } return result; } diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/spi/MessageFactory2Adapter.java b/log4j-api/src/main/java/org/apache/logging/log4j/spi/MessageFactory2Adapter.java index ff31515ef8f..d42cbcb373b 100644 --- a/log4j-api/src/main/java/org/apache/logging/log4j/spi/MessageFactory2Adapter.java +++ b/log4j-api/src/main/java/org/apache/logging/log4j/spi/MessageFactory2Adapter.java @@ -115,4 +115,74 @@ public Message newMessage(final String message) { public Message newMessage(final String message, final Object... params) { return wrapped.newMessage(message, params); } + + @Override + public Message newMessage(StackTraceElement source, CharSequence charSequence) { + return wrapped.newMessage(source, charSequence); + } + + @Override + public Message newMessage(StackTraceElement source, String message, Object p0) { + return wrapped.newMessage(source, message, p0); + } + + @Override + public Message newMessage(StackTraceElement source, String message, Object p0, Object p1) { + return wrapped.newMessage(source, message, p0, p1); + } + + @Override + public Message newMessage(StackTraceElement source, String message, Object p0, Object p1, Object p2) { + return wrapped.newMessage(source, message, p0, p1, p2); + } + + @Override + public Message newMessage(StackTraceElement source, String message, Object p0, Object p1, Object p2, Object p3) { + return wrapped.newMessage(source, message, p0, p1, p2, p3); + } + + @Override + public Message newMessage(StackTraceElement source, String message, Object p0, Object p1, Object p2, Object p3, Object p4) { + return wrapped.newMessage(source, message, p0, p1, p2, p3, p4); + } + + @Override + public Message newMessage(StackTraceElement source, String message, Object p0, Object p1, Object p2, Object p3, Object p4, Object p5) { + return wrapped.newMessage(source, message, p0, p1, p2, p3, p4, p5); + } + + @Override + public Message newMessage(StackTraceElement source, String message, Object p0, Object p1, Object p2, Object p3, Object p4, Object p5, Object p6) { + return wrapped.newMessage(source, message, p0, p1, p2, p3, p4, p5, p6); + } + + @Override + public Message newMessage(StackTraceElement source, String message, Object p0, Object p1, Object p2, Object p3, Object p4, Object p5, Object p6, Object p7) { + return wrapped.newMessage(source, message, p0, p1, p2, p3, p4, p5, p6, p7); + } + + @Override + public Message newMessage(StackTraceElement source, String message, Object p0, Object p1, Object p2, Object p3, Object p4, Object p5, Object p6, Object p7, Object p8) { + return wrapped.newMessage(source, message, p0, p1, p2, p3, p4, p5, p6, p7, p8); + } + + @Override + public Message newMessage(StackTraceElement source, String message, Object p0, Object p1, Object p2, Object p3, Object p4, Object p5, Object p6, Object p7, Object p8, Object p9) { + return wrapped.newMessage(source, message, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9); + } + + @Override + public Message newMessage(StackTraceElement source, Object message) { + return wrapped.newMessage(source, message); + } + + @Override + public Message newMessage(StackTraceElement source, String message) { + return wrapped.newMessage(source, message); + } + + @Override + public Message newMessage(StackTraceElement source, String message, Object... params) { + return wrapped.newMessage(source, message, params); + } } diff --git a/log4j-api/src/test/java/org/apache/logging/log4j/AbstractLoggerTest.java b/log4j-api/src/test/java/org/apache/logging/log4j/AbstractLoggerTest.java index f4800fecac8..f7d1bd3a739 100644 --- a/log4j-api/src/test/java/org/apache/logging/log4j/AbstractLoggerTest.java +++ b/log4j-api/src/test/java/org/apache/logging/log4j/AbstractLoggerTest.java @@ -22,11 +22,7 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; -import org.apache.logging.log4j.message.Message; -import org.apache.logging.log4j.message.ObjectMessage; -import org.apache.logging.log4j.message.ParameterizedMessage; -import org.apache.logging.log4j.message.ParameterizedMessageFactory; -import org.apache.logging.log4j.message.SimpleMessage; +import org.apache.logging.log4j.message.*; import org.apache.logging.log4j.spi.AbstractLogger; import org.apache.logging.log4j.spi.MessageFactory2Adapter; import org.apache.logging.log4j.util.MessageSupplier; @@ -1267,5 +1263,10 @@ public Object[] getParameters() { public Throwable getThrowable() { return throwable; } + + @Override + public StackTraceElement getSource() { + return null; + } } } diff --git a/log4j-api/src/test/java/org/apache/logging/log4j/message/JsonMessage.java b/log4j-api/src/test/java/org/apache/logging/log4j/message/JsonMessage.java index 5107841f380..17dbd591093 100644 --- a/log4j-api/src/test/java/org/apache/logging/log4j/message/JsonMessage.java +++ b/log4j-api/src/test/java/org/apache/logging/log4j/message/JsonMessage.java @@ -28,6 +28,7 @@ public class JsonMessage implements Message { private static final long serialVersionUID = 1L; private static final ObjectMapper mapper = new ObjectMapper(); + private final StackTraceElement source; private final Object object; /** @@ -35,7 +36,18 @@ public class JsonMessage implements Message { * * @param object the Object to serialize. */ - public JsonMessage(final Object object) { + public JsonMessage(Object object) { + this(null, object); + } + + /** + * Constructs a JsonMessage. + * + * @param source the location of the log statement. + * @param object the Object to serialize. + */ + public JsonMessage(final StackTraceElement source, final Object object) { + this.source = source; this.object = object; } @@ -63,4 +75,9 @@ public Object[] getParameters() { public Throwable getThrowable() { return null; } + + @Override + public StackTraceElement getSource() { + return source; + } } diff --git a/log4j-api/src/test/java/org/apache/logging/log4j/message/StringFormattedMessageTest.java b/log4j-api/src/test/java/org/apache/logging/log4j/message/StringFormattedMessageTest.java index 1cda558af76..a66c0b65a05 100644 --- a/log4j-api/src/test/java/org/apache/logging/log4j/message/StringFormattedMessageTest.java +++ b/log4j-api/src/test/java/org/apache/logging/log4j/message/StringFormattedMessageTest.java @@ -115,7 +115,7 @@ public void testSafeAfterGetFormattedMessageIsCalled() { // LOG4J2-763 } @Test - public void testSerialization() throws IOException, ClassNotFoundException { + public void testSerializationWithoutSource() throws IOException, ClassNotFoundException { final StringFormattedMessage expected = new StringFormattedMessage("Msg", "a", "b", "c"); final ByteArrayOutputStream baos = new ByteArrayOutputStream(); try (final ObjectOutputStream out = new ObjectOutputStream(baos)) { @@ -128,5 +128,24 @@ public void testSerialization() throws IOException, ClassNotFoundException { Assert.assertEquals(expected.getFormat(), actual.getFormat()); Assert.assertEquals(expected.getFormattedMessage(), actual.getFormattedMessage()); Assert.assertArrayEquals(expected.getParameters(), actual.getParameters()); + Assert.assertEquals(expected.getSource(), actual.getSource()); + } + + @Test + public void testSerializationWithSource() throws IOException, ClassNotFoundException { + final StackTraceElement source = new StackTraceElement("class", "method", "file", 55); + final StringFormattedMessage expected = new StringFormattedMessage(source, "Msg", "a", "b", "c"); + final ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try (final ObjectOutputStream out = new ObjectOutputStream(baos)) { + out.writeObject(expected); + } + final ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); + final ObjectInputStream in = new ObjectInputStream(bais); + final StringFormattedMessage actual = (StringFormattedMessage) in.readObject(); + Assert.assertEquals(expected, actual); + Assert.assertEquals(expected.getFormat(), actual.getFormat()); + Assert.assertEquals(expected.getFormattedMessage(), actual.getFormattedMessage()); + Assert.assertArrayEquals(expected.getParameters(), actual.getParameters()); + Assert.assertEquals(expected.getSource(), actual.getSource()); } } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLogger.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLogger.java index 358422c20ae..2bbf682206c 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLogger.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLogger.java @@ -202,7 +202,7 @@ private void initTranslator(final RingBufferLogEventTranslator translator, final ThreadContext.getImmutableStack(), // // location (expensive to calculate) - calcLocationIfRequested(fqcn), // + calcLocationIfRequested(fqcn, message), // CLOCK, // nanoClock // ); @@ -221,11 +221,14 @@ private void initTranslatorThreadValues(final RingBufferLogEventTranslator trans * @param fqcn fully qualified caller name. * @return the caller location if requested, {@code null} otherwise. */ - private StackTraceElement calcLocationIfRequested(final String fqcn) { + private StackTraceElement calcLocationIfRequested(final String fqcn, final Message message) { // location: very expensive operation. LOG4J2-153: // Only include if "includeLocation=true" is specified, // exclude if not specified or if "false" was specified. - return includeLocation ? StackLocatorUtil.calcLocation(fqcn) : null; + StackTraceElement messageSource = message.getSource(); + return messageSource == null ? + (includeLocation ? StackLocatorUtil.calcLocation(fqcn) : null) : + messageSource; } /** @@ -257,7 +260,7 @@ private void logWithVarargTranslator(final String fqcn, final Level level, final // calls the translateTo method on this AsyncLogger if (!disruptor.getRingBuffer().tryPublishEvent(this, this, // asyncLogger: 0 - (location = calcLocationIfRequested(fqcn)), // location: 1 + (location = calcLocationIfRequested(fqcn, message)), // location: 1 fqcn, // 2 level, // 3 marker, // 4 diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/RingBufferLogEvent.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/RingBufferLogEvent.java index 9e5181538af..5ed8486f255 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/RingBufferLogEvent.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/RingBufferLogEvent.java @@ -273,6 +273,13 @@ public Object[] swapParameters(final Object[] emptyReplacement) { return result; } + @Override + public StackTraceElement swapSource(StackTraceElement source) { + StackTraceElement original = this.location; + this.location = source; + return original; + } + /* * @see ReusableMessage#getParameterCount */ diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ConfigurationFactory.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ConfigurationFactory.java index 7e6dfb256c0..5a8dce8f71c 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ConfigurationFactory.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ConfigurationFactory.java @@ -332,12 +332,12 @@ public Configuration getConfiguration(final LoggerContext loggerContext, final S final String[] sources = configLocationStr.split(","); if (sources.length > 1) { final List configs = new ArrayList<>(); - for (final String sourceLocation : sources) { - final Configuration config = getConfiguration(loggerContext, sourceLocation.trim()); + for (final String StackTraceElement : sources) { + final Configuration config = getConfiguration(loggerContext, StackTraceElement.trim()); if (config != null && config instanceof AbstractConfiguration) { configs.add((AbstractConfiguration) config); } else { - LOGGER.error("Failed to created configuration at {}", sourceLocation); + LOGGER.error("Failed to created configuration at {}", StackTraceElement); return null; } } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/Log4jLogEvent.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/Log4jLogEvent.java index 2fe5a9be91c..045932acbba 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/Log4jLogEvent.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/Log4jLogEvent.java @@ -663,6 +663,9 @@ public StackTraceElement getSource() { if (source != null) { return source; } + if (message.getSource() != null) { + return message.getSource(); + } if (loggerFqcn == null || !includeLocation) { return null; } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/MutableLogEvent.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/MutableLogEvent.java index d03223f5f9e..6823edfe880 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/MutableLogEvent.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/MutableLogEvent.java @@ -359,6 +359,13 @@ public StackTraceElement getSource() { return source; } + @Override + public StackTraceElement swapSource(StackTraceElement source) { + StackTraceElement originalSource = this.source; + this.source = source; + return originalSource; + } + @SuppressWarnings("unchecked") @Override public ReadOnlyStringMap getContextData() { diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/FileLocationPatternConverter.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/FileLocationPatternConverter.java index 7fcd33d53bf..2c28361bfca 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/FileLocationPatternConverter.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/FileLocationPatternConverter.java @@ -19,7 +19,6 @@ import org.apache.logging.log4j.core.LogEvent; import org.apache.logging.log4j.core.config.plugins.Plugin; - /** * Returns the event's line location information in a StringBuilder. */ diff --git a/log4j-samples/log4j-samples-loggerProperties/src/main/java/org/apache/logging/log4j/lookup/CustomMapMessage.java b/log4j-samples/log4j-samples-loggerProperties/src/main/java/org/apache/logging/log4j/lookup/CustomMapMessage.java index 06f78f45174..8bf3d45dd42 100644 --- a/log4j-samples/log4j-samples-loggerProperties/src/main/java/org/apache/logging/log4j/lookup/CustomMapMessage.java +++ b/log4j-samples/log4j-samples-loggerProperties/src/main/java/org/apache/logging/log4j/lookup/CustomMapMessage.java @@ -31,7 +31,11 @@ public class CustomMapMessage extends StringMapMessage { private final String message; public CustomMapMessage(final String msg, final Map map) { - super(map); + this(null, msg, map); + } + + public CustomMapMessage(final StackTraceElement source, final String msg, final Map map) { + super(source, map); this.message = msg; } From 66680d9ee7cf24c0364c503bff4b09013edd64e6 Mon Sep 17 00:00:00 2001 From: Jeff Shaw Date: Mon, 16 Jul 2018 13:33:11 -0400 Subject: [PATCH 2/2] fix MomentoMessage --- .../log4j/core/async/RingBufferLogEvent.java | 2 +- .../logging/log4j/core/impl/Log4jLogEvent.java | 2 +- .../log4j/core/impl/MementoMessage.java | 18 ++++++++++++++++++ .../log4j/core/impl/MutableLogEvent.java | 2 +- .../async/AsyncLoggerConfigErrorOnFormat.java | 5 +++++ 5 files changed, 26 insertions(+), 3 deletions(-) diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/RingBufferLogEvent.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/RingBufferLogEvent.java index a5b8c57db61..932c49069df 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/RingBufferLogEvent.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/RingBufferLogEvent.java @@ -299,7 +299,7 @@ public void forEachParameter(ParameterConsumer action, S state) { @Override public Message memento() { if (message == null) { - message = new MementoMessage(String.valueOf(messageText), messageFormat, getParameters()); + message = new MementoMessage(getSource(), String.valueOf(messageText), messageFormat, getParameters()); } return message; } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/Log4jLogEvent.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/Log4jLogEvent.java index 0c75891b9f4..f72a470cab5 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/Log4jLogEvent.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/Log4jLogEvent.java @@ -538,7 +538,7 @@ public Message getMessage() { } public void makeMessageImmutable() { - message = new MementoMessage(message.getFormattedMessage(), message.getFormat(), message.getParameters()); + message = new MementoMessage(message.getSource(), message.getFormattedMessage(), message.getFormat(), message.getParameters()); } @Override diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/MementoMessage.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/MementoMessage.java index c867ce44713..5869ad6ffa9 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/MementoMessage.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/MementoMessage.java @@ -35,11 +35,24 @@ public final class MementoMessage implements Message, StringBuilderFormattable { private final String formattedMessage; private final String format; private final Object[] parameters; + private final StackTraceElement source; + /** + * @deprecated Use {@link #MementoMessage(StackTraceElement, String, String, Object[])} + */ + @Deprecated public MementoMessage(String formattedMessage, String format, Object[] parameters) { this.formattedMessage = formattedMessage; this.format = format; this.parameters = parameters; + this.source = null; + } + + public MementoMessage(StackTraceElement source, String formattedMessage, String format, Object[] parameters) { + this.source = source; + this.formattedMessage = formattedMessage; + this.format = format; + this.parameters = parameters; } @Override @@ -67,6 +80,11 @@ public Throwable getThrowable() { return null; } + @Override + public StackTraceElement getSource() { + return source; + } + @Override public void formatTo(StringBuilder buffer) { buffer.append(formattedMessage); diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/MutableLogEvent.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/MutableLogEvent.java index a5d7ebd5072..ca52ac6864c 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/MutableLogEvent.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/MutableLogEvent.java @@ -304,7 +304,7 @@ public short getParameterCount() { @Override public Message memento() { if (message == null) { - message = new MementoMessage(String.valueOf(messageText), messageFormat, getParameters()); + message = new MementoMessage(source, String.valueOf(messageText), messageFormat, getParameters()); } return message; } diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/async/AsyncLoggerConfigErrorOnFormat.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/async/AsyncLoggerConfigErrorOnFormat.java index 57ed7fdf13a..cfe67ac00ac 100644 --- a/log4j-core/src/test/java/org/apache/logging/log4j/core/async/AsyncLoggerConfigErrorOnFormat.java +++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/async/AsyncLoggerConfigErrorOnFormat.java @@ -97,5 +97,10 @@ public Object[] getParameters() { public Throwable getThrowable() { return null; } + + @Override + public StackTraceElement getSource() { + return null; + } } }