1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.logging.log4j.core.jmx;
18
19 import java.lang.management.ManagementFactory;
20 import java.util.List;
21 import java.util.Map;
22 import java.util.Set;
23 import java.util.concurrent.Executor;
24 import java.util.concurrent.ExecutorService;
25 import java.util.concurrent.Executors;
26
27 import javax.management.InstanceAlreadyExistsException;
28 import javax.management.MBeanRegistrationException;
29 import javax.management.MBeanServer;
30 import javax.management.NotCompliantMBeanException;
31 import javax.management.ObjectName;
32
33 import org.apache.logging.log4j.LogManager;
34 import org.apache.logging.log4j.core.Appender;
35 import org.apache.logging.log4j.core.LoggerContext;
36 import org.apache.logging.log4j.core.appender.AsyncAppender;
37 import org.apache.logging.log4j.core.async.AsyncLogger;
38 import org.apache.logging.log4j.core.async.AsyncLoggerConfig;
39 import org.apache.logging.log4j.core.async.AsyncLoggerContext;
40 import org.apache.logging.log4j.core.async.DaemonThreadFactory;
41 import org.apache.logging.log4j.core.config.LoggerConfig;
42 import org.apache.logging.log4j.core.impl.Log4jContextFactory;
43 import org.apache.logging.log4j.core.selector.ContextSelector;
44 import org.apache.logging.log4j.core.util.Loader;
45 import org.apache.logging.log4j.spi.LoggerContextFactory;
46 import org.apache.logging.log4j.status.StatusLogger;
47 import org.apache.logging.log4j.util.PropertiesUtil;
48
49
50
51
52
53
54
55 public final class Server {
56
57
58
59
60 public static final String DOMAIN = "org.apache.logging.log4j2";
61 private static final String PROPERTY_DISABLE_JMX = "log4j2.disable.jmx";
62 private static final String PROPERTY_ASYNC_NOTIF = "log4j2.jmx.notify.async";
63 private static final String THREAD_NAME_PREFIX = "log4j2.jmx.notif";
64 private static final StatusLogger LOGGER = StatusLogger.getLogger();
65 static final Executor executor = isJmxDisabled() ? null : createExecutor();
66
67 private Server() {
68 }
69
70
71
72
73
74
75
76
77 private static ExecutorService createExecutor() {
78 final boolean defaultAsync = !isWebApp();
79 final boolean async = PropertiesUtil.getProperties().getBooleanProperty(PROPERTY_ASYNC_NOTIF, defaultAsync);
80 return async ? Executors.newFixedThreadPool(1, new DaemonThreadFactory(THREAD_NAME_PREFIX)) : null;
81 }
82
83
84
85
86
87 private static boolean isWebApp() {
88 return Loader.isClassAvailable("javax.servlet.Servlet");
89 }
90
91
92
93
94
95
96
97
98 public static String escape(final String name) {
99 final StringBuilder sb = new StringBuilder(name.length() * 2);
100 boolean needsQuotes = false;
101 for (int i = 0; i < name.length(); i++) {
102 final char c = name.charAt(i);
103 switch (c) {
104 case '\\':
105 case '*':
106 case '?':
107 case '\"':
108
109 sb.append('\\');
110 needsQuotes = true;
111 break;
112 case ',':
113 case '=':
114 case ':':
115
116 needsQuotes = true;
117 break;
118 case '\r':
119
120 continue;
121 case '\n':
122
123 sb.append("\\n");
124 needsQuotes = true;
125 continue;
126 }
127 sb.append(c);
128 }
129 if (needsQuotes) {
130 sb.insert(0, '\"');
131 sb.append('\"');
132 }
133 return sb.toString();
134 }
135
136 private static boolean isJmxDisabled() {
137 return PropertiesUtil.getProperties().getBooleanProperty(PROPERTY_DISABLE_JMX);
138 }
139
140 public static void reregisterMBeansAfterReconfigure() {
141
142 if (isJmxDisabled()) {
143 LOGGER.debug("JMX disabled for log4j2. Not registering MBeans.");
144 return;
145 }
146 final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
147 reregisterMBeansAfterReconfigure(mbs);
148 }
149
150 public static void reregisterMBeansAfterReconfigure(final MBeanServer mbs) {
151 if (isJmxDisabled()) {
152 LOGGER.debug("JMX disabled for log4j2. Not registering MBeans.");
153 return;
154 }
155
156
157
158 try {
159 final ContextSelector selector = getContextSelector();
160 if (selector == null) {
161 LOGGER.debug("Could not register MBeans: no ContextSelector found.");
162 return;
163 }
164 final List<LoggerContext> contexts = selector.getLoggerContexts();
165 for (final LoggerContext ctx : contexts) {
166
167
168 unregisterLoggerContext(ctx.getName(), mbs);
169
170 final LoggerContextAdmin mbean = new LoggerContextAdmin(ctx, executor);
171 register(mbs, mbean, mbean.getObjectName());
172
173 if (ctx instanceof AsyncLoggerContext) {
174 final RingBufferAdmin rbmbean = AsyncLogger.createRingBufferAdmin(ctx.getName());
175 register(mbs, rbmbean, rbmbean.getObjectName());
176 }
177
178
179
180
181
182
183 registerStatusLogger(ctx.getName(), mbs, executor);
184 registerContextSelector(ctx.getName(), selector, mbs, executor);
185
186 registerLoggerConfigs(ctx, mbs, executor);
187 registerAppenders(ctx, mbs, executor);
188 }
189 } catch (final Exception ex) {
190 LOGGER.error("Could not register mbeans", ex);
191 }
192 }
193
194
195
196
197 public static void unregisterMBeans() {
198 final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
199 unregisterMBeans(mbs);
200 }
201
202
203
204
205
206
207 public static void unregisterMBeans(final MBeanServer mbs) {
208 unregisterStatusLogger("*", mbs);
209 unregisterContextSelector("*", mbs);
210 unregisterContexts(mbs);
211 unregisterLoggerConfigs("*", mbs);
212 unregisterAsyncLoggerRingBufferAdmins("*", mbs);
213 unregisterAsyncLoggerConfigRingBufferAdmins("*", mbs);
214 unregisterAppenders("*", mbs);
215 unregisterAsyncAppenders("*", mbs);
216 }
217
218
219
220
221
222
223 private static ContextSelector getContextSelector() {
224 final LoggerContextFactory factory = LogManager.getFactory();
225 if (factory instanceof Log4jContextFactory) {
226 final ContextSelector selector = ((Log4jContextFactory) factory).getSelector();
227 return selector;
228 }
229 return null;
230 }
231
232
233
234
235
236
237
238 public static void unregisterLoggerContext(final String loggerContextName) {
239 final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
240 unregisterLoggerContext(loggerContextName, mbs);
241 }
242
243
244
245
246
247
248
249
250 public static void unregisterLoggerContext(final String contextName, final MBeanServer mbs) {
251 final String pattern = LoggerContextAdminMBean.PATTERN;
252 final String search = String.format(pattern, escape(contextName), "*");
253 unregisterAllMatching(search, mbs);
254
255
256 unregisterStatusLogger(contextName, mbs);
257 unregisterContextSelector(contextName, mbs);
258 unregisterLoggerConfigs(contextName, mbs);
259 unregisterAppenders(contextName, mbs);
260 unregisterAsyncAppenders(contextName, mbs);
261 unregisterAsyncLoggerRingBufferAdmins(contextName, mbs);
262 unregisterAsyncLoggerConfigRingBufferAdmins(contextName, mbs);
263 }
264
265 private static void registerStatusLogger(final String contextName, final MBeanServer mbs, final Executor executor)
266 throws InstanceAlreadyExistsException, MBeanRegistrationException, NotCompliantMBeanException {
267
268 final StatusLoggerAdmin mbean = new StatusLoggerAdmin(contextName, executor);
269 register(mbs, mbean, mbean.getObjectName());
270 }
271
272 private static void registerContextSelector(final String contextName, final ContextSelector selector,
273 final MBeanServer mbs, final Executor executor) throws InstanceAlreadyExistsException,
274 MBeanRegistrationException, NotCompliantMBeanException {
275
276 final ContextSelectorAdmin mbean = new ContextSelectorAdmin(contextName, selector);
277 register(mbs, mbean, mbean.getObjectName());
278 }
279
280 private static void unregisterStatusLogger(final String contextName, final MBeanServer mbs) {
281 final String pattern = StatusLoggerAdminMBean.PATTERN;
282 final String search = String.format(pattern, escape(contextName), "*");
283 unregisterAllMatching(search, mbs);
284 }
285
286 private static void unregisterContextSelector(final String contextName, final MBeanServer mbs) {
287 final String pattern = ContextSelectorAdminMBean.PATTERN;
288 final String search = String.format(pattern, escape(contextName), "*");
289 unregisterAllMatching(search, mbs);
290 }
291
292 private static void unregisterLoggerConfigs(final String contextName, final MBeanServer mbs) {
293 final String pattern = LoggerConfigAdminMBean.PATTERN;
294 final String search = String.format(pattern, escape(contextName), "*");
295 unregisterAllMatching(search, mbs);
296 }
297
298 private static void unregisterContexts(final MBeanServer mbs) {
299 final String pattern = LoggerContextAdminMBean.PATTERN;
300 final String search = String.format(pattern, "*");
301 unregisterAllMatching(search, mbs);
302 }
303
304 private static void unregisterAppenders(final String contextName, final MBeanServer mbs) {
305 final String pattern = AppenderAdminMBean.PATTERN;
306 final String search = String.format(pattern, escape(contextName), "*");
307 unregisterAllMatching(search, mbs);
308 }
309
310 private static void unregisterAsyncAppenders(final String contextName, final MBeanServer mbs) {
311 final String pattern = AsyncAppenderAdminMBean.PATTERN;
312 final String search = String.format(pattern, escape(contextName), "*");
313 unregisterAllMatching(search, mbs);
314 }
315
316 private static void unregisterAsyncLoggerRingBufferAdmins(final String contextName, final MBeanServer mbs) {
317 final String pattern1 = RingBufferAdminMBean.PATTERN_ASYNC_LOGGER;
318 final String search1 = String.format(pattern1, escape(contextName));
319 unregisterAllMatching(search1, mbs);
320 }
321
322 private static void unregisterAsyncLoggerConfigRingBufferAdmins(final String contextName, final MBeanServer mbs) {
323 final String pattern2 = RingBufferAdminMBean.PATTERN_ASYNC_LOGGER_CONFIG;
324 final String search2 = String.format(pattern2, escape(contextName), "*");
325 unregisterAllMatching(search2, mbs);
326 }
327
328 private static void unregisterAllMatching(final String search, final MBeanServer mbs) {
329 try {
330 final ObjectName pattern = new ObjectName(search);
331 final Set<ObjectName> found = mbs.queryNames(pattern, null);
332 for (final ObjectName objectName : found) {
333 LOGGER.debug("Unregistering MBean {}", objectName);
334 mbs.unregisterMBean(objectName);
335 }
336 } catch (final Exception ex) {
337 LOGGER.error("Could not unregister MBeans for " + search, ex);
338 }
339 }
340
341 private static void registerLoggerConfigs(final LoggerContext ctx, final MBeanServer mbs, final Executor executor)
342 throws InstanceAlreadyExistsException, MBeanRegistrationException, NotCompliantMBeanException {
343
344 final Map<String, LoggerConfig> map = ctx.getConfiguration().getLoggers();
345 for (final String name : map.keySet()) {
346 final LoggerConfig cfg = map.get(name);
347 final LoggerConfigAdmin mbean = new LoggerConfigAdmin(ctx, cfg);
348 register(mbs, mbean, mbean.getObjectName());
349
350 if (cfg instanceof AsyncLoggerConfig) {
351 final AsyncLoggerConfig async = (AsyncLoggerConfig) cfg;
352 final RingBufferAdmin rbmbean = async.createRingBufferAdmin(ctx.getName());
353 register(mbs, rbmbean, rbmbean.getObjectName());
354 }
355 }
356 }
357
358 private static void registerAppenders(final LoggerContext ctx, final MBeanServer mbs, final Executor executor)
359 throws InstanceAlreadyExistsException, MBeanRegistrationException, NotCompliantMBeanException {
360
361 final Map<String, Appender> map = ctx.getConfiguration().getAppenders();
362 for (final String name : map.keySet()) {
363 final Appender appender = map.get(name);
364
365 if (appender instanceof AsyncAppender) {
366 final AsyncAppender async = ((AsyncAppender) appender);
367 final AsyncAppenderAdmin mbean = new AsyncAppenderAdmin(ctx.getName(), async);
368 register(mbs, mbean, mbean.getObjectName());
369 } else {
370 final AppenderAdmin mbean = new AppenderAdmin(ctx.getName(), appender);
371 register(mbs, mbean, mbean.getObjectName());
372 }
373 }
374 }
375
376 private static void register(final MBeanServer mbs, final Object mbean, final ObjectName objectName)
377 throws InstanceAlreadyExistsException, MBeanRegistrationException, NotCompliantMBeanException {
378 LOGGER.debug("Registering MBean {}", objectName);
379 mbs.registerMBean(mbean, objectName);
380 }
381 }