Just FYI, if you make a plugin builder, you don't need to make a plugin factory as well. The builder is used first if available followed by the factory method. Classes that have both are mainly for backwards compatibility.
---------- Forwarded message ---------- From: <[email protected]> Date: 26 May 2016 at 10:12 Subject: logging-log4j2 git commit: FastConsoleAppender To: [email protected] Repository: logging-log4j2 Updated Branches: refs/heads/LOG4J2-1395 [created] 06c554689 FastConsoleAppender Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/06c55468 Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/06c55468 Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/06c55468 Branch: refs/heads/LOG4J2-1395 Commit: 06c55468948efdce95a591801d5f9e2ffdb69dd4 Parents: e012269 Author: Mikael Ståldal <[email protected]> Authored: Thu May 26 17:12:33 2016 +0200 Committer: Mikael Ståldal <[email protected]> Committed: Thu May 26 17:12:33 2016 +0200 ---------------------------------------------------------------------- .../core/appender/FastConsoleAppender.java | 227 +++++++++++++++++++ .../jmh/Log4j2AppenderComparisonBenchmark.java | 18 ++ .../resources/log4j2-appenderComparison.xml | 6 + 3 files changed, 251 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/06c55468/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/FastConsoleAppender.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/FastConsoleAppender.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/FastConsoleAppender.java new file mode 100644 index 0000000..a7cd905 --- /dev/null +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/FastConsoleAppender.java @@ -0,0 +1,227 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache license, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ +package org.apache.logging.log4j.core.appender; + +import java.io.FileDescriptor; +import java.io.FileOutputStream; +import java.io.OutputStream; +import java.io.Serializable; +import java.util.concurrent.atomic.AtomicInteger; + +import org.apache.logging.log4j.core.Filter; +import org.apache.logging.log4j.core.Layout; +import org.apache.logging.log4j.core.config.plugins.Plugin; +import org.apache.logging.log4j.core.config.plugins.PluginAttribute; +import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute; +import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory; +import org.apache.logging.log4j.core.config.plugins.PluginElement; +import org.apache.logging.log4j.core.config.plugins.PluginFactory; +import org.apache.logging.log4j.core.config.plugins.validation.constraints.Required; +import org.apache.logging.log4j.core.layout.PatternLayout; +import org.apache.logging.log4j.core.util.CloseShieldOutputStream; + +/** + * Appends log events to <code>System.out</code> or <code>System.err</code> using a layout specified by the user. The + * default target is <code>System.out</code>. + */ +@Plugin(name = "FastConsole", category = "Core", elementType = "appender", printObject = true) +public final class FastConsoleAppender extends AbstractOutputStreamAppender<OutputStreamManager> { + + private static ConsoleManagerFactory factory = new ConsoleManagerFactory(); + private static final Target DEFAULT_TARGET = Target.SYSTEM_OUT; + private static final AtomicInteger COUNT = new AtomicInteger(); + + private final Target target; + + /** + * Enumeration of console destinations. + */ + public enum Target { + /** Standard output. */ + SYSTEM_OUT, + /** Standard error output. */ + SYSTEM_ERR + } + + private FastConsoleAppender(final String name, final Layout<? extends Serializable> layout, final Filter filter, + final OutputStreamManager manager, final boolean ignoreExceptions, Target target) { + super(name, layout, filter, ignoreExceptions, true, manager); + this.target = target; + } + + /** + * Creates a Console Appender. + * + * @param layout The layout to use (required). + * @param filter The Filter or null. + * @param target The target (SYSTEM_OUT or SYSTEM_ERR). The default is SYSTEM_OUT. + * @param name The name of the Appender (required). + * @param ignoreExceptions If {@code "true"} (default) exceptions encountered when appending events are logged; otherwise they + * are propagated to the caller. + * @return The ConsoleAppender. + */ + @PluginFactory + public static FastConsoleAppender createAppender( + // @formatter:off + @PluginElement("Layout") Layout<? extends Serializable> layout, + @PluginElement("Filter") final Filter filter, + @PluginAttribute(value = "target") Target target, + @PluginAttribute("name") final String name, + @PluginAttribute(value = "ignoreExceptions", defaultBoolean = true) final boolean ignoreExceptions) { + // @formatter:on + if (name == null) { + LOGGER.error("No name provided for ConsoleAppender"); + return null; + } + if (layout == null) { + layout = PatternLayout.createDefaultLayout(); + } + target = target == null ? Target.SYSTEM_OUT : target; + return new FastConsoleAppender(name, layout, filter, getManager(target, layout), ignoreExceptions, target); + } + + public static FastConsoleAppender createDefaultAppenderForLayout(final Layout<? extends Serializable> layout) { + // this method cannot use the builder class without introducing an infinite loop due to DefaultConfiguration + return new FastConsoleAppender("DefaultConsole-" + COUNT.incrementAndGet(), layout, null, + getDefaultManager(DEFAULT_TARGET, layout), true, DEFAULT_TARGET); + } + + @PluginBuilderFactory + public static Builder newBuilder() { + return new Builder(); + } + + /** + * Builds ConsoleAppender instances. + */ + public static class Builder implements org.apache.logging.log4j.core.util.Builder<FastConsoleAppender> { + + @PluginElement("Layout") + @Required + private Layout<? extends Serializable> layout = PatternLayout.createDefaultLayout(); + + @PluginElement("Filter") + private Filter filter; + + @PluginBuilderAttribute + @Required + private Target target = DEFAULT_TARGET; + + @PluginBuilderAttribute + @Required + private String name; + + @PluginBuilderAttribute + private boolean ignoreExceptions = true; + + public Builder setLayout(final Layout<? extends Serializable> aLayout) { + this.layout = aLayout; + return this; + } + + public Builder setFilter(final Filter aFilter) { + this.filter = aFilter; + return this; + } + + public Builder setTarget(final Target aTarget) { + this.target = aTarget; + return this; + } + + public Builder setName(final String aName) { + this.name = aName; + return this; + } + + public Builder setIgnoreExceptions(final boolean shouldIgnoreExceptions) { + this.ignoreExceptions = shouldIgnoreExceptions; + return this; + } + + @Override + public FastConsoleAppender build() { + return new FastConsoleAppender(name, layout, filter, getManager(target, layout), ignoreExceptions, target); + } + } + + private static OutputStreamManager getDefaultManager(final Target target, final Layout<? extends Serializable> layout) { + final OutputStream os = getOutputStream(target); + + // LOG4J2-1176 DefaultConfiguration should not share OutputStreamManager instances to avoid memory leaks. + final String managerName = target.name() + '-' + COUNT.get(); + return OutputStreamManager.getManager(managerName, new FactoryData(os, managerName, layout), factory); + } + + private static OutputStreamManager getManager(final Target target, final Layout<? extends Serializable> layout) { + final OutputStream os = getOutputStream(target); + final String managerName = target.name(); + return OutputStreamManager.getManager(managerName, new FactoryData(os, managerName, layout), factory); + } + + private static OutputStream getOutputStream(final Target target) { + OutputStream outputStream = target == Target.SYSTEM_OUT + ? new FileOutputStream(FileDescriptor.out) + : new FileOutputStream(FileDescriptor.err); + return new CloseShieldOutputStream(outputStream); + } + + /** + * Data to pass to factory method. + */ + private static class FactoryData { + private final OutputStream os; + private final String name; + private final Layout<? extends Serializable> layout; + + /** + * Constructor. + * + * @param os The OutputStream. + * @param type The name of the target. + * @param layout A Serializable layout + */ + public FactoryData(final OutputStream os, final String type, final Layout<? extends Serializable> layout) { + this.os = os; + this.name = type; + this.layout = layout; + } + } + + /** + * Factory to create the Appender. + */ + private static class ConsoleManagerFactory implements ManagerFactory<OutputStreamManager, FactoryData> { + + /** + * Create an OutputStreamManager. + * + * @param name The name of the entity to manage. + * @param data The data required to create the entity. + * @return The OutputStreamManager + */ + @Override + public OutputStreamManager createManager(final String name, final FactoryData data) { + return new OutputStreamManager(data.os, data.name, data.layout, true); + } + } + + public Target getTarget() { + return target; + } + +} http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/06c55468/log4j-perf/src/main/java/org/apache/logging/log4j/perf/jmh/Log4j2AppenderComparisonBenchmark.java ---------------------------------------------------------------------- diff --git a/log4j-perf/src/main/java/org/apache/logging/log4j/perf/jmh/Log4j2AppenderComparisonBenchmark.java b/log4j-perf/src/main/java/org/apache/logging/log4j/perf/jmh/Log4j2AppenderComparisonBenchmark.java index 0527709..a4ea02e 100644 --- a/log4j-perf/src/main/java/org/apache/logging/log4j/perf/jmh/Log4j2AppenderComparisonBenchmark.java +++ b/log4j-perf/src/main/java/org/apache/logging/log4j/perf/jmh/Log4j2AppenderComparisonBenchmark.java @@ -57,12 +57,14 @@ public class Log4j2AppenderComparisonBenchmark { private Logger rafLogger; private Logger mmapLogger; private Logger consoleLogger; + private Logger fastConsoleLogger; private Logger noopLogger; private Logger rewriteLogger; private Appender fileAppender; private Appender rafAppender; private Appender mmapAppender; private Appender consoleAppender; + private Appender fastConsoleAppender; private Appender noopAppender; private Appender rewriteAppender; @@ -102,6 +104,7 @@ public class Log4j2AppenderComparisonBenchmark { rafLogger = LogManager.getLogger("RAFLogger"); mmapLogger = LogManager.getLogger("MMapLogger"); consoleLogger = LogManager.getLogger("ConsoleLogger"); + fastConsoleLogger = LogManager.getLogger("FastConsoleLogger"); noopLogger = LogManager.getLogger("NoopLogger"); rewriteLogger = LogManager.getLogger("RewriteLogger"); @@ -109,6 +112,7 @@ public class Log4j2AppenderComparisonBenchmark { rafAppender = ((org.apache.logging.log4j.core.Logger) rafLogger).getAppenders().get("RandomAccessFile"); mmapAppender = ((org.apache.logging.log4j.core.Logger) mmapLogger).getAppenders().get("MemoryMappedFile"); consoleAppender = ((org.apache.logging.log4j.core.Logger) consoleLogger).getAppenders().get("Console"); + fastConsoleAppender = ((org.apache.logging.log4j.core.Logger) fastConsoleLogger).getAppenders().get("FastConsole"); noopAppender = ((org.apache.logging.log4j.core.Logger) noopLogger).getAppenders().get("NoOp"); rewriteAppender = ((org.apache.logging.log4j.core.Logger) rewriteLogger).getAppenders().get("Rewrite"); } @@ -218,4 +222,18 @@ public class Log4j2AppenderComparisonBenchmark { public void appenderConsole() { consoleAppender.append(EVENT); } + + @BenchmarkMode(Mode.Throughput) + @OutputTimeUnit(TimeUnit.SECONDS) + @Benchmark + public void end2endFastConsole() { + fastConsoleLogger.debug(MESSAGE); + } + + @BenchmarkMode(Mode.Throughput) + @OutputTimeUnit(TimeUnit.SECONDS) + @Benchmark + public void appenderFastConsole() { + fastConsoleAppender.append(EVENT); + } } http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/06c55468/log4j-perf/src/main/resources/log4j2-appenderComparison.xml ---------------------------------------------------------------------- diff --git a/log4j-perf/src/main/resources/log4j2-appenderComparison.xml b/log4j-perf/src/main/resources/log4j2-appenderComparison.xml index 741a225..8ad6327 100644 --- a/log4j-perf/src/main/resources/log4j2-appenderComparison.xml +++ b/log4j-perf/src/main/resources/log4j2-appenderComparison.xml @@ -21,6 +21,9 @@ <Console name="Console" target="SYSTEM_OUT"> <PatternLayout pattern="%d %p [%t] %c{1} %X{transactionId} - %m%n"/> </Console> + <FastConsole name="FastConsole" target="SYSTEM_OUT"> + <PatternLayout pattern="%d %p [%t] %c{1} %X{transactionId} - %m%n"/> + </FastConsole> <File name="File" fileName="target/testlog4j2.log" immediateFlush="false"> <PatternLayout pattern="%d %p [%t] %c{1} %X{transactionId} - %m%n"/> </File> @@ -55,6 +58,9 @@ <Logger name="ConsoleLogger" level="debug" additivity="false"> <AppenderRef ref="Console"/> </Logger> + <Logger name="FastConsoleLogger" level="debug" additivity="false"> + <AppenderRef ref="FastConsole"/> + </Logger> <Logger name="NoopLogger" level="debug" additivity="false"> <AppenderRef ref="NoOp"/> </Logger> -- Matt Sicker <[email protected]>
