You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by rp...@apache.org on 2013/10/16 09:16:54 UTC

svn commit: r1532659 - in /logging/log4j/log4j2/trunk: log4j-core/src/main/java/org/apache/logging/log4j/core/appender/ log4j-core/src/main/java/org/apache/logging/log4j/core/async/ log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/ src/changes/

Author: rpopma
Date: Wed Oct 16 07:16:54 2013
New Revision: 1532659

URL: http://svn.apache.org/r1532659
Log:
LOG4J2-423 Added MBeans for instrumenting AsyncAppenders and AsyncLogger RingBuffers, exposing queue size and remaining capacity

Added:
    logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/AsyncAppenderAdmin.java   (with props)
    logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/AsyncAppenderAdminMBean.java   (with props)
    logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/RingBufferAdmin.java   (with props)
    logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/RingBufferAdminMBean.java   (with props)
Modified:
    logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/AsyncAppender.java
    logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLogger.java
    logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLoggerConfig.java
    logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLoggerConfigHelper.java
    logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/AppenderAdmin.java
    logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/AppenderAdminMBean.java
    logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/Server.java
    logging/log4j/log4j2/trunk/src/changes/changes.xml

Modified: logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/AsyncAppender.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/AsyncAppender.java?rev=1532659&r1=1532658&r2=1532659&view=diff
==============================================================================
--- logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/AsyncAppender.java (original)
+++ logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/AsyncAppender.java Wed Oct 16 07:16:54 2013
@@ -53,6 +53,7 @@ public final class AsyncAppender extends
     private static final String SHUTDOWN = "Shutdown";
 
     private final BlockingQueue<Serializable> queue;
+    private final int queueSize;
     private final boolean blocking;
     private final Configuration config;
     private final AppenderRef[] appenderRefs;
@@ -69,6 +70,7 @@ public final class AsyncAppender extends
                            final boolean includeLocation) {
         super(name, filter, null, ignoreExceptions);
         this.queue = new ArrayBlockingQueue<Serializable>(queueSize);
+        this.queueSize = queueSize;
         this.blocking = blocking;
         this.config = config;
         this.appenderRefs = appenderRefs;
@@ -264,4 +266,52 @@ public final class AsyncAppender extends
             }
         }
     }
+
+    /**
+     * Returns the names of the appenders that this asyncAppender delegates to
+     * as an array of Strings.
+     * @return the names of the sink appenders
+     */
+    public String[] getAppenderRefStrings() {
+        final String[] result = new String[appenderRefs.length];
+        for (int i = 0; i < result.length; i++) {
+            result[i] = appenderRefs[i].getRef();
+        }
+        return result;
+    }
+    
+    /**
+     * Returns {@code true} if this AsyncAppender will take a snapshot of the stack with
+     * every log event to determine the class and method where the logging call
+     * was made.
+     * @return {@code true} if location is included with every event, {@code false} otherwise
+     */
+    public boolean isIncludeLocation() {
+        return includeLocation;
+    }
+    
+    /**
+     * Returns {@code true} if this AsyncAppender will block when the queue is full,
+     * or {@code false} if events are dropped when the queue is full.
+     * @return whether this AsyncAppender will block or drop events when the queue is full.
+     */
+    public boolean isBlocking() {
+        return blocking;
+    }
+    
+    /**
+     * Returns the name of the appender that any errors are logged to or {@code null}.
+     * @return the name of the appender that any errors are logged to or {@code null}
+     */
+    public String getErrorRef() {
+        return errorRef;
+    }
+    
+    public int getQueueCapacity() {
+        return queueSize;
+    }
+    
+    public int getQueueRemainingCapacity() {
+        return queue.remainingCapacity();
+    }
 }

Modified: logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLogger.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLogger.java?rev=1532659&r1=1532658&r2=1532659&view=diff
==============================================================================
--- logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLogger.java (original)
+++ logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLogger.java Wed Oct 16 07:16:54 2013
@@ -29,6 +29,7 @@ import org.apache.logging.log4j.core.con
 import org.apache.logging.log4j.core.helpers.Clock;
 import org.apache.logging.log4j.core.helpers.ClockFactory;
 import org.apache.logging.log4j.core.impl.Log4jLogEvent;
+import org.apache.logging.log4j.core.jmx.RingBufferAdmin;
 import org.apache.logging.log4j.message.Message;
 import org.apache.logging.log4j.message.MessageFactory;
 import org.apache.logging.log4j.status.StatusLogger;
