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