logging-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From rpo...@apache.org
Subject svn commit: r1563745 - in /logging/log4j/log4j2/trunk: log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/ log4j-jmx-gui/src/main/java/org/apache/logging/log4j/jmx/gui/
Date Sun, 02 Feb 2014 23:24:40 GMT
Author: rpopma
Date: Sun Feb  2 23:24:40 2014
New Revision: 1563745

URL: http://svn.apache.org/r1563745
Log:
LOG4J2-500 Better JMX support for multiple contexts (todo: tests & doc updates)

Modified:
    logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/ContextSelectorAdmin.java
    logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/ContextSelectorAdminMBean.java
    logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/Server.java
    logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/StatusLoggerAdmin.java
    logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/StatusLoggerAdminMBean.java
    logging/log4j/log4j2/trunk/log4j-jmx-gui/src/main/java/org/apache/logging/log4j/jmx/gui/Client.java
    logging/log4j/log4j2/trunk/log4j-jmx-gui/src/main/java/org/apache/logging/log4j/jmx/gui/ClientGUI.java

Modified: logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/ContextSelectorAdmin.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/ContextSelectorAdmin.java?rev=1563745&r1=1563744&r2=1563745&view=diff
==============================================================================
--- logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/ContextSelectorAdmin.java
(original)
+++ logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/ContextSelectorAdmin.java
Sun Feb  2 23:24:40 2014
@@ -31,14 +31,22 @@ public class ContextSelectorAdmin implem
 
     /**
      * Constructs a new {@code ContextSelectorAdmin}.
-     *
+     * 
+     * @param contextName name of the LoggerContext under which to register this
+     *            ContextSelectorAdmin. Note that the ContextSelector may be
+     *            registered multiple times, once for each LoggerContext. In web
+     *            containers, each web application has its own LoggerContext and
+     *            by associating the ContextSelector with the LoggerContext, all
+     *            associated MBeans can be unloaded when the web application is
+     *            undeployed.
      * @param selector the instrumented object
      */