@@ -65,11 +66,12 @@ import com.lmax.disruptor.util.Util;
  * at all.
  * <p>
  * For best performance, use AsyncLogger with the RandomAccessFileAppender or
- * RollingRandomAccessFileAppender, with immediateFlush=false. These appenders have
- * built-in support for the batching mechanism used by the Disruptor library,
- * and they will flush to disk at the end of each batch. This means that even
- * with immediateFlush=false, there will never be any items left in the buffer;
- * all log events will all be written to disk in a very efficient manner.
+ * RollingRandomAccessFileAppender, with immediateFlush=false. These appenders
+ * have built-in support for the batching mechanism used by the Disruptor
+ * library, and they will flush to disk at the end of each batch. This means
+ * that even with immediateFlush=false, there will never be any items left in
+ * the buffer; all log events will all be written to disk in a very efficient
+ * manner.
  */
 public class AsyncLogger extends Logger {
     private static final int HALF_A_SECOND = 500;
@@ -89,36 +91,32 @@ public class AsyncLogger extends Logger 
         final int ringBufferSize = calculateRingBufferSize();
 
         final WaitStrategy waitStrategy = createWaitStrategy();
-        disruptor = new Disruptor<RingBufferLogEvent>(
-                RingBufferLogEvent.FACTORY, ringBufferSize, executor,
+        disruptor = new Disruptor<RingBufferLogEvent>(RingBufferLogEvent.FACTORY, ringBufferSize, executor,
                 ProducerType.MULTI, waitStrategy);
         final EventHandler<RingBufferLogEvent>[] handlers = new RingBufferLogEventHandler[] {//
         new RingBufferLogEventHandler() };
         disruptor.handleExceptionsWith(getExceptionHandler());
         disruptor.handleEventsWith(handlers);
 
-        LOGGER.debug(
-                "Starting AsyncLogger disruptor with ringbuffer size {}...",
-                disruptor.getRingBuffer().getBufferSize());
+        LOGGER.debug("Starting AsyncLogger disruptor with ringbuffer size {}...", disruptor.getRingBuffer()
+                .getBufferSize());
         disruptor.start();
     }
 
     private static int calculateRingBufferSize() {
         int ringBufferSize = RINGBUFFER_DEFAULT_SIZE;
-        final String userPreferredRBSize = System.getProperty(
-                "AsyncLogger.RingBufferSize", String.valueOf(ringBufferSize));
+        final String userPreferredRBSize = System.getProperty("AsyncLogger.RingBufferSize",
+                String.valueOf(ringBufferSize));
         try {
             int size = Integer.parseInt(userPreferredRBSize);
             if (size < RINGBUFFER_MIN_SIZE) {
                 size = RINGBUFFER_MIN_SIZE;
-                LOGGER.warn(
-                        "Invalid RingBufferSize {}, using minimum size {}.",
-                        userPreferredRBSize, RINGBUFFER_MIN_SIZE);
+                LOGGER.warn("Invalid RingBufferSize {}, using minimum size {}.", userPreferredRBSize,
+                        RINGBUFFER_MIN_SIZE);
             }
             ringBufferSize = size;
         } catch (final Exception ex) {
-            LOGGER.warn("Invalid RingBufferSize {}, using default size {}.",
-                    userPreferredRBSize, ringBufferSize);
+            LOGGER.warn("Invalid RingBufferSize {}, using default size {}.", userPreferredRBSize, ringBufferSize);
         }
         return Util.ceilingNextPowerOfTwo(ringBufferSize);
     }
@@ -148,16 +146,12 @@ public class AsyncLogger extends Logger 
         }
         try {
             @SuppressWarnings("unchecked")
-            final
-            Class<? extends ExceptionHandler> klass = (Class<? extends ExceptionHandler>) Class
-                    .forName(cls);
+            final Class<? extends ExceptionHandler> klass = (Class<? extends ExceptionHandler>) Class.forName(cls);
             final ExceptionHandler result = klass.newInstance();
             LOGGER.debug("AsyncLogger.ExceptionHandler=" + result);
             return result;
         } catch (final Exception ignored) {
-            LOGGER.debug(
-                    "AsyncLogger.ExceptionHandler not set: error creating "
-                            + cls + ": ", ignored);
+            LOGGER.debug("AsyncLogger.ExceptionHandler not set: error creating " + cls + ": ", ignored);
             return null;
         }
     }
