Repository: logging-log4j2
Updated Branches:
  refs/heads/master a2d54a01c -> 183771c87


[LOG4J2-1178] Support use-case for JDBC's
CommonDataSource.setLogWriter(PrintWriter) and
java.sql.DriverManager.setLogWriter(PrintWriter).

Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo
Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/183771c8
Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/183771c8
Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/183771c8

Branch: refs/heads/master
Commit: 183771c87394a5f42511a969fa5784bf6caf594a
Parents: a2d54a0
Author: ggregory <ggreg...@apache.org>
Authored: Sun Nov 1 13:55:42 2015 -0800
Committer: ggregory <ggreg...@apache.org>
Committed: Sun Nov 1 13:55:42 2015 -0800

----------------------------------------------------------------------
 .../apache/logging/log4j/core/StringLayout.java |  33 ++++
 .../core/appender/AbstractWriterAppender.java   | 124 +++++++++++++
 .../log4j/core/appender/WriterAppender.java     | 183 +++++++++++++++++++
 .../log4j/core/layout/AbstractStringLayout.java |   6 +-
 .../log4j/core/layout/PatternLayout.java        |  24 ++-
 .../log4j/core/util/CloseShieldWriter.java      |  30 +++
 .../core/appender/ConfigurationTestUtils.java   |  37 ++++
 .../log4j/core/appender/WriterAppenderTest.java | 104 +++++++++++
 src/changes/changes.xml                         |   3 +
 src/site/site.xml                               |   1 +
 src/site/xdoc/manual/customconfig.xml           |  48 +++++
 11 files changed, 586 insertions(+), 7 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/183771c8/log4j-core/src/main/java/org/apache/logging/log4j/core/StringLayout.java
