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