@@ -165,13 +159,12 @@ public class AsyncLogger extends Logger 
     /**
      * Constructs an {@code AsyncLogger} with the specified context, name and
      * message factory.
-     *
+     * 
      * @param context context of this logger
      * @param name name of this logger
      * @param messageFactory message factory of this logger
      */
-    public AsyncLogger(final LoggerContext context, final String name,
-            final MessageFactory messageFactory) {
+    public AsyncLogger(final LoggerContext context, final String name, final MessageFactory messageFactory) {
         super(context, name, messageFactory);
     }
 
@@ -184,8 +177,7 @@ public class AsyncLogger extends Logger 
     }
 
     @Override
-    public void log(final Marker marker, final String fqcn, final Level level, final Message data,
-            final Throwable t) {
+    public void log(final Marker marker, final String fqcn, final Level level, final Message data, final Throwable t) {
         Info info = threadlocalInfo.get();
         if (info == null) {
             info = new Info();
@@ -195,8 +187,7 @@ public class AsyncLogger extends Logger 
         }
 
         final boolean includeLocation = config.loggerConfig.isIncludeLocation();
-        info.translator.setValues(this, getName(), marker, fqcn, level, data,
-                t, //
+        info.translator.setValues(this, getName(), marker, fqcn, level, data, t, //
 
                 // config properties are taken care of in the EventHandler
                 // thread in the #actualAsyncLog method
@@ -230,13 +221,12 @@ public class AsyncLogger extends Logger 
     /**
      * This method is called by the EventHandler that processes the
      * RingBufferLogEvent in a separate thread.
-     *
+     * 
      * @param event the event to log
      */
     public void actualAsyncLog(final RingBufferLogEvent event) {
         final Map<Property, Boolean> properties = config.loggerConfig.getProperties();
-        event.mergePropertiesIntoContextMap(properties,
-                config.config.getStrSubstitutor());
+        event.mergePropertiesIntoContextMap(properties, config.config.getStrSubstitutor());
         config.logEvent(event);
     }
 
@@ -264,4 +254,14 @@ public class AsyncLogger extends Logger 
         executor.shutdown(); // finally, kill the processor thread
         threadlocalInfo = new ThreadLocal<Info>(); // LOG4J2-323
     }
+
+    /**
+     * Creates and returns a new {@code RingBufferAdmin} that instruments the
+     * ringbuffer of the {@code AsyncLogger}.
+     * 
+     * @param contextName name of the global {@code AsyncLoggerContext}
+     */
+    public static RingBufferAdmin createRingBufferAdmin(String contextName) {
+        return RingBufferAdmin.forAsyncLogger(disruptor.getRingBuffer(), contextName);
+    }
 }

Modified: logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLoggerConfig.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLoggerConfig.java?rev=1532659&r1=1532658&r2=1532659&view=diff
==============================================================================
--- logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLoggerConfig.java (original)
+++ logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLoggerConfig.java Wed Oct 16 07:16:54 2013
@@ -33,6 +33,7 @@ import org.apache.logging.log4j.core.con
 import org.apache.logging.log4j.core.config.plugins.PluginElement;
 import org.apache.logging.log4j.core.config.plugins.PluginFactory;
 import org.apache.logging.log4j.core.helpers.Booleans;
+import org.apache.logging.log4j.core.jmx.RingBufferAdmin;
 
 /**
  * Asynchronous Logger object that is created via configuration and can be
@@ -131,6 +132,16 @@ public class AsyncLoggerConfig extends L
     }
 
     /**
+     * Creates and returns a new {@code RingBufferAdmin} that instruments the
+     * ringbuffer of this {@code AsyncLoggerConfig}.
+     * 
+     * @param contextName name of the {@code LoggerContext}
+     */
+    public RingBufferAdmin createRingBufferAdmin(String contextName) {
+        return helper.createRingBufferAdmin(contextName, getName());
+    }
+
+    /**
      * Factory method to create a LoggerConfig.
      *
      * @param additivity True if additive, false otherwise.

Modified: logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLoggerConfigHelper.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLoggerConfigHelper.java?rev=1532659&r1=1532658&r2=1532659&view=diff
==============================================================================
--- logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLoggerConfigHelper.java (original)
+++ logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLoggerConfigHelper.java Wed Oct 16 07:16:54 2013
@@ -22,6 +22,7 @@ import java.util.concurrent.ThreadFactor
 
 import org.apache.logging.log4j.Logger;
 import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.jmx.RingBufferAdmin;
 import org.apache.logging.log4j.status.StatusLogger;
 
 import com.lmax.disruptor.BlockingWaitStrategy;
@@ -283,4 +284,15 @@ class AsyncLoggerConfigHelper {
         disruptor.getRingBuffer().publishEvent(translator, event, asyncLoggerConfig);
     }
 
+    /**
+     * Creates and returns a new {@code RingBufferAdmin} that instruments the
+     * ringbuffer of this {@code AsyncLoggerConfig}.
+     * 
+     * @param contextName name of the {@code LoggerContext}
+     * @param loggerConfigName name of the logger config
+     */
+    public RingBufferAdmin createRingBufferAdmin(String contextName, String loggerConfigName) {
+        return RingBufferAdmin.forAsyncLoggerConfig(disruptor.getRingBuffer(), contextName, loggerConfigName);
+    }
+
 }

Modified: logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/AppenderAdmin.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/AppenderAdmin.java?rev=1532659&r1=1532658&r2=1532659&view=diff
==============================================================================
--- logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/AppenderAdmin.java (original)
+++ logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/AppenderAdmin.java Wed Oct 16 07:16:54 2013
@@ -19,6 +19,7 @@ package org.apache.logging.log4j.core.jm
 import javax.management.ObjectName;
 
 import org.apache.logging.log4j.core.Appender;
+import org.apache.logging.log4j.core.filter.AbstractFilterable;
 import org.apache.logging.log4j.core.helpers.Assert;
 
 /**
@@ -72,7 +73,7 @@ public class AppenderAdmin implements Ap
     }
 
     @Override
-    public boolean isExceptionSuppressed() {
+    public boolean isIgnoreExceptions() {
         return appender.ignoreExceptions();
     }
 
@@ -80,4 +81,12 @@ public class AppenderAdmin implements Ap
     public String getErrorHandler() {
         return String.valueOf(appender.getHandler());
     }
+
+    @Override
+    public String getFilter() {
+        if (appender instanceof AbstractFilterable) {
+            return String.valueOf(((AbstractFilterable) appender).getFilter());
+        }
+        return null;
+    }
 }

Modified: logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/AppenderAdminMBean.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/AppenderAdminMBean.java?rev=1532659&r1=1532658&r2=1532659&view=diff
==============================================================================
--- logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/AppenderAdminMBean.java (original)
+++ logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/AppenderAdminMBean.java Wed Oct 16 07:16:54 2013
@@ -71,7 +71,7 @@ public interface AppenderAdminMBean {
      * @return {@code true} if any exceptions thrown by the Appender will be
      *         logged or {@code false} if such exceptions are re-thrown.
      */
-    boolean isExceptionSuppressed();
+    boolean isIgnoreExceptions();
 
     /**
      * Returns the result of calling {@code toString} on the error handler of
@@ -81,4 +81,13 @@ public interface AppenderAdminMBean {
      *         appender, or {@code "null"}
      */
     String getErrorHandler();
+
+    /**
+     * Returns a string description of all filters configured for the
+     * instrumented {@code Appender}.
+     *
+     * @return a string description of all configured filters for this
+     *         appender
+     */
+    String getFilter();
 }

