The Apache Log4j support for web servlet containers
—
Web-aware logging components including servlet context lookups for configuration variables, servlet appenders for direct logging to container logs, and thread context utilities for capturing request information. These features enable Log4j to integrate deeply with web application environments.
Log4j lookup plugin that provides access to servlet context information within log configurations. Enables dynamic configuration values based on web application context.
/**
* Log4j lookup for accessing servlet context information in configurations.
* Supports servlet context attributes, init parameters, and container metadata.
*/
@Plugin(name = "web", category = "Lookup")
public class WebLookup extends AbstractLookup {
/**
* Resolves web-related lookup keys to their corresponding values.
* Accesses current ServletContext via WebLoggerContextUtils.
*
* @param event LogEvent triggering the lookup (unused)
* @param key Lookup key specifying what information to retrieve
* @return String value corresponding to the key, or null if not found
*/
public String lookup(LogEvent event, String key);
}Supported Lookup Keys:
public class WebLookup extends AbstractLookup {
/** Prefix for servlet context attributes */
private static final String ATTR_PREFIX = "attr.";
/** Prefix for servlet init parameters */
private static final String INIT_PARAM_PREFIX = "initParam.";
}Available Lookup Values:
attr.{name} - ServletContext attribute value by nameinitParam.{name} - ServletContext init parameter by namerootDir - Web application root directory pathcontextPath - ServletContext path (e.g., "/myapp")contextPathName - Context path name without slashesservletContextName - ServletContext display nameserverInfo - Server container informationeffectiveMajorVersion - Effective servlet API major versioneffectiveMinorVersion - Effective servlet API minor versionmajorVersion - Servlet API major versionminorVersion - Servlet API minor versionUsage in Log4j Configuration:
<Configuration>
<Properties>
<Property name="logDir">${web:rootDir}/logs</Property>
<Property name="appName">${web:servletContextName}</Property>
<Property name="version">${web:attr.appVersion}</Property>
</Properties>
<Appenders>
<File name="AppLog" fileName="${logDir}/${appName}.log">
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} [%level] %logger{36} - %msg%n"/>
</File>
</Appenders>
<Loggers>
<Root level="info">
<AppenderRef ref="AppLog"/>
</Root>
</Loggers>
</Configuration>Error Handling:
The WebLookup can throw exceptions in certain scenarios:
// If rootDir cannot be resolved (e.g., in unexploded WAR)
throw new IllegalStateException(
"Failed to resolve web:rootDir -- servlet container unable to translate virtual path " +
" to real path (probably not deployed as exploded"
);Common Exceptions:
IllegalStateException: Thrown when web:rootDir cannot be resolved (usually in non-exploded WAR deployments)null for unknown lookup keys or when ServletContext is not availableLog4j appender that writes log events directly to the servlet container's logging system using ServletContext.log(). Integrates Log4j output with container-managed logs.
/**
* Log4j appender that logs using ServletContext's log method.
* Integrates with servlet container logging infrastructure.
*/
@Plugin(name = "Servlet", category = "Core", elementType = "appender", printObject = true)
public final class ServletAppender extends AbstractAppender {
/**
* Appends a log event to the servlet context log.
* Uses ServletContext.log() with optional throwable support.
*
* @param event LogEvent to append to servlet context log
*/
public void append(LogEvent event);
/**
* Creates a new ServletAppender builder for configuration.
*
* @return Builder instance for ServletAppender configuration
*/
public static <B extends Builder<B>> B newBuilder();
/**
* Creates ServletAppender instance (deprecated - use newBuilder()).
*
* @param layout Layout for formatting log events (must extend StringLayout)
* @param filter Filter for event filtering (optional)
* @param name Appender name (required)
* @param ignoreExceptions Whether to ignore exceptions during appending
* @return ServletAppender instance or null if invalid configuration
* @deprecated Use newBuilder() instead
*/
@Deprecated
public static ServletAppender createAppender(
Layout<? extends Serializable> layout,
Filter filter,
String name,
boolean ignoreExceptions);
}ServletAppender Builder:
/**
* Builder for ServletAppender configuration.
* Extends AbstractAppender.Builder with servlet-specific options.
*/
public static class Builder<B extends Builder<B>> extends AbstractAppender.Builder<B>
implements org.apache.logging.log4j.core.util.Builder<ServletAppender> {
/**
* Builds the ServletAppender with current configuration.
* Validates ServletContext availability and layout requirements.
*
* @return ServletAppender instance or null if validation fails
*/
public ServletAppender build();
/**
* Gets whether throwables are logged with servlet context.
*
* @return true if throwables logged via ServletContext.log(String, Throwable)
*/
public boolean isLogThrowables();
/**
* Sets whether to log throwables with servlet context.
* Controls whether to use ServletContext.log(String) or log(String, Throwable).
*
* @param logThrowables true to log throwables with servlet context
*/
public void setLogThrowables(boolean logThrowables);
}Configuration in Log4j XML:
<Configuration>
<Appenders>
<Servlet name="ServletLog" logThrowables="true">
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} [%level] %logger{36} - %msg%n"/>
</Servlet>
</Appenders>
<Loggers>
<Logger name="com.example.web" level="debug">
<AppenderRef ref="ServletLog"/>
</Logger>
<Root level="info">
<AppenderRef ref="ServletLog"/>
</Root>
</Loggers>
</Configuration>Usage Notes:
logThrowables parameter controls exception logging behaviorUtility class for capturing servlet request information in Log4j's ThreadContext. Enables automatic inclusion of request details in log messages.
/**
* Utility for adding servlet request information to ThreadContext.
* Captures request metadata for inclusion in log messages.
*/
public class ServletRequestThreadContext {
/**
* Adds servlet request information to ThreadContext.
* Captures remote address, host, and port with specified key prefix.
*
* @param key Prefix for ThreadContext keys
* @param servletRequest ServletRequest to capture information from
*/
public static void put(String key, ServletRequest servletRequest);
/**
* Adds HTTP servlet request information to ThreadContext.
* Delegates to ServletRequest version - HTTP-specific data handled elsewhere.
*
* @param key Prefix for ThreadContext keys
* @param servletRequest HttpServletRequest to capture information from
*/
public static void put(String key, HttpServletRequest servletRequest);
/**
* Adds field-value pair to ThreadContext with key prefix.
* Helper method for structured request data capture.
*
* @param key Base key for ThreadContext
* @param field Field name to append to key
* @param value Field value to store
*/
public static void put(String key, String field, Object value);
/**
* Adds key-value pair directly to ThreadContext.
* Direct wrapper around ThreadContext.put().
*
* @param key ThreadContext key
* @param value ThreadContext value
*/
public static void put(String key, String value);
}Captured Request Information:
When calling put(String key, ServletRequest servletRequest), the following ThreadContext entries are created:
{key}.RemoteAddr - request.getRemoteAddr(){key}.RemoteHost - request.getRemoteHost(){key}.RemotePort - request.getRemotePort()Usage in Servlet Filter:
public class RequestLoggingFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// Capture request information in ThreadContext
ServletRequestThreadContext.put("request", request);
if (request instanceof HttpServletRequest) {
HttpServletRequest httpRequest = (HttpServletRequest) request;
ServletRequestThreadContext.put("request", "Method", httpRequest.getMethod());
ServletRequestThreadContext.put("request", "URI", httpRequest.getRequestURI());
ServletRequestThreadContext.put("request", "QueryString", httpRequest.getQueryString());
}
try {
chain.doFilter(request, response);
} finally {
// ThreadContext automatically cleared by Log4jServletFilter
}
}
}Log4j Configuration with Request Data:
<Configuration>
<Appenders>
<Console name="Console">
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} [%level] %logger{36} - %msg [%X{request.RemoteAddr}] [%X{request.Method} %X{request.URI}]%n"/>
</Console>
</Appenders>
<Loggers>
<Root level="info">
<AppenderRef ref="Console"/>
</Root>
</Loggers>
</Configuration>@WebFilter("/*")
public class LoggingFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// Capture request context
ServletRequestThreadContext.put("req", request);
if (request instanceof HttpServletRequest) {
HttpServletRequest httpReq = (HttpServletRequest) request;
ServletRequestThreadContext.put("req", "method", httpReq.getMethod());
ServletRequestThreadContext.put("req", "uri", httpReq.getRequestURI());
ServletRequestThreadContext.put("req", "userAgent", httpReq.getHeader("User-Agent"));
}
Logger logger = LogManager.getLogger();
logger.info("Processing request");
try {
chain.doFilter(request, response);
logger.info("Request completed successfully");
} catch (Exception e) {
logger.error("Request failed", e);
throw e;
}
}
}<Configuration>
<Properties>
<Property name="app.name">${web:servletContextName}</Property>
<Property name="app.version">${web:attr.version}</Property>
<Property name="log.path">${web:rootDir}/WEB-INF/logs</Property>
<Property name="container.info">${web:serverInfo}</Property>
</Properties>
<Appenders>
<File name="AppFile" fileName="${log.path}/${app.name}-${app.version}.log">
<PatternLayout pattern="%d [%level] %logger - %msg [%X{req.RemoteAddr}] [%X{req.method} %X{req.uri}]%n"/>
</File>
<Servlet name="ContainerLog" logThrowables="true">
<PatternLayout pattern="[${app.name}] %d [%level] %logger - %msg%n"/>
</Servlet>
</Appenders>
<Loggers>
<Logger name="com.example" level="debug">
<AppenderRef ref="AppFile"/>
<AppenderRef ref="ContainerLog"/>
</Logger>
<Root level="info">
<AppenderRef ref="ContainerLog"/>
</Root>
</Loggers>
</Configuration>This configuration demonstrates:
Install with Tessl CLI
npx tessl i tessl/maven-org-apache-logging-log4j--log4j-web