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.Appender;
022import org.apache.logging.log4j.core.Core;
023import org.apache.logging.log4j.core.Filter;
024import org.apache.logging.log4j.core.StringLayout;
025import org.apache.logging.log4j.core.config.plugins.Plugin;
026import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory;
027import org.apache.logging.log4j.core.config.plugins.PluginFactory;
028import org.apache.logging.log4j.core.layout.PatternLayout;
029import org.apache.logging.log4j.core.util.CloseShieldWriter;
030
031/**
032 * Appends log events to a {@link Writer}.
033 */
034@Plugin(name = "Writer", category = Core.CATEGORY_NAME, elementType = Appender.ELEMENT_TYPE, printObject = true)
035public final class WriterAppender extends AbstractWriterAppender<WriterManager> {
036
037    /**
038     * Builds WriterAppender instances.
039     */
040    public static class Builder implements org.apache.logging.log4j.core.util.Builder<WriterAppender> {
041
042        private Filter filter;
043
044        private boolean follow = false;
045
046        private boolean ignoreExceptions = true;
047
048        private StringLayout layout = PatternLayout.createDefaultLayout();
049
050        private String name;
051
052        private Writer target;
053
054        @Override
055        public WriterAppender build() {
056            return new WriterAppender(name, layout, filter, getManager(target, follow, layout), ignoreExceptions);
057        }
058
059        public Builder setFilter(final Filter aFilter) {
060            this.filter = aFilter;
061            return this;
062        }
063
064        public Builder setFollow(final boolean shouldFollow) {
065            this.follow = shouldFollow;
066            return this;
067        }
068
069        public Builder setIgnoreExceptions(final boolean shouldIgnoreExceptions) {
070            this.ignoreExceptions = shouldIgnoreExceptions;
071            return this;
072        }
073
074        public Builder setLayout(final StringLayout aLayout) {
075            this.layout = aLayout;
076            return this;
077        }
078
079        public Builder setName(final String aName) {
080            this.name = aName;
081            return this;
082        }
083
084        public Builder setTarget(final Writer aTarget) {
085            this.target = aTarget;
086            return this;
087        }
088    }
089    /**
090     * Holds data to pass to factory method.
091     */
092    private static class FactoryData {
093        private final StringLayout layout;
094        private final String name;
095        private final Writer writer;
096
097        /**
098         * Builds instances.
099         * 
100         * @param writer
101         *            The OutputStream.
102         * @param type
103         *            The name of the target.
104         * @param layout
105         *            A String layout
106         */
107        public FactoryData(final Writer writer, final String type, final StringLayout layout) {
108            this.writer = writer;
109            this.name = type;
110            this.layout = layout;
111        }
112    }
113
114    private static class WriterManagerFactory implements ManagerFactory<WriterManager, FactoryData> {
115
116        /**
117         * Creates a WriterManager.
118         * 
119         * @param name
120         *            The name of the entity to manage.
121         * @param data
122         *            The data required to create the entity.
123         * @return The WriterManager
124         */
125        @Override
126        public WriterManager createManager(final String name, final FactoryData data) {
127            return new WriterManager(data.writer, data.name, data.layout, true);
128        }
129    }
130
131    private static WriterManagerFactory factory = new WriterManagerFactory();
132
133    /**
134     * Creates a WriterAppender.
135     * 
136     * @param layout
137     *            The layout to use or null to get the default layout.
138     * @param filter
139     *            The Filter or null.
140     * @param target
141     *            The target Writer
142     * @param follow
143     *            If true will follow changes to the underlying output stream.
144     *            Use false as the default.
145     * @param name
146     *            The name of the Appender (required).
147     * @param ignore
148     *            If {@code "true"} (default) exceptions encountered when
149     *            appending events are logged; otherwise they are propagated to
150     *            the caller. Use true as the default.
151     * @return The ConsoleAppender.
152     */
153    @PluginFactory
154    public static WriterAppender createAppender(StringLayout layout, final Filter filter, final Writer target,
155            final String name, final boolean follow, final boolean ignore) {
156        if (name == null) {
157            LOGGER.error("No name provided for WriterAppender");
158            return null;
159        }
160        if (layout == null) {
161            layout = PatternLayout.createDefaultLayout();
162        }
163        return new WriterAppender(name, layout, filter, getManager(target, follow, layout), ignore);
164    }
165
166    private static WriterManager getManager(final Writer target, final boolean follow, final StringLayout layout) {
167        final Writer writer = new CloseShieldWriter(target);
168        final String managerName = target.getClass().getName() + "@" + Integer.toHexString(target.hashCode()) + '.'
169                + follow;
170        return WriterManager.getManager(managerName, new FactoryData(writer, managerName, layout), factory);
171    }
172
173    @PluginBuilderFactory
174    public static Builder newBuilder() {
175        return new Builder();
176    }
177
178    private WriterAppender(final String name, final StringLayout layout, final Filter filter,
179            final WriterManager manager, final boolean ignoreExceptions) {
180        super(name, layout, filter, ignoreExceptions, true, manager);
181    }
182
183}