Added: logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/AsyncAppenderAdmin.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/AsyncAppenderAdmin.java?rev=1532659&view=auto
==============================================================================
--- logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/AsyncAppenderAdmin.java (added)
+++ logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/AsyncAppenderAdmin.java Wed Oct 16 07:16:54 2013
@@ -0,0 +1,133 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.core.jmx;
+
+import javax.management.ObjectName;
+
+import org.apache.logging.log4j.core.appender.AsyncAppender;
+import org.apache.logging.log4j.core.helpers.Assert;
+
+/**
+ * Implementation of the {@code AsyncAppenderAdminMBean} interface.
+ */
+public class AsyncAppenderAdmin implements AsyncAppenderAdminMBean {
+
+    private final String contextName;
+    private final AsyncAppender asyncAppender;
+    private final ObjectName objectName;
+
+    /**
+     * Constructs a new {@code AsyncAppenderAdmin} with the specified contextName
+     * and async appender.
+     *
+     * @param contextName used in the {@code ObjectName} for this mbean
+     * @param appender the instrumented object
+     */
+    public AsyncAppenderAdmin(final String contextName, final AsyncAppender appender) {
+        // super(executor); // no notifications for now
+        this.contextName = Assert.isNotNull(contextName, "contextName");
+        this.asyncAppender = Assert.isNotNull(appender, "async appender");
+        try {
+            final String ctxName = Server.escape(this.contextName);
+            final String configName = Server.escape(appender.getName());
+            final String name = String.format(PATTERN, ctxName, configName);
+            objectName = new ObjectName(name);
+        } catch (final Exception e) {
+            throw new IllegalStateException(e);
+        }
+    }
+
+    /**
+     * Returns the {@code ObjectName} of this mbean.
+     *
+     * @return the {@code ObjectName}
+     * @see AppenderAdminMBean#PATTERN
+     */
+    public ObjectName getObjectName() {
+        return objectName;
+    }
+
+    @Override
+    public String getName() {
+        return asyncAppender.getName();
+    }
+
+    @Override
+    public String getLayout() {
+        return String.valueOf(asyncAppender.getLayout());
+    }
+
+    @Override
+    public boolean isIgnoreExceptions() {
+        return asyncAppender.ignoreExceptions();
+    }
+
+    @Override
+    public String getErrorHandler() {
+        return String.valueOf(asyncAppender.getHandler());
+    }
+
+    @Override
+    public String getFilter() {
+        return String.valueOf(asyncAppender.getFilter());
+    }
+
+    @Override
+    public String[] getAppenderRefs() {
+        return asyncAppender.getAppenderRefStrings();
+    }
+    
+    /**
+     * Returns {@code true} if this AsyncAppender will take a snapshot of the stack with
+     * every log event to determine the class and method where the logging call
+     * was made.
+     * @return {@code true} if location is included with every event, {@code false} otherwise
+     */
+    @Override
+    public boolean isIncludeLocation() {
+        return asyncAppender.isIncludeLocation();
+    }
+    
+    /**
+     * Returns {@code true} if this AsyncAppender will block when the queue is full,
+     * or {@code false} if events are dropped when the queue is full.
+     * @return whether this AsyncAppender will block or drop events when the queue is full.
+     */
+    @Override
+    public boolean isBlocking() {
+        return asyncAppender.isBlocking();
+    }
+    
+    /**
+     * Returns the name of the appender that any errors are logged to or {@code null}.
+     * @return the name of the appender that any errors are logged to or {@code null}
+     */
+    @Override
+    public String getErrorRef() {
+        return asyncAppender.getErrorRef();
+    }
+    
+    @Override
+    public int getQueueCapacity() {
+        return asyncAppender.getQueueCapacity();
+    }
+    
+    @Override
+    public int getQueueRemainingCapacity() {
+        return asyncAppender.getQueueRemainingCapacity();
+    }
+}

