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