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