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; 018 019import java.util.AbstractMap; 020import java.util.Map; 021import java.util.concurrent.locks.Lock; 022import java.util.concurrent.locks.ReentrantLock; 023import jakarta.servlet.ServletContext; 024 025import org.apache.logging.log4j.LogManager; 026import org.apache.logging.log4j.core.LoggerContext; 027import org.apache.logging.log4j.core.impl.ContextAnchor; 028 029/** 030 * Convenience methods for retrieving the {@link org.apache.logging.log4j.core.LoggerContext} associated with a 031 * particular ServletContext. These methods are most particularly useful for asynchronous servlets where the 032 * Thread Context ClassLoader (TCCL) is potentially different from the TCCL used by the 033 * Servlet container that bootstrapped Log4j. 034 * 035 * @since 2.0.1 036 */ 037public final class WebLoggerContextUtils { 038 private WebLoggerContextUtils() { 039 } 040 041 private static final Lock WEB_SUPPORT_LOOKUP = new ReentrantLock(); 042 private static final String SERVLET_CONTEXT = "__SERVLET_CONTEXT__"; 043 044 /** 045 * Finds the main {@link org.apache.logging.log4j.core.LoggerContext} configured for the given ServletContext. 046 * 047 * @param servletContext the ServletContext to locate a LoggerContext for 048 * @return the LoggerContext for the given ServletContext 049 * @since 2.0.1 050 */ 051 public static LoggerContext getWebLoggerContext(final ServletContext servletContext) { 052 return (LoggerContext) servletContext.getAttribute(Log4jWebSupport.CONTEXT_ATTRIBUTE); 053 } 054 055 /** 056 * Finds the main {@link org.apache.logging.log4j.core.LoggerContext} configured for the given ServletContext. 057 * 058 * @param servletContext the ServletContext to locate a LoggerContext for 059 * @return the LoggerContext for the given ServletContext or {@code null} if none was set 060 * @throws java.lang.IllegalStateException if no LoggerContext could be found on the given ServletContext 061 * @since 2.0.1 062 */ 063 public static LoggerContext getRequiredWebLoggerContext(final ServletContext servletContext) { 064 final LoggerContext loggerContext = getWebLoggerContext(servletContext); 065 if (loggerContext == null) { 066 throw new IllegalStateException( 067 "No LoggerContext found in ServletContext attribute " + Log4jWebSupport.CONTEXT_ATTRIBUTE); 068 } 069 return loggerContext; 070 } 071 072 /** 073 * Finds or initializes the {@link org.apache.logging.log4j.web.Log4jWebLifeCycle} singleton for the given 074 * ServletContext. 075 * 076 * @param servletContext the ServletContext to get the Log4jWebLifeCycle for 077 * @return the Log4jWebLifeCycle for the given ServletContext 078 * @since 2.0.1 079 */ 080 public static Log4jWebLifeCycle getWebLifeCycle(final ServletContext servletContext) { 081 WEB_SUPPORT_LOOKUP.lock(); 082 try { 083 Log4jWebLifeCycle webLifeCycle = (Log4jWebLifeCycle) servletContext.getAttribute( 084 Log4jWebSupport.SUPPORT_ATTRIBUTE); 085 if (webLifeCycle == null) { 086 webLifeCycle = Log4jWebInitializerImpl.initialize(servletContext); 087 } 088 return webLifeCycle; 089 } finally { 090 WEB_SUPPORT_LOOKUP.unlock(); 091 } 092 } 093 094 /** 095 * Wraps a Runnable instance by setting its thread context {@link org.apache.logging.log4j.core.LoggerContext} 096 * before execution and clearing it after execution. 097 * 098 * @param servletContext the ServletContext to locate a LoggerContext for 099 * @param runnable the Runnable to wrap execution for 100 * @return a wrapped Runnable 101 * @since 2.0.1 102 */ 103 public static Runnable wrapExecutionContext(final ServletContext servletContext, final Runnable runnable) { 104 return () -> { 105 final Log4jWebSupport webSupport = getWebLifeCycle(servletContext); 106 webSupport.setLoggerContext(); 107 try { 108 runnable.run(); 109 } finally { 110 webSupport.clearLoggerContext(); 111 } 112 }; 113 } 114 115 public static Map.Entry<String, Object> createExternalEntry(ServletContext servletContext) { 116 return new AbstractMap.SimpleEntry<>(SERVLET_CONTEXT, servletContext); 117 } 118 119 public static void setServletContext(LoggerContext lc, ServletContext servletContext) { 120 if (lc != null) { 121 lc.putObject(SERVLET_CONTEXT, servletContext); 122 } 123 } 124 125 /** 126 * Gets the current {@link ServletContext} if it has already been assigned to a LoggerContext's external context. 127 * 128 * @return the current ServletContext attached to a LoggerContext or {@code null} if none could be found 129 * @since 2.1 130 */ 131 public static ServletContext getServletContext() { 132 org.apache.logging.log4j.spi.LoggerContext lc = ContextAnchor.THREAD_CONTEXT.get(); 133 if (lc == null) { 134 lc = LogManager.getContext(false); 135 } 136 137 Object obj = lc != null ? lc.getObject(SERVLET_CONTEXT) : null; 138 if (obj instanceof ServletContext) { 139 return (ServletContext) obj; 140 } 141 return null; 142 } 143}