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