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