Propchange: logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/AsyncAppenderAdmin.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/AsyncAppenderAdminMBean.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/AsyncAppenderAdminMBean.java?rev=1532659&view=auto
==============================================================================
--- logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/AsyncAppenderAdminMBean.java (added)
+++ logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/AsyncAppenderAdminMBean.java Wed Oct 16 07:16:54 2013
@@ -0,0 +1,131 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.core.jmx;
+
+/**
+ * The MBean interface for monitoring and managing an {@code AsyncAppender}.
+ */
+public interface AsyncAppenderAdminMBean {
+    /**
+     * ObjectName pattern ({@value} ) for AsyncAppenderAdmin MBeans. This
+     * pattern contains two variables, where the first is the name of the
+     * context, the second is the name of the instrumented appender.
+     * <p>
+     * You can find all registered AsyncAppenderAdmin MBeans like this:
+     * </p>
+     * 
+     * <pre>
+     * MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
+     * String pattern = String.format(AsyncAppenderAdminMBean.PATTERN, &quot;*&quot;, &quot;*&quot;);
+     * Set&lt;ObjectName&gt; appenderNames = mbs.queryNames(new ObjectName(pattern), null);
+     * </pre>
+     * <p>
+     * Some characters are not allowed in ObjectNames. The logger context name
+     * and appender name may be quoted. When AsyncAppenderAdmin MBeans are
+     * registered, their ObjectNames are created using this pattern as follows:
+     * </p>
+     * 
+     * <pre>
+     * String ctxName = Server.escape(loggerContext.getName());
+     * String appenderName = Server.escape(appender.getName());
+     * String name = String.format(PATTERN, ctxName, appenderName);
+     * ObjectName objectName = new ObjectName(name);
+     * </pre>
+     * 
+     * @see Server#escape(String)
+     */
+    String PATTERN = "org.apache.logging.log4j2:type=LoggerContext,ctx=%s,sub=AsyncAppender,name=%s";
+
+    /**
+     * Returns the name of the instrumented {@code AsyncAppender}.
+     * 
+     * @return the name of the AsyncAppender
+     */
+    String getName();
+
+    /**
+     * Returns the result of calling {@code toString} on the {@code Layout}
+     * object of the instrumented {@code AsyncAppender}.
+     * 
+     * @return the {@code Layout} of the instrumented {@code AsyncAppender} as a
+     *         string
+     */
+    String getLayout();
+
+    /**
+     * Returns how exceptions thrown on the instrumented {@code AsyncAppender}
+     * are handled.
+     * 
+     * @return {@code true} if any exceptions thrown by the AsyncAppender will
+     *         be logged or {@code false} if such exceptions are re-thrown.
+     */
+    boolean isIgnoreExceptions();
+
+    /**
+     * Returns the result of calling {@code toString} on the error handler of
+     * this appender, or {@code "null"} if no error handler was set.
+     * 
+     * @return result of calling {@code toString} on the error handler of this
+     *         appender, or {@code "null"}
+     */
+    String getErrorHandler();
+
+    /**
+     * Returns a string description of all filters configured for the
+     * instrumented {@code AsyncAppender}.
+     * 
+     * @return a string description of all configured filters for this appender
+     */
+    String getFilter();
+
+    /**
+     * Returns a String array with the appender refs configured for the
+     * instrumented {@code AsyncAppender}.
+     * 
+     * @return the appender refs for the instrumented {@code AsyncAppender}.
+     */
+    String[] getAppenderRefs();
+
+    /**
+     * Returns {@code true} if this AsyncAppender will take a snapshot of the
+     * stack with every log event to determine the class and method where the
+     * logging call was made.
+     * 
+     * @return {@code true} if location is included with every event,
+     *         {@code false} otherwise
+     */
+    boolean isIncludeLocation();
+
+    /**
+     * Returns {@code true} if this AsyncAppender will block when the queue is
+     * full, or {@code false} if events are dropped when the queue is full.
+     * 
+     * @return whether this AsyncAppender will block or drop events when the
+     *         queue is full.
+     */
+    boolean isBlocking();
+    
+    /**
+     * Returns the name of the appender that any errors are logged to or {@code null}.
+     * @return the name of the appender that any errors are logged to or {@code null}
+     */
+    String getErrorRef();
+    
+    int getQueueCapacity();
+    
+    int getQueueRemainingCapacity();
+}

