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