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