Propchange: logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/AsyncAppenderAdminMBean.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/RingBufferAdmin.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/RingBufferAdmin.java?rev=1532659&view=auto
==============================================================================
--- logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/RingBufferAdmin.java (added)
+++ logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/RingBufferAdmin.java Wed Oct 16 07:16:54 2013
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.core.jmx;
+
+import javax.management.ObjectName;
+
+import org.apache.logging.log4j.core.helpers.Assert;
+
+import com.lmax.disruptor.RingBuffer;
+
+/**
+ * Instruments an LMAX Disruptor ring buffer.
+ */
+public class RingBufferAdmin implements RingBufferAdminMBean {
+
+    private final RingBuffer<?> ringBuffer;
+    private final ObjectName objectName;
+
+    public static RingBufferAdmin forAsyncLogger(RingBuffer<?> ringBuffer, String contextName) {
+        final String ctxName = Server.escape(contextName);
+        final String name = String.format(PATTERN_ASYNC_LOGGER, ctxName);
+        return new RingBufferAdmin(ringBuffer, name);
+    }
+
+    public static RingBufferAdmin forAsyncLoggerConfig(RingBuffer<?> ringBuffer, 
+            String contextName, String configName) {
+        final String ctxName = Server.escape(contextName);
+        final String cfgName = Server.escape(configName);
+        final String name = String.format(PATTERN_ASYNC_LOGGER_CONFIG, ctxName, cfgName);
+        return new RingBufferAdmin(ringBuffer, name);
+    }
+    
+    protected RingBufferAdmin(RingBuffer<?> ringBuffer, String mbeanName) {
+        this.ringBuffer = Assert.isNotNull(ringBuffer, "ringbuffer");        
+        try {
+            objectName = new ObjectName(mbeanName);
+        } catch (final Exception e) {
+            throw new IllegalStateException(e);
+        }
+    }
+    
+    public long getBufferSize() {
+        return ringBuffer.getBufferSize();
+    }
+    
+    public long getRemainingCapacity() {
+        return ringBuffer.remainingCapacity();
+    }
+
+    /**
+     * Returns the {@code ObjectName} of this mbean.
+     *
+     * @return the {@code ObjectName}
+     * @see RingBufferAdminMBean#PATTERN_ASYNC_LOGGER
+     * @see RingBufferAdminMBean#PATTERN_ASYNC_LOGGER_CONFIG
+     */
+    public ObjectName getObjectName() {
+        return objectName;
+    }
+
+}

