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