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