Propchange: logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/RingBufferAdmin.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/RingBufferAdminMBean.java
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/RingBufferAdminMBean.java?rev=1532659&view=auto
==============================================================================
--- logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/RingBufferAdminMBean.java (added)
+++ logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/RingBufferAdminMBean.java Wed Oct 16 07:16:54 2013
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.core.jmx;
+
+/**
+ * The MBean interface for monitoring and managing an LMAX Disruptor ring
+ * buffer.
+ */
+public interface RingBufferAdminMBean {
+    /**
+     * ObjectName pattern ({@value}) for the RingBufferAdmin MBean that instruments
+     * the global {@code AsyncLogger} ring buffer.
+     * This pattern contains one variable: the name of the context.
+     * <p>
+     * You can find the registered RingBufferAdmin MBean for the global AsyncLogger like this:
+     * </p>
+     * <pre>
+     * MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
+     * String pattern = String.format(RingBufferAdminMBean.PATTERN_ASYNC_LOGGER, &quot;*&quot;);
+     * Set&lt;ObjectName&gt; asyncLoggerNames = mbs.queryNames(new ObjectName(pattern), null);
+     * </pre>
+     */
+    String PATTERN_ASYNC_LOGGER = "org.apache.logging.log4j2:type=LoggerContext,ctx=%s,name=AsyncLoggerRingBuffer";
+    
+    /**
+     * ObjectName pattern ({@value}) for RingBufferAdmin MBeans that instrument
+     * {@code AsyncLoggerConfig} ring buffers.
+     * This pattern contains three variables, where the first is the name of the
+     * context, the second and third are identical and the name of the instrumented logger config.
+     * <p>
+     * You can find all registered RingBufferAdmin MBeans like this:
+     * </p>
+     * <pre>
+     * MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
+     * String pattern = String.format(RingBufferAdminMBean.PATTERN_ASYNC_LOGGER_CONFIG, &quot;*&quot;, &quot;*&quot;);
+     * Set&lt;ObjectName&gt; asyncConfigNames = mbs.queryNames(new ObjectName(pattern), null);
+     * </pre>
+     */
+    String PATTERN_ASYNC_LOGGER_CONFIG = "org.apache.logging.log4j2:type=LoggerContext,ctx=%s,sub=LoggerConfig,name=%s,subtype=RingBuffer";
+
+    /**
+     * Returns the number of slots that the ring buffer was configured with.
+     * Disruptor ring buffers are bounded-size data structures, this number does
+     * not change during the life of the ring buffer.
+     * 
+     * @return the number of slots that the ring buffer was configured with
+     */
+    long getBufferSize();
+
+    /**
+     * Returns the number of available slots in the ring buffer. May vary wildly
+     * between invocations.
+     * 
+     * @return the number of available slots in the ring buffer
+     */
+    long getRemainingCapacity();
+}

Propchange: logging/log4j/log4j2/trunk/log4j-core/src/main/java/org/apache/logging/log4j/core/jmx/RingBufferAdminMBean.java
------------------------------------------------------------------------------
    svn:eol-style = native

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=1532659&r1=1532658&r2=1532659&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 Wed Oct 16 07:16:54 2013
@@ -34,6 +34,10 @@ import javax.management.ObjectName;
 
 import org.apache.logging.log4j.core.Appender;
 import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.appender.AsyncAppender;
+import org.apache.logging.log4j.core.async.AsyncLogger;
+import org.apache.logging.log4j.core.async.AsyncLoggerConfig;
+import org.apache.logging.log4j.core.async.AsyncLoggerContext;
 import org.apache.logging.log4j.core.config.LoggerConfig;
 import org.apache.logging.log4j.core.selector.ContextSelector;
 import org.apache.logging.log4j.status.StatusLogger;
