You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by rg...@apache.org on 2019/08/13 05:20:30 UTC

[logging-log4j2] branch master updated: LOG4J2-2639 - Add documentation. Add serialization support

This is an automated email from the ASF dual-hosted git repository.

rgoers pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git


The following commit(s) were added to refs/heads/master by this push:
     new 377d177  LOG4J2-2639 - Add documentation. Add serialization support
377d177 is described below

commit 377d177edb1bb599365eb339bc5e6c8108c64b88
Author: Ralph Goers <rg...@apache.org>
AuthorDate: Mon Aug 12 22:18:58 2019 -0700

    LOG4J2-2639 - Add documentation. Add serialization support
---
 .../main/java/org/apache/logging/log4j/Logger.java |  18 ++---
 .../apache/logging/log4j/spi/AbstractLogger.java   |  32 ++++++---
 .../log4j/perf/jmh/FileAppenderBenchmark.java      |  19 +++--
 src/changes/changes.xml                            |   7 +-
 src/site/asciidoc/index.adoc                       |   4 ++
 src/site/asciidoc/manual/logbuilder.adoc           |  79 +++++++++++++++++++++
 src/site/resources/images/LocationPerf.png         | Bin 0 -> 33266 bytes
 src/site/site.xml                                  |   6 +-
 8 files changed, 138 insertions(+), 27 deletions(-)

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 0d7b090..c714807 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
@@ -4201,7 +4201,7 @@ public interface Logger {
      * @param location The location of the caller.
      * @param message The message format.
      * @param throwable the exception to log, including its stack trace.
-     * @since 3.0
+     * @since 2.13.0
      */
     default void logMessage(Level level, Marker marker, String fqcn, StackTraceElement location, Message message,
             Throwable throwable) {
@@ -4211,7 +4211,7 @@ public interface Logger {
     /**
      * Constuct a trace log event.
      * @return a LogBuilder.
-     * @since 3.0
+     * @since 2.13.0
      */
     default LogBuilder atTrace() {
         return LogBuilder.NOOP;
@@ -4219,7 +4219,7 @@ public interface Logger {
     /**
      * Constuct a trace log event.
      * @return a LogBuilder.
-     * @since 3.0
+     * @since 2.13.0
      */
     default LogBuilder atDebug() {
         return LogBuilder.NOOP;
@@ -4227,7 +4227,7 @@ public interface Logger {
     /**
      * Constuct a trace log event.
      * @return a LogBuilder.
-     * @since 3.0
+     * @since 2.13.0
      */
     default LogBuilder atInfo() {
         return LogBuilder.NOOP;
@@ -4235,7 +4235,7 @@ public interface Logger {
     /**
      * Constuct a trace log event.
      * @return a LogBuilder.
-     * @since 3.0
+     * @since 2.13.0
      */
     default LogBuilder atWarn() {
         return LogBuilder.NOOP;
@@ -4243,7 +4243,7 @@ public interface Logger {
     /**
      * Constuct a trace log event.
      * @return a LogBuilder.
-     * @since 3.0
+     * @since 2.13.0
      */
     default LogBuilder atError() {
         return LogBuilder.NOOP;
@@ -4251,7 +4251,7 @@ public interface Logger {
     /**
      * Constuct a trace log event.
      * @return a LogBuilder.
-     * @since 3.0
+     * @since 2.13.0
      */
     default LogBuilder atFatal() {
         return LogBuilder.NOOP;
@@ -4259,7 +4259,7 @@ public interface Logger {
     /**
      * Constuct a log event that will alwways be logged.
      * @return a LogBuilder.
-     * @since 3.0
+     * @since 2.13.0
      */
     default LogBuilder always() {
         return LogBuilder.NOOP;
@@ -4267,7 +4267,7 @@ public interface Logger {
     /**
      * Constuct a log event.
      * @return a LogBuilder.
-     * @since 3.0
+     * @since 2.13.0
      */
     default LogBuilder atLevel(Level level) {
         return LogBuilder.NOOP;
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 548f748..0134ab3 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
@@ -43,7 +43,10 @@ import org.apache.logging.log4j.util.StackLocatorUtil;
 import org.apache.logging.log4j.util.Strings;
 import org.apache.logging.log4j.util.Supplier;
 
+import java.io.IOException;
+import java.io.ObjectInputStream;
 import java.io.Serializable;
+import java.lang.reflect.Field;
 
 /**
  * Base implementation of a Logger. It is highly recommended that any Logger implementation extend this class.
@@ -107,7 +110,7 @@ public abstract class AbstractLogger implements ExtendedLogger, Serializable {
     private final MessageFactory messageFactory;
     private final FlowMessageFactory flowMessageFactory;
     private static ThreadLocal<int[]> recursionDepthHolder = new ThreadLocal<>(); // LOG4J2-1518, LOG4J2-2031
-    protected final ThreadLocal<DefaultLogBuilder> logBuilder;
+    protected final transient ThreadLocal<DefaultLogBuilder> logBuilder;
 
 
     /**
@@ -2766,7 +2769,7 @@ public abstract class AbstractLogger implements ExtendedLogger, Serializable {
     /**
      * Constuct a trace log event.
      * @return a LogBuilder.
-     * @since 3.0
+     * @since 2.13.0
      */
     @Override
     public  LogBuilder atTrace() {
@@ -2775,7 +2778,7 @@ public abstract class AbstractLogger implements ExtendedLogger, Serializable {
     /**
      * Constuct a debug log event.
      * @return a LogBuilder.
-     * @since 3.0
+     * @since 2.13.0
      */
     @Override
     public LogBuilder atDebug() {
@@ -2784,7 +2787,7 @@ public abstract class AbstractLogger implements ExtendedLogger, Serializable {
     /**
      * Constuct an informational log event.
      * @return a LogBuilder.
-     * @since 3.0
+     * @since 2.13.0
      */
     @Override
     public LogBuilder atInfo() {
@@ -2793,7 +2796,7 @@ public abstract class AbstractLogger implements ExtendedLogger, Serializable {
     /**
      * Constuct a warning log event.
      * @return a LogBuilder.
-     * @since 3.0
+     * @since 2.13.0
      */
     @Override
     public LogBuilder atWarn() {
@@ -2802,7 +2805,7 @@ public abstract class AbstractLogger implements ExtendedLogger, Serializable {
     /**
      * Constuct an error log event.
      * @return a LogBuilder.
-     * @since 3.0
+     * @since 2.13.0
      */
     @Override
     public LogBuilder atError() {
@@ -2811,7 +2814,7 @@ public abstract class AbstractLogger implements ExtendedLogger, Serializable {
     /**
      * Constuct a fatal log event.
      * @return a LogBuilder.
-     * @since 3.0
+     * @since 2.13.0
      */
     @Override
     public LogBuilder atFatal() {
@@ -2820,7 +2823,7 @@ public abstract class AbstractLogger implements ExtendedLogger, Serializable {
     /**
      * Constuct a fatal log event.
      * @return a LogBuilder.
-     * @since 3.0
+     * @since 2.13.0
      */
     @Override
     public LogBuilder always() {
@@ -2833,7 +2836,7 @@ public abstract class AbstractLogger implements ExtendedLogger, Serializable {
     /**
      * Constuct a log event.
      * @return a LogBuilder.
-     * @since 3.0
+     * @since 2.13.0
      */
     @Override
     public LogBuilder atLevel(Level level) {
@@ -2849,6 +2852,17 @@ public abstract class AbstractLogger implements ExtendedLogger, Serializable {
         return Constants.ENABLE_THREADLOCALS && !builder.isInUse() ? builder : new DefaultLogBuilder(this, level);
     }
 
+    private void readObject (final ObjectInputStream s) throws ClassNotFoundException, IOException {
+        s.defaultReadObject( );
+        try {
+            Field f = this.getClass().getDeclaredField("logBuilder");
+            f.setAccessible(true);
+            f.set(this, new LocalLogBuilder(this));
+        } catch (NoSuchFieldException | IllegalAccessException ex) {
+            StatusLogger.getLogger().warn("Unable to initialize LogBuilder");
+        }
+    }
+
     private class LocalLogBuilder extends ThreadLocal<DefaultLogBuilder> {
         private AbstractLogger logger;
         LocalLogBuilder(AbstractLogger logger) {
diff --git a/log4j-perf/src/main/java/org/apache/logging/log4j/perf/jmh/FileAppenderBenchmark.java b/log4j-perf/src/main/java/org/apache/logging/log4j/perf/jmh/FileAppenderBenchmark.java
index 3a803fa..91ce426 100644
--- a/log4j-perf/src/main/java/org/apache/logging/log4j/perf/jmh/FileAppenderBenchmark.java
+++ b/log4j-perf/src/main/java/org/apache/logging/log4j/perf/jmh/FileAppenderBenchmark.java
@@ -107,13 +107,13 @@ public class FileAppenderBenchmark {
         final File julFile = new File("target/testJulLog.log");
         julFile.delete();
     }
-
+/*
     @BenchmarkMode(Mode.Throughput)
     @OutputTimeUnit(TimeUnit.SECONDS)
     @Benchmark
     public void log4j2RAF() {
         log4j2RandomLogger.debug(MESSAGE);
-    }
+    }*/
 
     /* The MemoryMappedFileAppender gets exceptions in Java 11
     @BenchmarkMode(Mode.Throughput)
@@ -122,7 +122,7 @@ public class FileAppenderBenchmark {
     public void log4j2MMF() {
         log4j2MemoryLogger.debug(MESSAGE);
     } */
-
+/*
     @BenchmarkMode(Mode.Throughput)
     @OutputTimeUnit(TimeUnit.SECONDS)
     @Benchmark
@@ -142,7 +142,7 @@ public class FileAppenderBenchmark {
     @Benchmark
     public void log4j2AsyncLogger() {
         log4j2AsyncLogger.debug(MESSAGE);
-    }
+    }*/
 
     @BenchmarkMode(Mode.Throughput)
     @OutputTimeUnit(TimeUnit.SECONDS)
@@ -154,10 +154,17 @@ public class FileAppenderBenchmark {
     @BenchmarkMode(Mode.Throughput)
     @OutputTimeUnit(TimeUnit.SECONDS)
     @Benchmark
+    public void log4j2Builder() {
+        log4j2Logger.atDebug().withLocation().log(MESSAGE);
+    }
+
+    @BenchmarkMode(Mode.Throughput)
+    @OutputTimeUnit(TimeUnit.SECONDS)
+    @Benchmark
     public void logbackFile() {
         slf4jLogger.debug(MESSAGE);
     }
-
+/*
     @BenchmarkMode(Mode.Throughput)
     @OutputTimeUnit(TimeUnit.SECONDS)
     @Benchmark
@@ -178,5 +185,5 @@ public class FileAppenderBenchmark {
     public void julFile() {
         // must specify sourceClass or JUL will look it up by walking the stack trace!
         julLogger.logp(Level.INFO, getClass().getName(), "julFile", MESSAGE);
-    }
+    }*/
 }
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 5a295c1..1f94586 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -141,7 +141,12 @@
         Update Apache Flume from 1.8.0 to 1.9.0.
       </action>
     </release>
-    <release version="2.12.1" date="2019-MM-DD" description="GA Release 2.12.1">
+    <release version="2.13.0" date="2019-MM-DD" description="GA Release 2.13.0">
+      <action issue="LOG4J2-2639" dev="rgoers" type="add">
+        Add builder pattern to Logger interface.
+      </action>
+    </release>
+    <release version="2.12.1" date="2019-08-06" description="GA Release 2.12.1">
       <action issue="LOG4J2-1946" dev="rgoers" type="fix" due-to="Igor Perelyotov">
         Allow file renames to work when files are missing from the sequence.
       </action>
diff --git a/src/site/asciidoc/index.adoc b/src/site/asciidoc/index.adoc
index 341b5be..5e1c80a 100644
--- a/src/site/asciidoc/index.adoc
+++ b/src/site/asciidoc/index.adoc
@@ -95,6 +95,10 @@ level is not enabled, the same effect can be achieved with less code.
 In Log4j 2, link:manual/customloglevels.html[custom log levels] can easily be defined in code or in configuration. No
 subclassing is required.
 
+=== Log Builder API
+In addition to using one of the many log methods in the Log4j API, log events can be constructed using a builder. See
+link:manual/logbuilder.html[Log Builder] for more information.
+
 === Garbage-free
 
 During steady state logging, Log4j 2 is link:manual/garbagefree.html[garbage-free] in stand-alone applications, and low
diff --git a/src/site/asciidoc/manual/logbuilder.adoc b/src/site/asciidoc/manual/logbuilder.adoc
new file mode 100644
index 0000000..a7af088
--- /dev/null
+++ b/src/site/asciidoc/manual/logbuilder.adoc
@@ -0,0 +1,79 @@
+////
+    Licensed to the Apache Software Foundation (ASF) under one or more
+    contributor license agreements. See the NOTICE file distributed with
+    this work for additional information regarding copyright ownership.
+    The ASF licenses this file to You under the Apache License, Version 2.0
+    (the "License"); you may not use this file except in compliance with
+    the License. You may obtain a copy of the License at
+
+        https://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+////
+= Log4j 2 API
+
+== Log Builder
+
+Log4j has traditionally been used with logging statements like
+[source,java]
+----
+logger.error("Unable to process request due to {}", code, exception);
+----
+
+This has resulted in some confusion as to whether the exception should be a parameter to the message or
+if Log4j should handle it as a throwable. In order to make logging clearer a builder pattern has been
+added to the API. Using the builder syntax the above would be handled as:
+[source,java]
+----
+logger.atError().withThrowable(exception).log("Unable to process request due to {}", code);
+----
+
+With this syntax it is clear that the exception is to be treated as a Throwable by Log4j.
+
+The Logger class now returns a LogBuilder when any of the atTrace, atDebug, atInfo, atWarn, atError,
+atFatal, always, or atLevel(Level) methods are called. The logBuilder then allows a Marker, Throwable,
+and/or location to be added to the event before it is logged. A call to the log method always causes the
+log event to be finalized and sent.
+
+A logging statement with a Marker, Throwable, and location would look like:
+[source,java]
+----
+logger.atInfo().withMarker(marker).withLocation().withThrowable(exception).log("Login for user {} failed", userId);
+----
+Providing the location method on the LogBuilder provides two distinct advantages:
+
+1. Logging wrappers can use it to provide the location information to be used by Log4j.</li>
+2. The overhead of capturing location information when using the location method with no
+parameters is much better than having to calculate the location information when it is needed. Log4j
+can simply ask for the stack trace entry at a fixed index instead of having to walk the stack trace
+to determine the calling class. Of course, if the location information will not be used by the layout
+this will result in slower performance.</li>
+
+=== Location Performance
+
+The table below shows some of the results from the FileAppenderBenchmark and FileAppenderWithLocationBenchmark
+classes in the log4j-perf project when configured to use 4 threads. The results show that lazily including
+the location information is about 8 times slower than not including location information. While using the
+withLocation method of LogBuilder is about 3 times faster than lazily calculating the location information
+it is still about 2.5 times slower than not including locatoin information.
+
+The tests were run on a 2018 MacBook Pro with a 2.9 GHz Intel Core i9 processor with 6 cores, 32 GB of memory
+and 1 TB of SSD storage on Java 11 using Log4j 2.13.0 and Logback 1.2.3.
+image:../images/LocationPerf.png[Location Performance]
+
+|===
+|Test|Print Location Info|No Location Info Printed
+
+|Log4j2 File| 191,509.724 ± 11339.978  ops/s| 1,407,329.130 ± 22595.997  ops/s
+|Log4j2 Log Builder withLocation()|469,200.684 ± 50025.985  ops/s|577,127.463 ± 11464.342  ops/s
+|Logback File|159,116.538 ± 1884.969  ops/s|1,240,438.384 ± 76619.873  ops/s
+|===
+As expected, when using LogBuilder with a call to the withLocation() method logging is much faster when
+location informatoin is used in the output but significantly slower when it is not.
+
+Note: Running the tests at various times provides varying results. Although some results have been as much
+as 10% higher all results are generally affected similarly so the comparisons between them stay the same.
\ No newline at end of file
diff --git a/src/site/resources/images/LocationPerf.png b/src/site/resources/images/LocationPerf.png
new file mode 100644
index 0000000..1a232ef
Binary files /dev/null and b/src/site/resources/images/LocationPerf.png differ
diff --git a/src/site/site.xml b/src/site/site.xml
index 49dfe52..48ae309 100644
--- a/src/site/site.xml
+++ b/src/site/site.xml
@@ -76,6 +76,7 @@
       <item name="Log4j 1.x Migration" href="manual/migration.html"/>
 
       <item name="Java API" href="/manual/api.html" collapse="true">
+        <item name="Log Builder" href="manual/logbuilder.html"/>
         <item name="Flow Tracing" href="manual/flowtracing.html"/>
         <item name="Markers" href="manual/markers.html"/>
         <item name="Event Logging" href="/manual/eventlogging.html"/>
@@ -274,8 +275,9 @@
     </menu>
 
     <menu name="Legacy" inherit="top" img="icon-tags">
-      <item name="Log4j 1.2" href="http://logging.apache.org/log4j/1.2/"/>
-      <item name="Log4j 2.3" href="http://logging.apache.org/log4j/log4j-2.3/"/>
+      <item name="Log4j 1.2 - End of Life" href="http://logging.apache.org/log4j/1.2/"/>
+      <item name="Log4j 2.3 - Java 6" href="http://logging.apache.org/log4j/log4j-2.3/"/>
+      <item name="Log4j 2.12.1 - Java 7" href="http://logging.apache.org/log4j/log4j-2.12.1/"/>
     </menu>
 
     <menu name="Components" inherit="top" img="icon-cog">