----------------------------------------------------------------------
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/StringLayout.java 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/StringLayout.java
new file mode 100644
index 0000000..3eb99e8
--- /dev/null
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/StringLayout.java
@@ -0,0 +1,33 @@
+/*
+ * 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;
+
+import java.nio.charset.Charset;
+
+/**
+ * Instantiates the @{link Layout} type for String-based layouts.
+ */
+public interface StringLayout extends Layout<String> {
+
+    /**
+     * Gets the Charset this layout uses to encode Strings into bytes.
+     * 
+     * @return the Charset this layout uses to encode Strings into bytes.
+     */
+    Charset getCharset();
+
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/183771c8/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/AbstractWriterAppender.java
----------------------------------------------------------------------
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/AbstractWriterAppender.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/AbstractWriterAppender.java
new file mode 100644
index 0000000..2346d7d
--- /dev/null
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/AbstractWriterAppender.java
@@ -0,0 +1,124 @@
+/*
+ * 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.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+import org.apache.logging.log4j.core.Filter;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.StringLayout;
+
+/**
+ * Appends log events as strings to a writer.
+ * 
+ * @param <M>
+ *            The kind of {@link WriterManager} under management
+ */
+public abstract class AbstractWriterAppender<M extends WriterManager> extends 
AbstractAppender {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * Immediate flush means that the underlying writer will be flushed at the
+     * end of each append operation. Immediate flush is slower but ensures that
+     * each append request is actually written. If <code>immediateFlush</code>
+     * is set to {@code false}, then there is a good chance that the last few
+     * logs events are not actually written to persistent media if and when the
+     * application crashes.
+     */
+    protected final boolean immediateFlush;
+    private final M manager;
+    private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
+    private final Lock readLock = readWriteLock.readLock();
+
+    /**
+     * Instantiates.
+     * 
+     * @param name
+     *            The name of the Appender.
+     * @param layout
+     *            The layout to format the message.
+     * @param manager
+     *            The OutputStreamManager.
+     */
+    protected AbstractWriterAppender(final String name, final StringLayout 
layout, final Filter filter,
+            final boolean ignoreExceptions, final boolean immediateFlush, 
final M manager) {
+        super(name, filter, layout, ignoreExceptions);
+        this.manager = manager;
+        this.immediateFlush = immediateFlush;
+    }
+
+    /**
+     * Actual writing occurs here.
+     * <p>
+     * Most subclasses will need to override this method.
+     * </p>
+     * 
+     * @param event
+     *            The LogEvent.
+     */
+    @Override
+    public void append(final LogEvent event) {
+        readLock.lock();
+        try {
+            final String str = getStringLayout().toSerializable(event);
+            if (str.length() > 0) {
+                manager.write(str);
+                if (this.immediateFlush || event.isEndOfBatch()) {
+                    manager.flush();
+                }
+            }
+        } catch (final AppenderLoggingException ex) {
+            error("Unable to write " + manager.getName() + " for appender " + 
getName() + ": " + ex);
+            throw ex;
+        } finally {
+            readLock.unlock();
+        }
+    }
+
+    /**
+     * Gets the manager.
+     * 
+     * @return the manager.
+     */
+    public M getManager() {
+        return manager;
+    }
+
+    public StringLayout getStringLayout() {
+        return (StringLayout) getLayout();
+    }
+
+    @Override
+    public void start() {
+        if (getLayout() == null) {
+            LOGGER.error("No layout set for the appender named [{}].", 
getName());
+        }
+        if (manager == null) {
+            LOGGER.error("No OutputStreamManager set for the appender named 
[{}].", getName());
+        }
+        super.start();
+    }
+
+    @Override
+    public void stop() {
+        super.stop();
+        manager.release();
+    }
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/183771c8/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/WriterAppender.java
----------------------------------------------------------------------
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/WriterAppender.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/WriterAppender.java
new file mode 100644
index 0000000..e3d8ecb
--- /dev/null
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/WriterAppender.java
@@ -0,0 +1,183 @@
+/*
+ * 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.Writer;
+
+import org.apache.logging.log4j.core.Filter;
+import org.apache.logging.log4j.core.StringLayout;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory;
+import org.apache.logging.log4j.core.config.plugins.PluginFactory;
+import org.apache.logging.log4j.core.layout.PatternLayout;
+import org.apache.logging.log4j.core.util.CloseShieldWriter;
+
+/**
+ * Appends log events to a {@link Writer}.
+ */
+@Plugin(name = "Writer", category = "Core", elementType = "appender", 
printObject = true)
+public final class WriterAppender extends 
AbstractWriterAppender<WriterManager> {
+
+    /**
+     * Builds WriterAppender instances.
+     */
+    public static class Builder implements 
org.apache.logging.log4j.core.util.Builder<WriterAppender> {
+
+        private Filter filter;
+
+        private boolean follow = false;
+
+        private boolean ignoreExceptions = true;
+
+        private StringLayout layout = PatternLayout.createDefaultLayout();
+
+        private String name;
+
+        private Writer target;
+
+        @Override
+        public WriterAppender build() {
+            return new WriterAppender(name, layout, filter, getManager(target, 
follow, layout), ignoreExceptions);
+        }
+
+        public Builder setFilter(final Filter aFilter) {
+            this.filter = aFilter;
+            return this;
+        }
+
+        public Builder setFollow(final boolean shouldFollow) {
+            this.follow = shouldFollow;
+            return this;
+        }
+
+        public Builder setIgnoreExceptions(final boolean 
shouldIgnoreExceptions) {
+            this.ignoreExceptions = shouldIgnoreExceptions;
+            return this;
+        }
+
+        public Builder setLayout(final StringLayout aLayout) {
+            this.layout = aLayout;
+            return this;
+        }
+
+        public Builder setName(final String aName) {
+            this.name = aName;
+            return this;
+        }
+
+        public Builder setTarget(final Writer aTarget) {
+            this.target = aTarget;
+            return this;
+        }
+    }
+    /**
+     * Holds data to pass to factory method.
+     */
+    private static class FactoryData {
+        private final StringLayout layout;
+        private final String name;
+        private final Writer writer;
+
+        /**
+         * Builds instances.
+         * 
+         * @param writer
+         *            The OutputStream.
+         * @param type
+         *            The name of the target.
+         * @param layout
+         *            A String layout
+         */
+        public FactoryData(final Writer writer, final String type, final 
StringLayout layout) {
+            this.writer = writer;
+            this.name = type;
+            this.layout = layout;
+        }
+    }
+
+    private static class WriterManagerFactory implements 
ManagerFactory<WriterManager, FactoryData> {
+
+        /**
+         * Creates a WriterManager.
+         * 
+         * @param name
+         *            The name of the entity to manage.
+         * @param data
+         *            The data required to create the entity.
+         * @return The WriterManager
+         */
+        @Override
+        public WriterManager createManager(final String name, final 
FactoryData data) {
+            return new WriterManager(data.writer, data.name, data.layout, 
true);
+        }
+    }
+
+    private static WriterManagerFactory factory = new WriterManagerFactory();
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * Creates a WriterAppender.
+     * 
+     * @param layout
+     *            The layout to use or null to get the default layout.
+     * @param filter
+     *            The Filter or null.
+     * @param target
+     *            The target Writer
+     * @param follow
+     *            If true will follow changes to the underlying output stream.
+     *            Use false as the default.
+     * @param name
+     *            The name of the Appender (required).
+     * @param ignore
+     *            If {@code "true"} (default) exceptions encountered when
+     *            appending events are logged; otherwise they are propagated to
+     *            the caller. Use true as the default.
+     * @return The ConsoleAppender.
+     */
+    @PluginFactory
+    public static WriterAppender createAppender(StringLayout layout, final 
Filter filter, final Writer target,
+            final String name, final boolean follow, final boolean ignore) {
+        if (name == null) {
+            LOGGER.error("No name provided for WriterAppender");
+            return null;
+        }
+        if (layout == null) {
+            layout = PatternLayout.createDefaultLayout();
+        }
+        return new WriterAppender(name, layout, filter, getManager(target, 
follow, layout), ignore);
+    }
+
+    private static WriterManager getManager(final Writer target, final boolean 
follow, final StringLayout layout) {
+        final Writer writer = new CloseShieldWriter(target);
+        final String managerName = target.getClass().getName() + "@" + 
Integer.toHexString(target.hashCode()) + '.'
+                + follow;
+        return WriterManager.getManager(managerName, new FactoryData(writer, 
managerName, layout), factory);
+    }
+
+    @PluginBuilderFactory
+    public static Builder newBuilder() {
+        return new Builder();
+    }
+
+    private WriterAppender(final String name, final StringLayout layout, final 
Filter filter,
+            final WriterManager manager, final boolean ignoreExceptions) {
+        super(name, layout, filter, ignoreExceptions, true, manager);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/183771c8/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/AbstractStringLayout.java
----------------------------------------------------------------------
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/AbstractStringLayout.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/AbstractStringLayout.java
index 9de19e6..2bfa371 100644
--- 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/AbstractStringLayout.java
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/AbstractStringLayout.java
@@ -24,6 +24,7 @@ import java.nio.charset.Charset;
 import java.nio.charset.StandardCharsets;
 
 import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.StringLayout;
 
 /**
  * Abstract base class for Layouts that result in a String.
@@ -35,7 +36,7 @@ import org.apache.logging.log4j.core.LogEvent;
  * Implementation note: prefer String.getBytes(String) to 
String.getBytes(Charset) for performance reasons. See
  * https://issues.apache.org/jira/browse/LOG4J2-935 for details.
  */
-public abstract class AbstractStringLayout extends AbstractLayout<String> {
+public abstract class AbstractStringLayout extends AbstractLayout<String> 
implements StringLayout {
 
     /**
      * Default length for new StringBuilder instances: {@value} .
@@ -201,7 +202,8 @@ public abstract class AbstractStringLayout extends 
AbstractLayout<String> {
         return byteOffset;
     }
 
-    protected Charset getCharset() {
+    @Override
+    public Charset getCharset() {
         return charset;
     }
 

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/183771c8/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/PatternLayout.java
----------------------------------------------------------------------
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/PatternLayout.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/PatternLayout.java
index b6dbfd8..c78a7b7 100644
--- 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/PatternLayout.java
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/PatternLayout.java
@@ -84,7 +84,7 @@ public final class PatternLayout extends AbstractStringLayout 
{
     /**
      * Initial converter for pattern.
      */
-    private final PatternFormatter[] patternFormatters;
+    private final PatternFormatter[] formatters;
 
     /**
      * Conversion pattern.
@@ -138,12 +138,12 @@ public final class PatternLayout extends 
AbstractStringLayout {
             try {
                 List<PatternFormatter> list = parser.parse(pattern == null ? 
DEFAULT_CONVERSION_PATTERN : pattern,
                         this.alwaysWriteExceptions, this.noConsoleNoAnsi);
-                this.patternFormatters = list.toArray(new PatternFormatter[0]);
+                this.formatters = list.toArray(new PatternFormatter[0]);
             } catch (RuntimeException ex) {
                 throw new IllegalArgumentException("Cannot parse pattern '" + 
pattern + "'", ex);
             }
         } else {
-            this.patternFormatters = null;
+            this.formatters = null;
             serializer = new PatternSelectorSerializer();
         }
     }
@@ -286,9 +286,9 @@ public final class PatternLayout extends 
AbstractStringLayout {
         @Override
         public String toSerializable(final LogEvent event) {
             final StringBuilder buf = getStringBuilder();
-            final int len = patternFormatters.length;
+            final int len = formatters.length;
             for (int i = 0; i < len; i++) {
-                patternFormatters[i].format(event, buf);
+                formatters[i].format(event, buf);
             }
             String str = buf.toString();
             if (replace != null) {
@@ -327,6 +327,20 @@ public final class PatternLayout extends 
AbstractStringLayout {
     }
 
     /**
+     * Creates a PatternLayout using the default options and the given
+     * configuration. These options include using UTF-8, the default conversion
+     * pattern, exceptions being written, and with ANSI escape codes.
+     * 
+     * @param configuration The Configuration.
+     *
+     * @return the PatternLayout.
+     * @see #DEFAULT_CONVERSION_PATTERN Default conversion pattern
+     */
+    public static PatternLayout createDefaultLayout(Configuration 
configuration) {
+        return newBuilder().withConfiguration(configuration).build();
+    }
+
+    /**
      * Creates a builder for a custom PatternLayout.
      * @return a PatternLayout builder.
      */

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/183771c8/log4j-core/src/main/java/org/apache/logging/log4j/core/util/CloseShieldWriter.java
----------------------------------------------------------------------
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/CloseShieldWriter.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/CloseShieldWriter.java
new file mode 100644
index 0000000..6239d41
--- /dev/null
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/CloseShieldWriter.java
@@ -0,0 +1,30 @@
+package org.apache.logging.log4j.core.util;
+
+import java.io.IOException;
+import java.io.Writer;
+
+public class CloseShieldWriter extends Writer {
+
+    private final Writer delegate;
+
+    public CloseShieldWriter(final Writer delegate) {
+        this.delegate = delegate;
+    }
+
+    @Override
+    public void close() throws IOException {
+        // do not close delegate
+    }
+
+    @Override
+    public void flush() throws IOException {
+        delegate.flush();
+
+    }
+
+    @Override
+    public void write(final char[] cbuf, final int off, final int len) throws 
IOException {
+        delegate.write(cbuf, off, len);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/183771c8/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/ConfigurationTestUtils.java
----------------------------------------------------------------------
diff --git 
a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/ConfigurationTestUtils.java
 
b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/ConfigurationTestUtils.java
new file mode 100644
index 0000000..c0e4dc8
--- /dev/null
+++ 
b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/ConfigurationTestUtils.java
@@ -0,0 +1,37 @@
+/*
+ * 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 org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.core.Appender;
+import org.apache.logging.log4j.core.Filter;
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.core.config.LoggerConfig;
+
+public class ConfigurationTestUtils {
+
+    static void updateLoggers(final Appender appender, final Configuration 
config) {
+        final Level level = null;
+        final Filter filter = null;
+        for (final LoggerConfig loggerConfig : config.getLoggers().values()) {
+            loggerConfig.addAppender(appender, level, filter);
+        }
+        config.getRootLogger().addAppender(appender, level, filter);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/183771c8/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/WriterAppenderTest.java
----------------------------------------------------------------------
diff --git 
a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/WriterAppenderTest.java
 
b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/WriterAppenderTest.java
new file mode 100644
index 0000000..fff28e2
--- /dev/null
+++ 
b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/WriterAppenderTest.java
@@ -0,0 +1,104 @@
+/*
+ * 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.ByteArrayOutputStream;
+import java.io.CharArrayWriter;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.sql.SQLException;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.Appender;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.core.layout.PatternLayout;
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestName;
+
+/**
+ * Tests {@link WriterAppender}.
+ */
+public class WriterAppenderTest {
+
+    private static final String TEST_MSG = "FOO ERROR";
+
+    @Rule
+    public TestName testName = new TestName();
+
+    private String getName(final Writer writer) {
+        return writer.getClass().getSimpleName() + "." + 
testName.getMethodName();
+    }
+
+    private void test(final ByteArrayOutputStream out, final Writer writer) 
throws SQLException {
+        final String name = getName(writer);
+        addAppender(writer, name);
+        final Logger logger = LogManager.getLogger(name);
+        logger.error(TEST_MSG);
+        final String actual = out.toString();
+        Assert.assertTrue(actual, actual.contains(TEST_MSG));
+    }
+
+    private void test(final Writer writer) throws SQLException {
+        final String name = getName(writer);
+        addAppender(writer, name);
+        final Logger logger = LogManager.getLogger(name);
+        logger.error(TEST_MSG);
+        final String actual = writer.toString();
+        Assert.assertTrue(actual, actual.contains(TEST_MSG));
+    }
+
+    private void addAppender(final Writer writer, final String writerName) {
+        final LoggerContext context = LoggerContext.getContext(false);
+        final Configuration config = context.getConfiguration();
+        final PatternLayout layout = PatternLayout.createDefaultLayout(config);
+        final Appender appender = WriterAppender.createAppender(layout, null, 
writer, writerName, false, true);
+        appender.start();
+        config.addAppender(appender);
+        ConfigurationTestUtils.updateLoggers(appender, config);
+    }
+
+    @Test
+    public void testWriterAppenderToCharArrayWriter() throws SQLException {
+        test(new CharArrayWriter());
+    }
+
+    @Test
+    public void testWriterAppenderToOutputStreamWriter() throws SQLException {
+        final ByteArrayOutputStream out = new ByteArrayOutputStream();
+        final Writer writer = new OutputStreamWriter(out);
+        test(out, writer);
+    }
+
+    @Test
+    public void testWriterAppenderToPrintWriter() throws SQLException {
+        final ByteArrayOutputStream out = new ByteArrayOutputStream();
+        final Writer writer = new PrintWriter(out);
+        test(out, writer);
+    }
+
+    @Test
+    public void testWriterAppenderToStringWriter() throws SQLException {
+        test(new StringWriter());
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/183771c8/src/changes/changes.xml
----------------------------------------------------------------------
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 54c352f..1911c3c 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -36,6 +36,9 @@
       <action issue="LOG4J2-898" dev="rpopma" type="add">
         Added system property to allow users to control whether messages 
should be formatted in the background.
       </action>
+      <action issue="LOG4J2-1178" dev="ggregory" type="add">
+        Support use-case for JDBC's CommonDataSource.setLogWriter(PrintWriter) 
and java.sql.DriverManager.setLogWriter(PrintWriter).
+      </action>
       <action issue="LOG4J2-879" dev="rpopma" type="fix">
         Documentation: fixed minor issues with the site and manual pages.
       </action>

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/183771c8/src/site/site.xml
----------------------------------------------------------------------
diff --git a/src/site/site.xml b/src/site/site.xml
index e6cb6e8..0cb6f9c 100644
--- a/src/site/site.xml
+++ b/src/site/site.xml
@@ -196,6 +196,7 @@
         <item name="Using Configurator" 
href="/manual/customconfig.html#Configurator"/>
         <item name="Config File and Code" 
href="/manual/customconfig.html#Hybrid"/>
         <item name="After Initialization" 
href="/manual/customconfig.html#AddingToCurrent"/>
+        <item name="Appending to Writers &amp; OutputStreams" 
href="/manual/customconfig.html#AppendingToWritersAndOutputStreams"/>        
       </item>
 
       <item name="Custom Log Levels" href="/manual/customloglevels.html" 
collapse="true">

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/183771c8/src/site/xdoc/manual/customconfig.xml
----------------------------------------------------------------------
diff --git a/src/site/xdoc/manual/customconfig.xml 
b/src/site/xdoc/manual/customconfig.xml
index d34da41..485e93a 100644
--- a/src/site/xdoc/manual/customconfig.xml
+++ b/src/site/xdoc/manual/customconfig.xml
@@ -282,7 +282,55 @@ public class MyXMLConfiguration extends XMLConfiguration {
         ctx.updateLoggers();
 }]]></pre>
           </subsection>
+        <a name="AppendingToWritersAndOutputStreams"/>
+        <subsection name="Appending Log Events to Writers and OutputStreams 
Programmatically">
+            <p>
+              Log4j 2.5 provides facilities to append log events to Writers 
and OutputStreams. For example, this 
+              provides simple integration for JDBC Driver implementors that 
use Log4j internally and still want 
+              to support the JDBC APIs 
<code>CommonDataSource.setLogWriter(PrintWriter)</code>, 
+              <code>java.sql.DriverManager.setLogWriter(PrintWriter)</code>, 
and 
+              <code>java.sql.DriverManager.setLogStream(PrintStream)</code>.   
           
+            </p>
+            <p>
+              Given any <code>Writer</code>, like a <code>PrintWriter</code>, 
you tell Log4j to append events to 
+              that writer by creating a <code>WriterAppender</code> and 
updating the Log4j configuration:
+            </p>
+            <pre class="prettyprint linenums"><![CDATA[
+void addAppender(final Writer writer, final String writerName) {
+    final LoggerContext context = LoggerContext.getContext(false);
+    final Configuration config = context.getConfiguration();
+    final PatternLayout layout = PatternLayout.createDefaultLayout(config);
+    final Appender appender = WriterAppender.createAppender(layout, null, 
writer, writerName, false, true);
+    appender.start();
+    config.addAppender(appender);
+    updateLoggers(appender, config);
+}
 
+private void updateLoggers(final Appender appender, final Configuration 
config) {
+    final Level level = null;
+    final Filter filter = null;
+    for (final LoggerConfig loggerConfig : config.getLoggers().values()) {
+        loggerConfig.addAppender(appender, level, filter);
+    }
+    config.getRootLogger().addAppender(appender, level, filter);
+}]]></pre>
+            <p>
+              You can achieve the same effect with an 
<code>OutputStream</code>, like a <code>PrintStream</code>:
+            </p>            
+            <pre class="prettyprint linenums"><![CDATA[
+void addAppender(final OutputStream outputStream, final String 
outputStreamName) {
+    final LoggerContext context = LoggerContext.getContext(false);
+    final Configuration config = context.getConfiguration();
+    final PatternLayout layout = PatternLayout.createDefaultLayout(config);
+    final Appender appender = OutputStreamAppender.createAppender(layout, 
null, outputStream, outputStreamName, false, true);
+    appender.start();
+    config.addAppender(appender);
+    updateLoggers(appender, config);
+}]]></pre>
+        </subsection>
+            <p>
+              The difference is the use of <code>OutputStreamAppender</code> 
instead of <code>WriterAppender</code>.
+            </p>            
       </section>
 
     </body>

Reply via email to