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 & 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>