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.web.appender;
018
019import java.io.Serializable;
020
021import javax.servlet.ServletContext;
022
023import org.apache.logging.log4j.core.Filter;
024import org.apache.logging.log4j.core.Layout;
025import org.apache.logging.log4j.core.LogEvent;
026import org.apache.logging.log4j.core.appender.AbstractAppender;
027import org.apache.logging.log4j.core.config.Property;
028import org.apache.logging.log4j.core.config.plugins.Plugin;
029import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute;
030import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory;
031import org.apache.logging.log4j.core.layout.AbstractStringLayout;
032import org.apache.logging.log4j.core.layout.PatternLayout;
033import org.apache.logging.log4j.web.WebLoggerContextUtils;
034
035/**
036 * Logs using the ServletContext's log method
037 */
038@Plugin(name = "Servlet", category = "Core", elementType = "appender", printObject = true)
039public class ServletAppender extends AbstractAppender {
040
041        public static class Builder<B extends Builder<B>> extends AbstractAppender.Builder<B>
042                        implements org.apache.logging.log4j.core.util.Builder<ServletAppender> {
043
044        @PluginBuilderAttribute
045        private boolean logThrowables;
046
047                @Override
048                public ServletAppender build() {
049                        final String name = getName();
050                        if (name == null) {
051                                LOGGER.error("No name provided for ServletAppender");
052                        }
053                        final ServletContext servletContext = WebLoggerContextUtils.getServletContext();
054                        if (servletContext == null) {
055                                LOGGER.error("No servlet context is available");
056                                return null;
057                        }
058                        Layout<? extends Serializable> layout = getLayout();
059                        if (layout == null) {
060                                layout = PatternLayout.createDefaultLayout();
061                        } else if (!(layout instanceof AbstractStringLayout)) {
062                                LOGGER.error("Layout must be a StringLayout to log to ServletContext");
063                                return null;
064                        }
065                        return new ServletAppender(name, layout, getFilter(), servletContext, isIgnoreExceptions(), logThrowables);
066                }
067
068        /**
069         * Logs with {@link ServletContext#log(String, Throwable)} if true and with {@link ServletContext#log(String)} if false.
070         *
071         * @return whether to log a Throwable with the servlet context.
072         */
073        public boolean isLogThrowables() {
074            return logThrowables;
075        }
076
077        /**
078         * Logs with {@link ServletContext#log(String, Throwable)} if true and with {@link ServletContext#log(String)} if false.
079         */
080        public void setLogThrowables(final boolean logThrowables) {
081            this.logThrowables = logThrowables;
082        }
083
084        }
085
086    @PluginBuilderFactory
087    public static <B extends Builder<B>> B newBuilder() {
088        return new Builder<B>().asBuilder();
089    }
090
091    private final ServletContext servletContext;
092    private final boolean logThrowables;
093
094    private ServletAppender(final String name, final Layout<? extends Serializable> layout, final Filter filter,
095            final ServletContext servletContext, final boolean ignoreExceptions, final boolean logThrowables) {
096        super(name, filter, layout, ignoreExceptions, Property.EMPTY_ARRAY);
097        this.servletContext = servletContext;
098        this.logThrowables = logThrowables;
099    }
100
101    @Override
102    public void append(final LogEvent event) {
103        final String serialized = ((AbstractStringLayout) getLayout()).toSerializable(event);
104        if (logThrowables) {
105            servletContext.log(serialized, event.getThrown());
106        } else {
107            servletContext.log(serialized);
108        }
109    }
110
111    /**
112     * Creates a Servlet Appender.
113     * @param layout The layout to use (required). Must extend {@link AbstractStringLayout}.
114     * @param filter The Filter or null.
115     * @param name The name of the Appender (required).
116     * @param ignoreExceptions If {@code true} (default) exceptions encountered when appending events are logged;
117     *                         otherwise they are propagated to the caller.
118     * @return The ServletAppender.
119     * @deprecated Use {@link #newBuilder()}.
120     */
121    @Deprecated
122    public static ServletAppender createAppender(final Layout<? extends Serializable> layout, final Filter filter,
123            final String name, final boolean ignoreExceptions) {
124        // @formatter:off
125        return newBuilder().setFilter(filter).setIgnoreExceptions(ignoreExceptions).setLayout(layout).setName(name)
126                        .build();
127        // @formatter:on
128    }
129
130}