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 jakarta.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}