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