001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache license, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the license for the specific language governing permissions and
015 * limitations under the license.
016 */
017package org.apache.logging.log4j.core.appender;
018
019import java.io.Writer;
020
021import org.apache.logging.log4j.core.Filter;
022import org.apache.logging.log4j.core.StringLayout;
023import org.apache.logging.log4j.core.config.plugins.Plugin;
024import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory;
025import org.apache.logging.log4j.core.config.plugins.PluginFactory;
026import org.apache.logging.log4j.core.layout.PatternLayout;
027import org.apache.logging.log4j.core.util.CloseShieldWriter;
028
029/**
030 * Appends log events to a {@link Writer}.
031 */
032@Plugin(name = "Writer", category = "Core", elementType = "appender", printObject = true)
033public final class WriterAppender extends AbstractWriterAppender<WriterManager> {
034
035    /**
036     * Builds WriterAppender instances.
037     */
038    public static class Builder implements org.apache.logging.log4j.core.util.Builder<WriterAppender> {
039
040        private Filter filter;
041
042        private boolean follow = false;
043
044        private boolean ignoreExceptions = true;
045
046        private StringLayout layout = PatternLayout.createDefaultLayout();
047
048        private String name;
049
050        private Writer target;
051
052        @Override
053        public WriterAppender build() {
054            return new WriterAppender(name, layout, filter, getManager(target, follow, layout), ignoreExceptions);
055        }
056
057        public Builder setFilter(final Filter aFilter) {
058            this.filter = aFilter;
059            return this;
060        }
061
062        public Builder setFollow(final boolean shouldFollow) {
063            this.follow = shouldFollow;
064            return this;
065        }
066
067        public Builder setIgnoreExceptions(final boolean shouldIgnoreExceptions) {
068            this.ignoreExceptions = shouldIgnoreExceptions;
069            return this;
070        }
071
072        public Builder setLayout(final StringLayout aLayout) {
073            this.layout = aLayout;
074            return this;
075        }
076
077        public Builder setName(final String aName) {
078            this.name = aName;
079            return this;
080        }
081
082        public Builder setTarget(final Writer aTarget) {
083            this.target = aTarget;
084            return this;
085        }
086    }
087    /**
088     * Holds data to pass to factory method.
089     */
090    private static class FactoryData {
091        private final StringLayout layout;
092        private final String name;
093        private final Writer writer;
094
095        /**
096         * Builds instances.
097         * 
098         * @param writer
099         *            The OutputStream.
100         * @param type
101         *            The name of the target.
102         * @param layout
103         *            A String layout
104         */
105        public FactoryData(final Writer writer, final String type, final StringLayout layout) {
106            this.writer = writer;
107            this.name = type;
108            this.layout = layout;
109        }
110    }
111
112    private static class WriterManagerFactory implements ManagerFactory<WriterManager, FactoryData> {
113
114        /**
115         * Creates a WriterManager.
116         * 
117         * @param name
118         *            The name of the entity to manage.
119         * @param data
120         *            The data required to create the entity.
121         * @return The WriterManager
122         */
123        @Override
124        public WriterManager createManager(final String name, final FactoryData data) {
125            return new WriterManager(data.writer, data.name, data.layout, true);
126        }
127    }
128
129    private static WriterManagerFactory factory = new WriterManagerFactory();
130
131    /**
132     * Creates a WriterAppender.
133     * 
134     * @param layout
135     *            The layout to use or null to get the default layout.
136     * @param filter
137     *            The Filter or null.
138     * @param target
139     *            The target Writer
140     * @param follow
141     *            If true will follow changes to the underlying output stream.
142     *            Use false as the default.
143     * @param name
144     *            The name of the Appender (required).
145     * @param ignore
146     *            If {@code "true"} (default) exceptions encountered when
147     *            appending events are logged; otherwise they are propagated to
148     *            the caller. Use true as the default.
149     * @return The ConsoleAppender.
150     */
151    @PluginFactory
152    public static WriterAppender createAppender(StringLayout layout, final Filter filter, final Writer target,
153            final String name, final boolean follow, final boolean ignore) {
154        if (name == null) {
155            LOGGER.error("No name provided for WriterAppender");
156            return null;
157        }
158        if (layout == null) {
159            layout = PatternLayout.createDefaultLayout();
160        }
161        return new WriterAppender(name, layout, filter, getManager(target, follow, layout), ignore);
162    }
163
164    private static WriterManager getManager(final Writer target, final boolean follow, final StringLayout layout) {
165        final Writer writer = new CloseShieldWriter(target);
166        final String managerName = target.getClass().getName() + "@" + Integer.toHexString(target.hashCode()) + '.'
167                + follow;
168        return WriterManager.getManager(managerName, new FactoryData(writer, managerName, layout), factory);
169    }
170
171    @PluginBuilderFactory
172    public static Builder newBuilder() {
173        return new Builder();
174    }
175
176    private WriterAppender(final String name, final StringLayout layout, final Filter filter,
177            final WriterManager manager, final boolean ignoreExceptions) {
178        super(name, layout, filter, ignoreExceptions, true, manager);
179    }
180
181}