1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.logging.log4j.web;
18
19 import java.net.URI;
20 import java.net.URL;
21 import java.text.SimpleDateFormat;
22 import java.util.Arrays;
23 import java.util.Date;
24 import java.util.Map;
25 import java.util.concurrent.ConcurrentHashMap;
26
27 import javax.servlet.ServletContext;
28
29 import org.apache.logging.log4j.LogManager;
30 import org.apache.logging.log4j.core.AbstractLifeCycle;
31 import org.apache.logging.log4j.core.LoggerContext;
32 import org.apache.logging.log4j.core.async.AsyncLoggerContext;
33 import org.apache.logging.log4j.core.config.Configurator;
34 import org.apache.logging.log4j.core.impl.ContextAnchor;
35 import org.apache.logging.log4j.core.impl.Log4jContextFactory;
36 import org.apache.logging.log4j.core.lookup.Interpolator;
37 import org.apache.logging.log4j.core.lookup.StrSubstitutor;
38 import org.apache.logging.log4j.core.selector.ContextSelector;
39 import org.apache.logging.log4j.core.selector.NamedContextSelector;
40 import org.apache.logging.log4j.core.util.Loader;
41 import org.apache.logging.log4j.core.util.NetUtils;
42 import org.apache.logging.log4j.core.util.SetUtils;
43 import org.apache.logging.log4j.spi.LoggerContextFactory;
44 import org.apache.logging.log4j.util.LoaderUtil;
45
46
47
48
49 final class Log4jWebInitializerImpl extends AbstractLifeCycle implements Log4jWebLifeCycle {
50
51 private static final String WEB_INF = "/WEB-INF/";
52
53 static {
54 if (Loader.isClassAvailable("org.apache.logging.log4j.core.web.JNDIContextFilter")) {
55 throw new IllegalStateException("You are using Log4j 2 in a web application with the old, extinct "
56 + "log4j-web artifact. This is not supported and could cause serious runtime problems. Please"
57 + "remove the log4j-web JAR file from your application.");
58 }
59 }
60
61 private final Map<String, String> map = new ConcurrentHashMap<>();
62 private final StrSubstitutor substitutor = new StrSubstitutor(new Interpolator(map));
63 private final ServletContext servletContext;
64
65 private String name;
66 private NamedContextSelector namedContextSelector;
67 private LoggerContext loggerContext;
68
69 private Log4jWebInitializerImpl(final ServletContext servletContext) {
70 this.servletContext = servletContext;
71 this.map.put("hostName", NetUtils.getLocalHostname());
72 }
73
74
75
76
77
78
79
80
81
82
83
84 protected static Log4jWebInitializerImpl initialize(final ServletContext servletContext) {
85 final Log4jWebInitializerImpl initializer = new Log4jWebInitializerImpl(servletContext);
86 servletContext.setAttribute(SUPPORT_ATTRIBUTE, initializer);
87 return initializer;
88 }
89
90 @Override
91 public synchronized void start() {
92 if (this.isStopped() || this.isStopping()) {
93 throw new IllegalStateException("Cannot start this Log4jWebInitializerImpl after it was stopped.");
94 }
95
96
97 if (this.isInitialized()) {
98 super.setStarting();
99
100 this.name = this.substitutor.replace(this.servletContext.getInitParameter(LOG4J_CONTEXT_NAME));
101 final String location = this.substitutor.replace(this.servletContext
102 .getInitParameter(LOG4J_CONFIG_LOCATION));
103 final boolean isJndi = "true".equalsIgnoreCase(this.servletContext
104 .getInitParameter(IS_LOG4J_CONTEXT_SELECTOR_NAMED));
105
106 if (isJndi) {
107 this.initializeJndi(location);
108 } else {
109 this.initializeNonJndi(location);
110 }
111 if (this.loggerContext instanceof AsyncLoggerContext) {
112 ((AsyncLoggerContext) this.loggerContext).setUseThreadLocals(false);
113 }
114
115 this.servletContext.setAttribute(CONTEXT_ATTRIBUTE, this.loggerContext);
116 super.setStarted();
117 }
118 }
119
120 private void initializeJndi(final String location) {
121 final URI configLocation = getConfigURI(location);
122
123 if (this.name == null) {
124 throw new IllegalStateException("A log4jContextName context parameter is required");
125 }
126
127 LoggerContext context;
128 final LoggerContextFactory factory = LogManager.getFactory();
129 if (factory instanceof Log4jContextFactory) {
130 final ContextSelector selector = ((Log4jContextFactory) factory).getSelector();
131 if (selector instanceof NamedContextSelector) {
132 this.namedContextSelector = (NamedContextSelector) selector;
133 context = this.namedContextSelector.locateContext(this.name, this.servletContext, configLocation);
134 ContextAnchor.THREAD_CONTEXT.set(context);
135 if (context.isInitialized()) {
136 context.start();
137 }
138 ContextAnchor.THREAD_CONTEXT.remove();
139 } else {
140 LOGGER.warn("Potential problem: Selector is not an instance of NamedContextSelector.");
141 return;
142 }
143 } else {
144 LOGGER.warn("Potential problem: LoggerContextFactory is not an instance of Log4jContextFactory.");
145 return;
146 }
147 this.loggerContext = context;
148 LOGGER.debug("Created logger context for [{}] using [{}].", this.name, context.getClass().getClassLoader());
149 }
150
151 private void initializeNonJndi(final String location) {
152 if (this.name == null) {
153 this.name = this.servletContext.getServletContextName();
154 LOGGER.debug("Using the servlet context name \"{}\".", this.name);
155 }
156 if (this.name == null) {
157 this.name = this.servletContext.getContextPath();
158 LOGGER.debug("Using the servlet context context-path \"{}\".", this.name);
159 }
160
161 if (this.name == null && location == null) {
162 LOGGER.error("No Log4j context configuration provided. This is very unusual.");
163 this.name = new SimpleDateFormat("yyyyMMdd_HHmmss.SSS").format(new Date());
164 }
165
166 final URI uri = getConfigURI(location);
167 this.loggerContext = Configurator.initialize(this.name, this.getClassLoader(), uri, this.servletContext);
168 }
169
170 private URI getConfigURI(final String location) {
171 try {
172 String configLocation = location;
173 if (configLocation == null) {
174 final String[] paths = SetUtils.prefixSet(servletContext.getResourcePaths(WEB_INF), WEB_INF + "log4j2");
175 LOGGER.debug("getConfigURI found resource paths {} in servletContext at [{}]", Arrays.toString(paths), WEB_INF);
176 if (paths.length == 1) {
177 configLocation = paths[0];
178 } else if (paths.length > 1) {
179 final String prefix = WEB_INF + "log4j2-" + this.name + ".";
180 boolean found = false;
181 for (final String str : paths) {
182 if (str.startsWith(prefix)) {
183 configLocation = str;
184 found = true;
185 break;
186 }
187 }
188 if (!found) {
189 configLocation = paths[0];
190 }
191 }
192 }
193 if (configLocation != null) {
194 final URL url = servletContext.getResource(configLocation);
195 if (url != null) {
196 final URI uri = url.toURI();
197 LOGGER.debug("getConfigURI found resource [{}] in servletContext at [{}]", uri, configLocation);
198 return uri;
199 }
200 }
201 } catch (final Exception ex) {
202
203 }
204 if (location != null) {
205 try {
206 final URI correctedFilePathUri = NetUtils.toURI(location);
207 LOGGER.debug("getConfigURI found [{}] in servletContext at [{}]", correctedFilePathUri, location);
208 return correctedFilePathUri;
209 } catch (final Exception e) {
210 LOGGER.error("Unable to convert configuration location [{}] to a URI", location, e);
211 }
212 }
213 return null;
214 }
215
216 @Override
217 public synchronized void stop() {
218 if (!this.isStarted() && !this.isStopped()) {
219 throw new IllegalStateException("Cannot stop this Log4jWebInitializer because it has not started.");
220 }
221
222
223 if (this.isStarted()) {
224 this.setStopping();
225 if (this.loggerContext != null) {
226 LOGGER.debug("Removing LoggerContext for [{}].", this.name);
227 this.servletContext.removeAttribute(CONTEXT_ATTRIBUTE);
228 if (this.namedContextSelector != null) {
229 this.namedContextSelector.removeContext(this.name);
230 }
231 this.loggerContext.stop();
232 this.loggerContext.setExternalContext(null);
233 this.loggerContext = null;
234 }
235 this.setStopped();
236 }
237 }
238
239 @Override
240 public void setLoggerContext() {
241 if (this.loggerContext != null) {
242 ContextAnchor.THREAD_CONTEXT.set(this.loggerContext);
243 }
244 }
245
246 @Override
247 public void clearLoggerContext() {
248 ContextAnchor.THREAD_CONTEXT.remove();
249 }
250
251 @Override
252 public void wrapExecution(final Runnable runnable) {
253 this.setLoggerContext();
254
255 try {
256 runnable.run();
257 } finally {
258 this.clearLoggerContext();
259 }
260 }
261
262 private ClassLoader getClassLoader() {
263 try {
264
265
266
267 return this.servletContext.getClassLoader();
268 } catch (final Throwable ignore) {
269
270 return LoaderUtil.getThreadContextClassLoader();
271 }
272 }
273
274 }