-    public ContextSelectorAdmin(final ContextSelector selector) {
+    public ContextSelectorAdmin(final String contextName, final ContextSelector selector)
{
         super();
         this.selector = Assert.isNotNull(selector, "ContextSelector");
         try {
-            objectName = new ObjectName(NAME);
+            final String mbeanName = String.format(PATTERN, Server.escape(contextName));
+            objectName = new ObjectName(mbeanName);
         } catch (final Exception e) {
             throw new IllegalStateException(e);
         }
@@ -46,7 +54,7 @@ public class ContextSelectorAdmin implem
 
     /**
      * Returns the {@code ObjectName} of this mbean.
-     *
+     * 
      * @return the {@code ObjectName}
      * @see ContextSelectorAdminMBean#NAME
      */

Modified: logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/ContextSelectorAdminMBean.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/ContextSelectorAdminMBean.java?rev=1563745&r1=1563744&r2=1563745&view=diff
==============================================================================
--- logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/ContextSelectorAdminMBean.java
(original)
+++ logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/ContextSelectorAdminMBean.java
Sun Feb  2 23:24:40 2014
@@ -21,9 +21,29 @@ package org.apache.logging.log4j.core.jm
  */
 public interface ContextSelectorAdminMBean {
     /**
-     * ObjectName pattern ({@value}) for the context selector MBean.
+     * ObjectName pattern ({@value}) for ContextSelectorAdmin MBeans.
+     * This pattern contains a variable, which is the name of the logger context.
+     * <p>
+     * You can find all registered ContextSelectorAdmin MBeans like this:
+     * </p>
+     * <pre>
+     * MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
+     * String pattern = String.format(ContextSelectorAdminMBean.PATTERN, &quot;*&quot;);
+     * Set&lt;ObjectName&gt; contextSelectorNames = mbs.queryNames(new ObjectName(pattern),
null);
+     * </pre>
+     * <p>
+     * Some characters are not allowed in ObjectNames. The logger context name
+     * may be quoted. When ContextSelectorAdmin MBeans are
+     * registered, their ObjectNames are created using this pattern as follows:
+     * </p>
+     * <pre>
+     * String ctxName = Server.escape(loggerContext.getName());
+     * String name = String.format(PATTERN, ctxName);
+     * ObjectName objectName = new ObjectName(name);
+     * </pre>
+     * @see Server#escape(String)
      */
-    String NAME = "org.apache.logging.log4j2:type=ContextSelector";
+    String PATTERN = "org.apache.logging.log4j2:type=LoggerContext,ctx=%s,sub=ContextSelector";
 
     /**
      * Returns the name of the class implementing the {@code ContextSelector}

Modified: logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/Server.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/Server.java?rev=1563745&r1=1563744&r2=1563745&view=diff
==============================================================================
--- logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/Server.java
(original)
+++ logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/Server.java
Sun Feb  2 23:24:40 2014
@@ -24,7 +24,6 @@ import java.util.concurrent.Executor;
 import java.util.concurrent.Executors;
 
 import javax.management.InstanceAlreadyExistsException;
-import javax.management.JMException;
 import javax.management.MBeanRegistrationException;
 import javax.management.MBeanServer;
 import javax.management.NotCompliantMBeanException;
@@ -105,59 +104,6 @@ public final class Server {
         return sb.toString();
     }
 
-    /**
-     * Creates MBeans to instrument classes in the log4j class hierarchy and
-     * registers the MBeans in the platform MBean server so they can be accessed
-     * by remote clients.
-     * 
-     * @throws JMException if a problem occurs during registration
-     */
-    public static void registerMBeans() throws JMException {
-        final ContextSelector selector = getContextSelector();
-        registerMBeans(selector);
-    }
-
-    /**
-     * Creates MBeans to instrument the specified selector and other classes in
-     * the log4j class hierarchy and registers the MBeans in the platform MBean
-     * server so they can be accessed by remote clients.
-     * 
-     * @param selector starting point in the log4j class hierarchy
-     * @throws JMException if a problem occurs during registration
-     */
-    public static void registerMBeans(final ContextSelector selector) throws JMException
{
-
-        // avoid creating Platform MBean Server if JMX disabled
-        if (Boolean.getBoolean(PROPERTY_DISABLE_JMX)) {
-            LOGGER.debug("JMX disabled for log4j2. Not registering MBeans.");
-            return;
-        }
-        final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
-        registerMBeans(selector, mbs);
-    }
-
-    /**
-     * Creates MBeans to instrument the specified selector and other classes in
-     * the log4j class hierarchy and registers the MBeans in the specified MBean
-     * server so they can be accessed by remote clients.
-     * 
-     * @param selector starting point in the log4j class hierarchy
-     * @param mbs the MBean Server to register the instrumented objects in
-     * @throws JMException if a problem occurs during registration
-     */
-    public static void registerMBeans(final ContextSelector selector, final MBeanServer mbs)
throws JMException {
-
-        if (Boolean.getBoolean(PROPERTY_DISABLE_JMX)) {
-            LOGGER.debug("JMX disabled for log4j2. Not registering MBeans.");
-            return;
-        }
-        registerStatusLogger(mbs, executor);
-        registerContextSelector(selector, mbs, executor);
-
-        final List<LoggerContext> contexts = selector.getLoggerContexts();
-        registerContexts(contexts, mbs, executor);
-    }
-
     public static void reregisterMBeansAfterReconfigure() {
         // avoid creating Platform MBean Server if JMX disabled
         if (Boolean.getBoolean(PROPERTY_DISABLE_JMX)) {
@@ -175,23 +121,43 @@ public final class Server {
         }
 
         // first unregister the old MBeans
+        // TODO is this too drastic? This may impact the MBean of other
+        // webapps...
+        // but below we will only re-register the MBeans of OUR context selector
         unregisterMBeans(mbs);
 
         // now provide instrumentation for the newly configured
         // LoggerConfigs and Appenders
         try {
-            registerStatusLogger(mbs, executor);
             final ContextSelector selector = getContextSelector();
             if (selector == null) {
                 LOGGER.debug("Could not register MBeans: no ContextSelector found.");
                 return;
             }
-            registerContextSelector(selector, mbs, executor);
             final List<LoggerContext> contexts = selector.getLoggerContexts();
-            registerContexts(contexts, mbs, executor);
-            for (LoggerContext context : contexts) {
-                registerLoggerConfigs(context, mbs, executor);
-                registerAppenders(context, mbs, executor);
+            for (LoggerContext ctx : contexts) {
+                // first unregister the context and all nested loggers,
+                // appenders, statusLogger, contextSelector, ringbuffers...
+                unregisterAll(ctx.getName(), mbs);
+
+                final LoggerContextAdmin mbean = new LoggerContextAdmin(ctx, executor);
+                register(mbs, mbean, mbean.getObjectName());
+
+                if (ctx instanceof AsyncLoggerContext) {
+                    RingBufferAdmin rbmbean = AsyncLogger.createRingBufferAdmin(ctx.getName());
+                    register(mbs, rbmbean, rbmbean.getObjectName());
+                }
+
+                // register the status logger and the context selector
+                // repeatedly
+                // for each known context: if one context is unregistered,
+                // these MBeans should still be available for the other
+                // contexts.
+                registerStatusLogger(ctx.getName(), mbs, executor);
+                registerContextSelector(ctx.getName(), selector, mbs, executor);
+
+                registerLoggerConfigs(ctx, mbs, executor);
+                registerAppenders(ctx, mbs, executor);
             }
         } catch (final Exception ex) {
             LOGGER.error("Could not register mbeans", ex);
@@ -212,8 +178,8 @@ public final class Server {
      * @param mbs the MBean server to unregister from.
      */
     public static void unregisterMBeans(MBeanServer mbs) {
-        unregisterStatusLogger(mbs);
-        unregisterContextSelector(mbs);
+        unregisterStatusLogger("*", mbs);
+        unregisterContextSelector("*", mbs);
         unregisterContexts(mbs);
         unregisterLoggerConfigs("*", mbs);
         unregisterAsyncLoggerRingBufferAdmins("*", mbs);
@@ -245,9 +211,9 @@ public final class Server {
      * 
      * @param loggerContextName name of the logger context to unregister
      */
-    public static void unregisterContext(String loggerContextName) {
+    public static void unregisterAll(String loggerContextName) {
         final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
-        unregisterContext(loggerContextName, mbs);
+        unregisterAll(loggerContextName, mbs);
     }
 
     /**
@@ -258,67 +224,51 @@ public final class Server {
      * @param loggerContextName name of the logger context to unregister
      * @param mbs the MBean Server to unregister the instrumented objects from
      */
-    public static void unregisterContext(String contextName, MBeanServer mbs) {
+    public static void unregisterAll(String contextName, MBeanServer mbs) {
         final String pattern = LoggerContextAdminMBean.PATTERN;
-        final String safeContextName = escape(contextName);
-        final String search = String.format(pattern, safeContextName, "*");
+        final String search = String.format(pattern, escape(contextName), "*");
         unregisterAllMatching(search, mbs); // unregister context mbean
-        unregisterLoggerConfigs(safeContextName, mbs);
-        unregisterAppenders(safeContextName, mbs);
-        unregisterAsyncAppenders(safeContextName, mbs);
-        unregisterAsyncLoggerRingBufferAdmins(safeContextName, mbs);
-        unregisterAsyncLoggerConfigRingBufferAdmins(safeContextName, mbs);
+
+        // now unregister all MBeans associated with this logger context
+        unregisterStatusLogger(contextName, mbs);
+        unregisterContextSelector(contextName, mbs);
+        unregisterLoggerConfigs(contextName, mbs);
+        unregisterAppenders(contextName, mbs);
+        unregisterAsyncAppenders(contextName, mbs);
+        unregisterAsyncLoggerRingBufferAdmins(contextName, mbs);
+        unregisterAsyncLoggerConfigRingBufferAdmins(contextName, mbs);
     }
 
-    private static void registerStatusLogger(final MBeanServer mbs, final Executor executor)
+    private static void registerStatusLogger(final String contextName, final MBeanServer
mbs, final Executor executor)
             throws InstanceAlreadyExistsException, MBeanRegistrationException, NotCompliantMBeanException
{
 
-        final StatusLoggerAdmin mbean = new StatusLoggerAdmin(executor);
+        final StatusLoggerAdmin mbean = new StatusLoggerAdmin(contextName, executor);
         register(mbs, mbean, mbean.getObjectName());
     }
 
-    private static void registerContextSelector(final ContextSelector selector, final MBeanServer
mbs,
-            final Executor executor) throws InstanceAlreadyExistsException, MBeanRegistrationException,
-            NotCompliantMBeanException {
+    private static void registerContextSelector(final String contextName, final ContextSelector
selector,
+            final MBeanServer mbs, final Executor executor) throws InstanceAlreadyExistsException,
+            MBeanRegistrationException, NotCompliantMBeanException {
 
-        final ContextSelectorAdmin mbean = new ContextSelectorAdmin(selector);
+        final ContextSelectorAdmin mbean = new ContextSelectorAdmin(contextName, selector);
         register(mbs, mbean, mbean.getObjectName());
     }
 
-    /**
-     * Registers MBeans for all contexts in the list. First unregisters each
-     * context (and nested loggers, appender etc) to prevent
-     * InstanceAlreadyExistsExceptions.
-     */
-    private static void registerContexts(final List<LoggerContext> contexts, final
MBeanServer mbs,
-            final Executor executor) throws InstanceAlreadyExistsException, MBeanRegistrationException,
-            NotCompliantMBeanException {
-
-        for (final LoggerContext ctx : contexts) {
-            // first unregister the context and all nested loggers & appenders
-            unregisterContext(ctx.getName());
-
-            final LoggerContextAdmin mbean = new LoggerContextAdmin(ctx, executor);
-            register(mbs, mbean, mbean.getObjectName());
-
-            if (ctx instanceof AsyncLoggerContext) {
-                RingBufferAdmin rbmbean = AsyncLogger.createRingBufferAdmin(ctx.getName());
-                register(mbs, rbmbean, rbmbean.getObjectName());
-            }
-        }
-    }
-
-    private static void unregisterStatusLogger(final MBeanServer mbs) {
-        unregisterAllMatching(StatusLoggerAdminMBean.NAME, mbs);
+    private static void unregisterStatusLogger(final String contextName, final MBeanServer
mbs) {
+        final String pattern = StatusLoggerAdminMBean.PATTERN;
+        final String search = String.format(pattern, escape(contextName), "*");
+        unregisterAllMatching(search, mbs);
     }
 
-    private static void unregisterContextSelector(final MBeanServer mbs) {
-        unregisterAllMatching(ContextSelectorAdminMBean.NAME, mbs);
+    private static void unregisterContextSelector(final String contextName, final MBeanServer
mbs) {
+        final String pattern = ContextSelectorAdminMBean.PATTERN;
+        final String search = String.format(pattern, escape(contextName), "*");
+        unregisterAllMatching(search, mbs);
     }
 
     private static void unregisterLoggerConfigs(final String contextName, final MBeanServer
mbs) {
         final String pattern = LoggerConfigAdminMBean.PATTERN;
-        final String search = String.format(pattern, contextName, "*");
+        final String search = String.format(pattern, escape(contextName), "*");
         unregisterAllMatching(search, mbs);
     }
 
@@ -330,25 +280,25 @@ public final class Server {
 
     private static void unregisterAppenders(final String contextName, final MBeanServer mbs)
{
         final String pattern = AppenderAdminMBean.PATTERN;
-        final String search = String.format(pattern, contextName, "*");
+        final String search = String.format(pattern, escape(contextName), "*");
         unregisterAllMatching(search, mbs);
     }
 
     private static void unregisterAsyncAppenders(final String contextName, final MBeanServer
mbs) {
         final String pattern = AsyncAppenderAdminMBean.PATTERN;
-        final String search = String.format(pattern, contextName, "*");
+        final String search = String.format(pattern, escape(contextName), "*");
         unregisterAllMatching(search, mbs);
     }
 
     private static void unregisterAsyncLoggerRingBufferAdmins(final String contextName, final
MBeanServer mbs) {
         final String pattern1 = RingBufferAdminMBean.PATTERN_ASYNC_LOGGER;
-        final String search1 = String.format(pattern1, contextName);
+        final String search1 = String.format(pattern1, escape(contextName));
         unregisterAllMatching(search1, mbs);
     }
 
     private static void unregisterAsyncLoggerConfigRingBufferAdmins(final String contextName,
final MBeanServer mbs) {
         final String pattern2 = RingBufferAdminMBean.PATTERN_ASYNC_LOGGER_CONFIG;
-        final String search2 = String.format(pattern2, contextName, "*");
+        final String search2 = String.format(pattern2, escape(contextName), "*");
         unregisterAllMatching(search2, mbs);
     }
 

Modified: logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/StatusLoggerAdmin.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/StatusLoggerAdmin.java?rev=1563745&r1=1563744&r2=1563745&view=diff
==============================================================================
--- logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/StatusLoggerAdmin.java
(original)
+++ logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/StatusLoggerAdmin.java
Sun Feb  2 23:24:40 2014
@@ -32,23 +32,32 @@ import org.apache.logging.log4j.status.S
 /**
  * Implementation of the {@code StatusLoggerAdminMBean} interface.
  */
-public class StatusLoggerAdmin extends NotificationBroadcasterSupport implements
-        StatusListener, StatusLoggerAdminMBean {
+public class StatusLoggerAdmin extends NotificationBroadcasterSupport implements StatusListener,
StatusLoggerAdminMBean {
 
     private final AtomicLong sequenceNo = new AtomicLong();
     private final ObjectName objectName;
+    private final String contextName;
     private Level level = Level.WARN;
 
     /**
      * Constructs a new {@code StatusLoggerAdmin} with the {@code Executor} to
      * be used for sending {@code Notification}s asynchronously to listeners.
-     *
+     * 
+     * @param contextName name of the LoggerContext under which to register this
+     *            StatusLoggerAdmin. Note that the StatusLogger may be
+     *            registered multiple times, once for each LoggerContext. In web
+     *            containers, each web application has its own LoggerContext and
+     *            by associating the StatusLogger with the LoggerContext, all
+     *            associated MBeans can be unloaded when the web application is
+     *            undeployed.
      * @param executor used to send notifications asynchronously
      */
-    public StatusLoggerAdmin(final Executor executor) {
+    public StatusLoggerAdmin(final String contextName, final Executor executor) {
         super(executor, createNotificationInfo());
+        this.contextName = contextName;
         try {
-            objectName = new ObjectName(NAME);
+            final String mbeanName = String.format(PATTERN, Server.escape(contextName));
+            objectName = new ObjectName(mbeanName);
         } catch (final Exception e) {
             throw new IllegalStateException(e);
         }
@@ -93,28 +102,31 @@ public class StatusLoggerAdmin extends N
         this.level = Level.toLevel(level, Level.ERROR);
     }
 
+    public String getContextName() {
+        return contextName;
+    }
+
     /*
      * (non-Javadoc)
-     *
+     * 
      * @see
      * org.apache.logging.log4j.status.StatusListener#log(org.apache.logging
      * .log4j.status.StatusData)
      */
     @Override
     public void log(final StatusData data) {
-        final Notification notifMsg = new Notification(NOTIF_TYPE_MESSAGE,
-                getObjectName(), nextSeqNo(), now(), data.getFormattedStatus());
+        final Notification notifMsg = new Notification(NOTIF_TYPE_MESSAGE, getObjectName(),
nextSeqNo(), now(),
+                data.getFormattedStatus());
         sendNotification(notifMsg);
 
-        final Notification notifData = new Notification(NOTIF_TYPE_DATA,
-                getObjectName(), nextSeqNo(), now());
+        final Notification notifData = new Notification(NOTIF_TYPE_DATA, getObjectName(),
nextSeqNo(), now());
         notifData.setUserData(data);
         sendNotification(notifData);
     }
 
     /**
      * Returns the {@code ObjectName} of this mbean.
-     *
+     * 
      * @return the {@code ObjectName}
      * @see StatusLoggerAdminMBean#NAME
      */

Modified: logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/StatusLoggerAdminMBean.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/StatusLoggerAdminMBean.java?rev=1563745&r1=1563744&r2=1563745&view=diff
==============================================================================
--- logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/StatusLoggerAdminMBean.java
(original)
+++ logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/StatusLoggerAdminMBean.java
Sun Feb  2 23:24:40 2014
@@ -18,14 +18,38 @@ package org.apache.logging.log4j.core.jm
 
 import java.util.List;
 
+import javax.management.ObjectName;
+
 import org.apache.logging.log4j.status.StatusData;
 
 /**
  * The MBean interface for monitoring and managing the {@code StatusLogger}.
  */
 public interface StatusLoggerAdminMBean {
-    /** Object name ({@value}) of this MBean. */
-    String NAME = "org.apache.logging.log4j2:type=StatusLogger";
+    /**
+     * ObjectName pattern ({@value}) for StatusLoggerAdmin MBeans.
+     * This pattern contains a variable, which is the name of the logger context.
+     * <p>
+     * You can find all registered StatusLoggerAdmin MBeans like this:
+     * </p>
+     * <pre>
+     * MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
+     * String pattern = String.format(StatusLoggerAdminMBean.PATTERN, &quot;*&quot;);
+     * Set&lt;ObjectName&gt; statusLoggerNames = mbs.queryNames(new ObjectName(pattern),
null);
+     * </pre>
+     * <p>
+     * Some characters are not allowed in ObjectNames. The logger context name
+     * may be quoted. When StatusLoggerAdmin MBeans are
+     * registered, their ObjectNames are created using this pattern as follows:
+     * </p>
+     * <pre>
+     * String ctxName = Server.escape(loggerContext.getName());
+     * String name = String.format(PATTERN, ctxName);
+     * ObjectName objectName = new ObjectName(name);
+     * </pre>
+     * @see Server#escape(String)
+     */
+    String PATTERN = "org.apache.logging.log4j2:type=LoggerContext,ctx=%s,sub=StatusLogger";
 
     /**
      * Notifications with this type have a {@code StatusData} userData object
@@ -40,6 +64,12 @@ public interface StatusLoggerAdminMBean 
     String NOTIF_TYPE_MESSAGE = "com.apache.logging.log4j.core.jmx.statuslogger.message";
 
     /**
+     * Returns the {@code ObjectName} that this status logger mbean is registered with.
+     * @return
+     */
+    public ObjectName getObjectName();
+    
+    /**
      * Returns a list with the most recent {@code StatusData} objects in the
      * status history. The list has up to 200 entries by default but the length
      * can be configured with system property {@code "log4j2.status.entries"}.
@@ -81,4 +111,9 @@ public interface StatusLoggerAdminMBean 
      */
     void setLevel(String level);
 
+    /**
+     * Returns the name of the LoggerContext that the {@code StatusLogger} is associated
with.
+     * @return logger context name
+     */
+    String getContextName();
 }

Modified: logging/log4j/log4j2/trunk/log4j-jmx-gui/src/main/java/org/apache/logging/log4j/jmx/gui/Client.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-jmx-gui/src/main/java/org/apache/logging/log4j/jmx/gui/Client.java?rev=1563745&r1=1563744&r2=1563745&view=diff
==============================================================================
--- logging/log4j/log4j2/trunk/log4j-jmx-gui/src/main/java/org/apache/logging/log4j/jmx/gui/Client.java
(original)
+++ logging/log4j/log4j2/trunk/log4j-jmx-gui/src/main/java/org/apache/logging/log4j/jmx/gui/Client.java
Sun Feb  2 23:24:40 2014
@@ -21,6 +21,7 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.Set;
 
+import javax.management.JMException;
 import javax.management.JMX;
 import javax.management.MBeanServerConnection;
 import javax.management.MalformedObjectNameException;
@@ -30,6 +31,7 @@ import javax.management.remote.JMXConnec
 import org.apache.logging.log4j.core.helpers.Assert;
 import org.apache.logging.log4j.core.jmx.ContextSelectorAdminMBean;
 import org.apache.logging.log4j.core.jmx.LoggerContextAdminMBean;
+import org.apache.logging.log4j.core.jmx.Server;
 import org.apache.logging.log4j.core.jmx.StatusLoggerAdminMBean;
 
 /**
@@ -39,22 +41,21 @@ import org.apache.logging.log4j.core.jmx
 public class Client {
     private JMXConnector connector;
     private final MBeanServerConnection connection;
-    private StatusLoggerAdminMBean statusLoggerAdmin;
-    private ContextSelectorAdminMBean contextSelectorAdmin;
+    private List<StatusLoggerAdminMBean> statusLoggerAdminList;
+    private List<ContextSelectorAdminMBean> contextSelectorAdminList;
     private List<LoggerContextAdminMBean> contextAdminList;
 
     /**
      * Constructs a new {@code Client} object and creates proxies for all known
      * remote MBeans.
-     *
+     * 
      * @param connector used to create the MBean server connection through which
      *            to communicate with the remote mbeans
      * @throws MalformedObjectNameException if a problem occurred identifying
      *             one of the remote mbeans
      * @throws IOException if the connection failed
      */
-    public Client(final JMXConnector connector) throws MalformedObjectNameException,
-            IOException {
+    public Client(final JMXConnector connector) throws JMException, IOException {
         this.connector = Assert.isNotNull(connector, "JMXConnector");
         this.connector.connect();
         this.connection = connector.getMBeanServerConnection();
@@ -64,33 +65,40 @@ public class Client {
     /**
      * Constructs a new {@code Client} object and creates proxies for all known
      * remote MBeans.
-     *
+     * 
      * @param mBeanServerConnection the MBean server connection through which to
      *            communicate with the remote mbeans
      * @throws MalformedObjectNameException if a problem occurred identifying
      *             one of the remote mbeans
      * @throws IOException if the connection failed
      */
-    public Client(final MBeanServerConnection mBeanServerConnection)
-            throws MalformedObjectNameException, IOException {
+    public Client(final MBeanServerConnection mBeanServerConnection) throws JMException,
IOException {
         this.connection = mBeanServerConnection;
         init();
     }
 
-    private void init() throws MalformedObjectNameException, IOException {
-        statusLoggerAdmin = JMX.newMBeanProxy(connection, //
-                new ObjectName(StatusLoggerAdminMBean.NAME), //
-                StatusLoggerAdminMBean.class, true);
-
-        contextSelectorAdmin = JMX.newMBeanProxy(connection, //
-                new ObjectName(ContextSelectorAdminMBean.NAME), //
-                ContextSelectorAdminMBean.class, false);
+    private void init() throws JMException, IOException {
+        statusLoggerAdminList = new ArrayList<StatusLoggerAdminMBean>();
+        final Set<ObjectName> statusLogNames = find(StatusLoggerAdminMBean.PATTERN);
+        for (final ObjectName statusLogName : statusLogNames) {
+            final StatusLoggerAdminMBean ctx = JMX.newMBeanProxy(connection, //
+                    statusLogName, //
+                    StatusLoggerAdminMBean.class, true); // notificationBroadcaster
+            statusLoggerAdminList.add(ctx);
+        }
+
+        contextSelectorAdminList = new ArrayList<ContextSelectorAdminMBean>();
+        final Set<ObjectName> selectorNames = find(ContextSelectorAdminMBean.PATTERN);
+        for (final ObjectName selectorName : selectorNames) {
+            final ContextSelectorAdminMBean ctx = JMX.newMBeanProxy(connection, //
+                    selectorName, //
+                    ContextSelectorAdminMBean.class, false);
+            contextSelectorAdminList.add(ctx);
+        }
 
         contextAdminList = new ArrayList<LoggerContextAdminMBean>();
-        final String pattern = String.format(LoggerContextAdminMBean.PATTERN, "*");
-        final ObjectName search = new ObjectName(pattern);
-        final Set<ObjectName> found = connection.queryNames(search, null);
-        for (final ObjectName contextName : found) {
+        final Set<ObjectName> contextNames = find(LoggerContextAdminMBean.PATTERN);
+        for (final ObjectName contextName : contextNames) {
             final LoggerContextAdminMBean ctx = JMX.newMBeanProxy(connection, //
                     contextName, //
                     LoggerContextAdminMBean.class, false);
@@ -100,20 +108,27 @@ public class Client {
         }
     }
 
+    private Set<ObjectName> find(String pattern) throws JMException, IOException {
+        final ObjectName search = new ObjectName(String.format(pattern, "*"));
+        final Set<ObjectName> result = connection.queryNames(search, null);
+        return result;
+    }
+
     /**
-     * Returns a proxy that allows operations to be performed on the remote
-     * {@code ContextSelectorAdminMBean}.
-     *
-     * @return a proxy to the remote {@code ContextSelectorAdminMBean}
+     * Returns a list of proxies that allows operations to be performed on the
+     * remote {@code ContextSelectorAdminMBean}s.
+     * 
+     * @return a list of proxies to the remote {@code ContextSelectorAdminMBean}
+     *         s
      */
-    public ContextSelectorAdminMBean getContextSelectorAdmin() {
-        return contextSelectorAdmin;
+    public List<ContextSelectorAdminMBean> getContextSelectorAdminList() {
+        return contextSelectorAdminList;
     }
 
     /**
      * Returns a list of proxies that allow operations to be performed on the
      * remote {@code LoggerContextAdminMBean}s.
-     *
+     * 
      * @return a list of proxies to the remote {@code LoggerContextAdminMBean}s
      */
     public List<LoggerContextAdminMBean> getLoggerContextAdmins() {
@@ -135,7 +150,7 @@ public class Client {
     /**
      * Returns the MBean server connection through which to communicate with the
      * remote mbeans.
-     *
+     * 
      * @return the MBean server connection
      */
     public MBeanServerConnection getConnection() {
@@ -143,12 +158,39 @@ public class Client {
     }
 
     /**
-     * Returns a proxy that allows operations to be performed on the remote
-     * {@code StatusLoggerAdminMBean}.
-     *
-     * @return a proxy to the remote {@code StatusLoggerAdminMBean}
+     * Returns a list of proxies that allows operations to be performed on the
+     * remote {@code StatusLoggerAdminMBean}s.
+     * 
+     * @return a list of proxies to the remote {@code StatusLoggerAdminMBean}s
      */
-    public StatusLoggerAdminMBean getStatusLoggerAdmin() {
-        return statusLoggerAdmin;
+    public List<StatusLoggerAdminMBean> getStatusLoggerAdminList() {
+        return statusLoggerAdminList;
+    }
+
+    /**
+     * Returns the {@code StatusLoggerAdminMBean} associated with the specified
+     * context name, or {@code null}.
+     * 
+     * @param contextName search key
+     * @return StatusLoggerAdminMBean or null
+     * @throws MalformedObjectNameException
+     * @throws IOException
+     */
+    public StatusLoggerAdminMBean getStatusLoggerAdmin(String contextName) throws MalformedObjectNameException,
+            IOException {
+        final String pattern = StatusLoggerAdminMBean.PATTERN;
+        final String mbean = String.format(pattern, Server.escape(contextName));
+        final ObjectName search = new ObjectName(mbean);
+        final Set<ObjectName> result = connection.queryNames(search, null);
+        if (result.size() == 0) {
+            return null;
+        }
+        if (result.size() > 1) {
+            System.err.println("WARN: multiple status loggers found for " + contextName +
": " + result);
+        }
+        final StatusLoggerAdminMBean proxy = JMX.newMBeanProxy(connection, //
+                result.iterator().next(), //
+                StatusLoggerAdminMBean.class, true); // notificationBroadcaster
+        return proxy;
     }
 }

Modified: logging/log4j/log4j2/trunk/log4j-jmx-gui/src/main/java/org/apache/logging/log4j/jmx/gui/ClientGUI.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-jmx-gui/src/main/java/org/apache/logging/log4j/jmx/gui/ClientGUI.java?rev=1563745&r1=1563744&r2=1563745&view=diff
==============================================================================
--- logging/log4j/log4j2/trunk/log4j-jmx-gui/src/main/java/org/apache/logging/log4j/jmx/gui/ClientGUI.java
(original)
+++ logging/log4j/log4j2/trunk/log4j-jmx-gui/src/main/java/org/apache/logging/log4j/jmx/gui/ClientGUI.java
Sun Feb  2 23:24:40 2014
@@ -53,88 +53,102 @@ import org.apache.logging.log4j.core.jmx
 import org.apache.logging.log4j.core.jmx.StatusLoggerAdminMBean;
 
 /**
- * Swing GUI that connects to a Java process via JMX and allows the user to view and
- * modify the Log4j 2 configuration, as well as monitor status logs.
- *
+ * Swing GUI that connects to a Java process via JMX and allows the user to view
+ * and modify the Log4j 2 configuration, as well as monitor status logs.
+ * 
  * @see <a href=
  *      "http://docs.oracle.com/javase/6/docs/technotes/guides/management/jconsole.html"
- *      >http://docs.oracle.com/javase/6/docs/technotes/guides/management/jconsole.html</a
>
+ *      >http://docs.oracle.com/javase/6/docs/technotes/guides/management/
+ *      jconsole.html</a >
  */
 public class ClientGUI extends JPanel implements NotificationListener {
     private static final long serialVersionUID = -253621277232291174L;
     private final Client client;
-    private JTextArea statusLogTextArea;
-    private JTabbedPane tabbedPane;
-    private JToggleButton wrapLinesToggleButton;
+    private Map<String, JTextArea> statusLogTextAreaMap = new HashMap<String, JTextArea>();
+    private JTabbedPane tabbedPaneContexts;
 
-    private final AbstractAction toggleWrapAction = new AbstractAction() {
-        private static final long serialVersionUID = -4214143754637722322L;
-
-        @Override
-        public void actionPerformed(final ActionEvent e) {
-            final boolean wrap = wrapLinesToggleButton.isSelected();
-            statusLogTextArea.setLineWrap(wrap);
-        }
-    };
-
-    public ClientGUI(final Client client) throws InstanceNotFoundException,
-            MalformedObjectNameException, IOException {
+    public ClientGUI(final Client client) throws InstanceNotFoundException, MalformedObjectNameException,
IOException {
         this.client = Assert.isNotNull(client, "client");
         createWidgets();
         populateWidgets();
-        registerListeners();
     }
 
     private void createWidgets() {
-        statusLogTextArea = new JTextArea();
-        statusLogTextArea.setEditable(false);
-        statusLogTextArea.setBackground(this.getBackground());
-        statusLogTextArea.setForeground(Color.black);
-        statusLogTextArea.setFont(new Font("Monospaced", Font.PLAIN,
-                statusLogTextArea.getFont().getSize()));
-        statusLogTextArea.setWrapStyleWord(true);
-
-        wrapLinesToggleButton = new JToggleButton(toggleWrapAction);
-        wrapLinesToggleButton.setToolTipText("Toggle line wrapping");
-        final JScrollPane scrollStatusLog = new JScrollPane(statusLogTextArea, //
-                ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS, //
-                ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
-        scrollStatusLog.setCorner(ScrollPaneConstants.LOWER_RIGHT_CORNER, wrapLinesToggleButton);
-
-        tabbedPane = new JTabbedPane();
-        tabbedPane.addTab("StatusLogger", scrollStatusLog);
-
+        tabbedPaneContexts = new JTabbedPane();
         this.setLayout(new BorderLayout());
-        this.add(tabbedPane, BorderLayout.CENTER);
+        this.add(tabbedPaneContexts, BorderLayout.CENTER);
     }
 
-    private void populateWidgets() {
-
-        final StatusLoggerAdminMBean statusAdmin = client.getStatusLoggerAdmin();
-        final String[] messages = statusAdmin.getStatusDataHistory();
-        for (final String message : messages) {
-            statusLogTextArea.append(message + "\n");
-        }
+    private void populateWidgets() throws MalformedObjectNameException, IOException, InstanceNotFoundException
{
 
         for (final LoggerContextAdminMBean ctx : client.getLoggerContextAdmins()) {
+            JTabbedPane contextTabs = new JTabbedPane();
+            tabbedPaneContexts.addTab("LoggerContext: " + ctx.getName(), contextTabs);
+
+            String contextName = ctx.getName();
+            StatusLoggerAdminMBean status = client.getStatusLoggerAdmin(contextName);
+            if (status != null) {
+                JTextArea text = createTextArea();
+                final String[] messages = status.getStatusDataHistory();
+                for (final String message : messages) {
+                    text.append(message + "\n");
+                }
+                statusLogTextAreaMap.put(status.getContextName(), text);
+                registerListeners(status);
+                JScrollPane scroll = scroll(text);
+                contextTabs.addTab("StatusLogger", scroll);
+            }
+
             final ClientEditConfigPanel editor = new ClientEditConfigPanel(ctx);
-            tabbedPane.addTab("LoggerContext: " + ctx.getName(), editor);
+            contextTabs.addTab("Configuration", editor);
         }
     }
 
-    private void registerListeners() throws InstanceNotFoundException,
+    private JTextArea createTextArea() {
+        JTextArea result = new JTextArea();
+        result.setEditable(false);
+        result.setBackground(this.getBackground());
+        result.setForeground(Color.black);
+        result.setFont(new Font("Monospaced", Font.PLAIN, result.getFont().getSize()));
+        result.setWrapStyleWord(true);
+        return result;
+    }
+
+    private JScrollPane scroll(final JTextArea text) {
+        final JToggleButton toggleButton = new JToggleButton();
+        toggleButton.setAction(new AbstractAction() {
+            private static final long serialVersionUID = -4214143754637722322L;
+
+            @Override
+            public void actionPerformed(final ActionEvent e) {
+                final boolean wrap = toggleButton.isSelected();
+                text.setLineWrap(wrap);
+            }
+        });
+        toggleButton.setToolTipText("Toggle line wrapping");
+        final JScrollPane scrollStatusLog = new JScrollPane(text, //
+                ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS, //
+                ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
+        scrollStatusLog.setCorner(ScrollPaneConstants.LOWER_RIGHT_CORNER, toggleButton);
+        return scrollStatusLog;
+    }
+
+    private void registerListeners(StatusLoggerAdminMBean status) throws InstanceNotFoundException,
             MalformedObjectNameException, IOException {
         final NotificationFilterSupport filter = new NotificationFilterSupport();
         filter.enableType(StatusLoggerAdminMBean.NOTIF_TYPE_MESSAGE);
-        final ObjectName objName = new ObjectName(StatusLoggerAdminMBean.NAME);
+        final ObjectName objName = status.getObjectName();
         client.getConnection().addNotificationListener(objName, this, filter,
-                null);
+                status.getContextName());
     }
 
     @Override
     public void handleNotification(final Notification notif, final Object paramObject) {
         if (StatusLoggerAdminMBean.NOTIF_TYPE_MESSAGE.equals(notif.getType())) {
-            statusLogTextArea.append(notif.getMessage() + "\n");
+            JTextArea text = statusLogTextAreaMap.get(paramObject);
+            if (text != null) {
+                text.append(notif.getMessage() + "\n");
+            }
         }
     }
 
@@ -144,7 +158,7 @@ public class ClientGUI extends JPanel im
      * Useful links:
      * http://www.componative.com/content/controller/developer/insights
      * /jconsole3/
-     *
+     * 
      * @param args must have at least one parameter, which specifies the
      *            location to connect to. Must be of the form {@code host:port}
      *            or {@code service:jmx:rmi:///jndi/rmi://<host>:<port>/jmxrmi}
@@ -192,8 +206,7 @@ public class ClientGUI extends JPanel im
                     // visible
                     final StringWriter sr = new StringWriter();
                     ex.printStackTrace(new PrintWriter(sr));
-                    JOptionPane.showMessageDialog(null, sr.toString(), "Error",
-                            JOptionPane.ERROR_MESSAGE);
+                    JOptionPane.showMessageDialog(null, sr.toString(), "Error", JOptionPane.ERROR_MESSAGE);
                 }
             }
         });
@@ -202,8 +215,7 @@ public class ClientGUI extends JPanel im
     private static void usage() {
         final String me = ClientGUI.class.getName();
         System.err.println("Usage: java " + me + " <host>:<port>");
-        System.err.println("   or: java " + me
-                + " service:jmx:rmi:///jndi/rmi://<host>:<port>/jmxrmi");
+        System.err.println("   or: java " + me + " service:jmx:rmi:///jndi/rmi://<host>:<port>/jmxrmi");
         final String longAdr = " service:jmx:rmi://<host>:<port>/jndi/rmi://<host>:<port>/jmxrmi";
         System.err.println("   or: java " + me + longAdr);
     }



Mime
View raw message