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.Serializable;
020import java.nio.charset.Charset;
021import java.util.Objects;
022
023import org.apache.logging.log4j.core.Appender;
024import org.apache.logging.log4j.core.ErrorHandler;
025import org.apache.logging.log4j.core.Filter;
026import org.apache.logging.log4j.core.Layout;
027import org.apache.logging.log4j.core.LogEvent;
028import org.apache.logging.log4j.core.config.Configuration;
029import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute;
030import org.apache.logging.log4j.core.config.plugins.PluginConfiguration;
031import org.apache.logging.log4j.core.config.plugins.PluginElement;
032import org.apache.logging.log4j.core.config.plugins.validation.constraints.Required;
033import org.apache.logging.log4j.core.filter.AbstractFilterable;
034import org.apache.logging.log4j.core.layout.PatternLayout;
035import org.apache.logging.log4j.core.util.Integers;
036
037/**
038 * Abstract base class for Appenders. Although Appenders do not have to extend this class, doing so will simplify their
039 * implementation.
040 */
041public abstract class AbstractAppender extends AbstractFilterable implements Appender {
042
043    /**
044     * Subclasses can extend this abstract Builder. 
045     * 
046     * @param <B> The type to build.
047     */
048    public abstract static class Builder<B extends Builder<B>> extends AbstractFilterable.Builder<B> {
049
050        @PluginBuilderAttribute
051        private boolean ignoreExceptions = true;
052        
053        @PluginElement("Layout")
054        private Layout<? extends Serializable> layout;
055
056        @PluginBuilderAttribute
057        @Required(message = "No appender name provided")
058        private String name;
059
060        @PluginConfiguration
061        private Configuration configuration;
062
063        public String getName() {
064            return name;
065        }
066
067        public boolean isIgnoreExceptions() {
068            return ignoreExceptions;
069        }
070
071        public Layout<? extends Serializable> getLayout() {
072            return layout;
073        }
074
075        public B withName(final String name) {
076            this.name = name;
077            return asBuilder();
078        }
079
080        public B withIgnoreExceptions(final boolean ignoreExceptions) {
081            this.ignoreExceptions = ignoreExceptions;
082            return asBuilder();
083        }
084
085        public B withLayout(final Layout<? extends Serializable> layout) {
086            this.layout = layout;
087            return asBuilder();
088        }
089
090        public Layout<? extends Serializable> getOrCreateLayout() {
091            if (layout == null) {
092                return PatternLayout.createDefaultLayout();
093            }
094            return layout;
095        }
096        
097        public Layout<? extends Serializable> getOrCreateLayout(final Charset charset) {
098            if (layout == null) {
099                return PatternLayout.newBuilder().withCharset(charset).build();
100            }
101            return layout;
102        }
103
104        /**
105         * @deprecated Use {@link #setConfiguration(Configuration)}
106         */
107        @Deprecated
108        public B withConfiguration(final Configuration configuration) {
109            this.configuration = configuration;
110            return asBuilder();
111        }
112
113        public B setConfiguration(final Configuration configuration) {
114            this.configuration = configuration;
115            return asBuilder();
116        }
117
118        public Configuration getConfiguration() {
119            return configuration;
120        }
121        
122    }
123    
124    private final String name;
125    private final boolean ignoreExceptions;
126    private final Layout<? extends Serializable> layout;
127    private ErrorHandler handler = new DefaultErrorHandler(this);
128
129    /**
130     * Constructor that defaults to suppressing exceptions.
131     * 
132     * @param name The Appender name.
133     * @param filter The Filter to associate with the Appender.
134     * @param layout The layout to use to format the event.
135     */
136    protected AbstractAppender(final String name, final Filter filter, final Layout<? extends Serializable> layout) {
137        this(name, filter, layout, true);
138    }
139
140    /**
141     * Constructor.
142     * 
143     * @param name The Appender name.
144     * @param filter The Filter to associate with the Appender.
145     * @param layout The layout to use to format the event.
146     * @param ignoreExceptions If true, exceptions will be logged and suppressed. If false errors will be logged and
147     *            then passed to the application.
148     */
149    protected AbstractAppender(final String name, final Filter filter, final Layout<? extends Serializable> layout,
150            final boolean ignoreExceptions) {
151        super(filter);
152        this.name = Objects.requireNonNull(name, "name");
153        this.layout = layout;
154        this.ignoreExceptions = ignoreExceptions;
155    }
156
157    public static int parseInt(final String s, final int defaultValue) {
158        try {
159            return Integers.parseInt(s, defaultValue);
160        } catch (final NumberFormatException e) {
161            LOGGER.error("Could not parse \"{}\" as an integer,  using default value {}: {}", s, defaultValue, e);
162            return defaultValue;
163        }
164    }
165
166    /**
167     * Handle an error with a message using the {@link ErrorHandler} configured for this Appender.
168     * 
169     * @param msg The message.
170     */
171    public void error(final String msg) {
172        handler.error(msg);
173    }
174
175    /**
176     * Handle an error with a message, exception, and a logging event, using the {@link ErrorHandler} configured for
177     * this Appender.
178     * 
179     * @param msg The message.
180     * @param event The LogEvent.
181     * @param t The Throwable.
182     */
183    public void error(final String msg, final LogEvent event, final Throwable t) {
184        handler.error(msg, event, t);
185    }
186
187    /**
188     * Handle an error with a message and an exception using the {@link ErrorHandler} configured for this Appender.
189     * 
190     * @param msg The message.
191     * @param t The Throwable.
192     */
193    public void error(final String msg, final Throwable t) {
194        handler.error(msg, t);
195    }
196
197    /**
198     * Returns the ErrorHandler, if any.
199     * 
200     * @return The ErrorHandler.
201     */
202    @Override
203    public ErrorHandler getHandler() {
204        return handler;
205    }
206
207    /**
208     * Returns the Layout for the appender.
209     * 
210     * @return The Layout used to format the event.
211     */
212    @Override
213    public Layout<? extends Serializable> getLayout() {
214        return layout;
215    }
216
217    /**
218     * Returns the name of the Appender.
219     * 
220     * @return The name of the Appender.
221     */
222    @Override
223    public String getName() {
224        return name;
225    }
226
227    /**
228     * Some appenders need to propagate exceptions back to the application. When {@code ignoreExceptions} is
229     * {@code false} the AppenderControl will allow the exception to percolate.
230     *
231     * @return {@code true} if exceptions will be logged but now thrown, {@code false} otherwise.
232     */
233    @Override
234    public boolean ignoreExceptions() {
235        return ignoreExceptions;
236    }
237
238    /**
239     * The handler must be set before the appender is started.
240     * 
241     * @param handler The ErrorHandler to use.
242     */
243    @Override
244    public void setHandler(final ErrorHandler handler) {
245        if (handler == null) {
246            LOGGER.error("The handler cannot be set to null");
247        }
248        if (isStarted()) {
249            LOGGER.error("The handler cannot be changed once the appender is started");
250            return;
251        }
252        this.handler = handler;
253    }
254
255    @Override
256    public String toString() {
257        return name;
258    }
259
260}