@@ -147,7 +151,9 @@ public final class Server {
                     // first unregister the MBeans that instrument the
                     // previous instrumented LoggerConfigs and Appenders
                     unregisterLoggerConfigs(context.getName(), mbs);
+                    unregisterAsyncLoggerConfigRingBufferAdmins(context.getName(), mbs);
                     unregisterAppenders(context.getName(), mbs);
+                    unregisterAsyncAppenders(context.getName(), mbs);
 
                     // now provide instrumentation for the newly configured
                     // LoggerConfigs and Appenders
@@ -189,9 +195,12 @@ public final class Server {
     public static void unregisterContext(String contextName, MBeanServer mbs) {
         final String pattern = LoggerContextAdminMBean.PATTERN;
         final String search = String.format(pattern, contextName, "*");
-        unregisterAllMatching(search, mbs);
+        unregisterAllMatching(search, mbs); // unregister context mbean
         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)
@@ -209,13 +218,26 @@ public final class Server {
         mbs.registerMBean(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);
             mbs.registerMBean(mbean, mbean.getObjectName());
+            
+            if (ctx instanceof AsyncLoggerContext) {
+                RingBufferAdmin rbmbean = AsyncLogger.createRingBufferAdmin(ctx.getName());
+                mbs.registerMBean(rbmbean, rbmbean.getObjectName());
+            }
         }
     }
 
@@ -233,6 +255,27 @@ public final class Server {
         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, "*");
+        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);
+        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, "*");
+        unregisterAllMatching(search2, mbs);
+    }
+
     private static void unregisterAllMatching(final String search, final MBeanServer mbs) {
         try {
             final ObjectName pattern = new ObjectName(search);
@@ -254,6 +297,12 @@ public final class Server {
             final LoggerConfig cfg = map.get(name);
             final LoggerConfigAdmin mbean = new LoggerConfigAdmin(ctx.getName(), cfg);
             mbs.registerMBean(mbean, mbean.getObjectName());
+            
+            if (cfg instanceof AsyncLoggerConfig) {
+                AsyncLoggerConfig async = (AsyncLoggerConfig) cfg;
+                RingBufferAdmin rbmbean = async.createRingBufferAdmin(ctx.getName());
+                mbs.registerMBean(rbmbean, rbmbean.getObjectName());
+            }
         }
     }
 
@@ -263,8 +312,15 @@ public final class Server {
         final Map<String, Appender> map = ctx.getConfiguration().getAppenders();
         for (final String name : map.keySet()) {
             final Appender appender = map.get(name);
-            final AppenderAdmin mbean = new AppenderAdmin(ctx.getName(), appender);
-            mbs.registerMBean(mbean, mbean.getObjectName());
+            
+            if (appender instanceof AsyncAppender) {
+                AsyncAppender async = ((AsyncAppender) appender);
+                final AsyncAppenderAdmin mbean = new AsyncAppenderAdmin(ctx.getName(), async);
+                mbs.registerMBean(mbean, mbean.getObjectName());
+            } else {
+                final AppenderAdmin mbean = new AppenderAdmin(ctx.getName(), appender);
+                mbs.registerMBean(mbean, mbean.getObjectName());
+            }
         }
     }
 }

Modified: logging/log4j/log4j2/trunk/src/changes/changes.xml
URL: http://svn.apache.org/viewvc/logging/log4j/log4j2/trunk/src/changes/changes.xml?rev=1532659&r1=1532658&r2=1532659&view=diff
==============================================================================
--- logging/log4j/log4j2/trunk/src/changes/changes.xml (original)
+++ logging/log4j/log4j2/trunk/src/changes/changes.xml Wed Oct 16 07:16:54 2013
@@ -21,6 +21,10 @@
   </properties>
   <body>
     <release version="2.0RC1" date="2013-MM-DD" description="Bug fixes and enhancements">
+      <action issue="LOG4J2-423" dev="rpopma" type="add">
+        Added MBeans for instrumenting AsyncAppenders and AsyncLogger RingBuffers,
+        exposing queue size, remaining capacity and other attributes.
+      </action>
       <action issue="LOG4J2-323" dev="rpopma" type="fix">
         Resolved memory leak by releasing reference to ThreadLocal when 
         AsyncLogger is stopped.