View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements. See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache license, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License. You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the license for the specific language governing permissions and
15   * limitations under the license.
16   */
17  package org.apache.logging.log4j.web;
18  
19  import java.util.AbstractMap;
20  import java.util.Map;
21  import java.util.concurrent.locks.Lock;
22  import java.util.concurrent.locks.ReentrantLock;
23  import jakarta.servlet.ServletContext;
24  
25  import org.apache.logging.log4j.LogManager;
26  import org.apache.logging.log4j.core.LoggerContext;
27  import org.apache.logging.log4j.core.impl.ContextAnchor;
28  
29  /**
30   * Convenience methods for retrieving the {@link org.apache.logging.log4j.core.LoggerContext} associated with a
31   * particular ServletContext. These methods are most particularly useful for asynchronous servlets where the
32   * Thread Context ClassLoader (TCCL) is potentially different from the TCCL used by the
33   * Servlet container that bootstrapped Log4j.
34   *
35   * @since 2.0.1
36   */
37  public final class WebLoggerContextUtils {
38      private WebLoggerContextUtils() {
39      }
40  
41      private static final Lock WEB_SUPPORT_LOOKUP = new ReentrantLock();
42      private static final String SERVLET_CONTEXT = "__SERVLET_CONTEXT__";
43  
44      /**
45       * Finds the main {@link org.apache.logging.log4j.core.LoggerContext} configured for the given ServletContext.
46       *
47       * @param servletContext the ServletContext to locate a LoggerContext for
48       * @return the LoggerContext for the given ServletContext
49       * @since 2.0.1
50       */
51      public static LoggerContext getWebLoggerContext(final ServletContext servletContext) {
52          return (LoggerContext) servletContext.getAttribute(Log4jWebSupport.CONTEXT_ATTRIBUTE);
53      }
54  
55      /**
56       * Finds the main {@link org.apache.logging.log4j.core.LoggerContext} configured for the given ServletContext.
57       *
58       * @param servletContext the ServletContext to locate a LoggerContext for
59       * @return the LoggerContext for the given ServletContext or {@code null} if none was set
60       * @throws java.lang.IllegalStateException if no LoggerContext could be found on the given ServletContext
61       * @since 2.0.1
62       */
63      public static LoggerContext getRequiredWebLoggerContext(final ServletContext servletContext) {
64          final LoggerContext loggerContext = getWebLoggerContext(servletContext);
65          if (loggerContext == null) {
66              throw new IllegalStateException(
67                  "No LoggerContext found in ServletContext attribute " + Log4jWebSupport.CONTEXT_ATTRIBUTE);
68          }
69          return loggerContext;
70      }
71  
72      /**
73       * Finds or initializes the {@link org.apache.logging.log4j.web.Log4jWebLifeCycle} singleton for the given
74       * ServletContext.
75       *
76       * @param servletContext the ServletContext to get the Log4jWebLifeCycle for
77       * @return the Log4jWebLifeCycle for the given ServletContext
78       * @since 2.0.1
79       */
80      public static Log4jWebLifeCycle getWebLifeCycle(final ServletContext servletContext) {
81          WEB_SUPPORT_LOOKUP.lock();
82          try {
83              Log4jWebLifeCycle webLifeCycle = (Log4jWebLifeCycle) servletContext.getAttribute(
84                  Log4jWebSupport.SUPPORT_ATTRIBUTE);
85              if (webLifeCycle == null) {
86                  webLifeCycle = Log4jWebInitializerImpl.initialize(servletContext);
87              }
88              return webLifeCycle;
89          } finally {
90              WEB_SUPPORT_LOOKUP.unlock();
91          }
92      }
93  
94      /**
95       * Wraps a Runnable instance by setting its thread context {@link org.apache.logging.log4j.core.LoggerContext}
96       * before execution and clearing it after execution.
97       *
98       * @param servletContext the ServletContext to locate a LoggerContext for
99       * @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 }