[LOG4J2-1187] Support use case for java.sql.DriverManager.setLogStream(PrintStream).
Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/449219b3 Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/449219b3 Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/449219b3 Branch: refs/heads/LOG4J-1181 Commit: 449219b3e809eb19af16690a660a4b8205343125 Parents: 183771c Author: ggregory <ggreg...@apache.org> Authored: Sun Nov 1 13:57:01 2015 -0800 Committer: ggregory <ggreg...@apache.org> Committed: Sun Nov 1 13:57:01 2015 -0800 ---------------------------------------------------------------------- .../core/appender/OutputStreamAppender.java | 191 +++++++++++++++++++ .../core/appender/OutputStreamAppenderTest.java | 103 ++++++++++ src/changes/changes.xml | 3 + 3 files changed, 297 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/449219b3/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/OutputStreamAppender.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/OutputStreamAppender.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/OutputStreamAppender.java new file mode 100644 index 0000000..8afb9b8 --- /dev/null +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/OutputStreamAppender.java @@ -0,0 +1,191 @@ +/* + * 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.OutputStream; +import java.io.Serializable; + +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.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.CloseShieldOutputStream; + +/** + * Appends log events to a given output stream using a layout. + * <p> + * Character encoding is handled within the Layout. + * </p> + */ +@Plugin(name = "OutputStream", category = "Core", elementType = "appender", printObject = true) +public final class OutputStreamAppender extends AbstractOutputStreamAppender<OutputStreamManager> { + + /** + * Builds OutputStreamAppender instances. + */ + public static class Builder implements org.apache.logging.log4j.core.util.Builder<OutputStreamAppender> { + + private Filter filter; + + private boolean follow = false; + + private boolean ignoreExceptions = true; + + private Layout<? extends Serializable> layout = PatternLayout.createDefaultLayout(); + + private String name; + + private OutputStream target; + + @Override + public OutputStreamAppender build() { + return new OutputStreamAppender(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 Layout<? extends Serializable> aLayout) { + this.layout = aLayout; + return this; + } + + public Builder setName(final String aName) { + this.name = aName; + return this; + } + + public Builder setTarget(final OutputStream aTarget) { + this.target = aTarget; + return this; + } + } + /** + * Holds data to pass to factory method. + */ + private static class FactoryData { + private final Layout<? extends Serializable> layout; + private final String name; + private final OutputStream os; + + /** + * Builds instances. + * + * @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; + } + } + + /** + * Creates the manager. + */ + private static class OutputStreamManagerFactory implements ManagerFactory<OutputStreamManager, FactoryData> { + + /** + * Creates 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); + } + } + + private static OutputStreamManagerFactory factory = new OutputStreamManagerFactory(); + + private static final long serialVersionUID = 1L; + + /** + * Creates an OutputStream Appender. + * + * @param layout + * The layout to use or null to get the default layout. + * @param filter + * The Filter or null. + * @param target + * an output stream. + * @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 OutputStreamAppender createAppender(Layout<? extends Serializable> layout, final Filter filter, + final OutputStream target, final String name, final boolean follow, final boolean ignore) { + if (name == null) { + LOGGER.error("No name provided for OutputStreamAppender"); + return null; + } + if (layout == null) { + layout = PatternLayout.createDefaultLayout(); + } + return new OutputStreamAppender(name, layout, filter, getManager(target, follow, layout), ignore); + } + + private static OutputStreamManager getManager(final OutputStream target, final boolean follow, + final Layout<? extends Serializable> layout) { + final OutputStream os = new CloseShieldOutputStream(target); + final String managerName = target.getClass().getName() + "@" + Integer.toHexString(target.hashCode()) + '.' + + follow; + return OutputStreamManager.getManager(managerName, new FactoryData(os, managerName, layout), factory); + } + + @PluginBuilderFactory + public static Builder newBuilder() { + return new Builder(); + } + + private OutputStreamAppender(final String name, final Layout<? extends Serializable> layout, final Filter filter, + final OutputStreamManager manager, final boolean ignoreExceptions) { + super(name, layout, filter, ignoreExceptions, true, manager); + } + +} http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/449219b3/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/OutputStreamAppenderTest.java ---------------------------------------------------------------------- diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/OutputStreamAppenderTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/OutputStreamAppenderTest.java new file mode 100644 index 0000000..206e98b --- /dev/null +++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/OutputStreamAppenderTest.java @@ -0,0 +1,103 @@ +/* + * 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.BufferedOutputStream; +import java.io.ByteArrayOutputStream; +import java.io.OutputStream; +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.Layout; +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 OutputStreamAppender}. + */ +public class OutputStreamAppenderTest { + + private static final String TEST_MSG = "FOO ERROR"; + + @Rule + public TestName testName = new TestName(); + + private String getName(final OutputStream out) { + return out.getClass().getSimpleName() + "." + testName.getMethodName(); + } + + /** + * Tests that you can add an output stream appender dynamically. + */ + private 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); + ConfigurationTestUtils.updateLoggers(appender, config); + } + + @Test + public void testOutputStreamAppenderToBufferedOutputStream() throws SQLException { + final ByteArrayOutputStream out = new ByteArrayOutputStream(); + final OutputStream os = new BufferedOutputStream(out); + final String name = getName(out); + final Logger logger = LogManager.getLogger(name); + addAppender(os, name); + logger.error(TEST_MSG); + final String actual = out.toString(); + Assert.assertTrue(actual, actual.contains(TEST_MSG)); + } + + @Test + public void testOutputStreamAppenderToByteArrayOutputStream() throws SQLException { + final OutputStream out = new ByteArrayOutputStream(); + final String name = getName(out); + final Logger logger = LogManager.getLogger(name); + addAppender(out, name); + logger.error(TEST_MSG); + final String actual = out.toString(); + Assert.assertTrue(actual, actual.contains(TEST_MSG)); + } + + /** + * Validates that the code pattern we use to add an appender on the fly + * works with a basic appender that is not the new OutputStream appender or + * new Writer appender. + */ + @Test + public void testUpdatePatternWithFileAppender() { + final LoggerContext ctx = (LoggerContext) LogManager.getContext(false); + final Configuration config = ctx.getConfiguration(); + final Layout<?> layout = PatternLayout.createDefaultLayout(); + final Appender appender = FileAppender.createAppender("target/" + getClass().getName() + ".log", "false", + "false", "File", "true", "false", "false", "4000", layout, null, "false", null, config); + appender.start(); + config.addAppender(appender); + ConfigurationTestUtils.updateLoggers(appender, config); + LogManager.getLogger().error("FOO MSG"); + } +} http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/449219b3/src/changes/changes.xml ---------------------------------------------------------------------- diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 1911c3c..a19805b 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -39,6 +39,9 @@ <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-1187" dev="ggregory" type="add"> + Support use case for java.sql.DriverManager.setLogStream(PrintStream). + </action> <action issue="LOG4J2-879" dev="rpopma" type="fix"> Documentation: fixed minor issues with the site and manual pages. </action>