geode-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From kl...@apache.org
Subject geode git commit: Latest changes
Date Mon, 23 Jan 2017 19:15:00 GMT
Repository: geode
Updated Branches:
  refs/heads/feature/GEODE-2297 deb52c06b -> d9815b6d2


Latest changes


Project: http://git-wip-us.apache.org/repos/asf/geode/repo
Commit: http://git-wip-us.apache.org/repos/asf/geode/commit/d9815b6d
Tree: http://git-wip-us.apache.org/repos/asf/geode/tree/d9815b6d
Diff: http://git-wip-us.apache.org/repos/asf/geode/diff/d9815b6d

Branch: refs/heads/feature/GEODE-2297
Commit: d9815b6d2f60d31062957cacaae89868f1302dfc
Parents: deb52c0
Author: Kirk Lund <klund@apache.org>
Authored: Fri Jan 20 14:13:27 2017 -0800
Committer: Kirk Lund <klund@apache.org>
Committed: Fri Jan 20 14:13:27 2017 -0800

----------------------------------------------------------------------
 .../geode/admin/AdminDistributedSystem.java     |   4 +-
 .../internal/AdminDistributedSystemImpl.java    |   2 +-
 .../internal/AdminDistributedSystemJmxImpl.java |   2 +-
 .../internal/DistributedAlertService.java       | 238 ++++++++++++++
 .../internal/InternalDistributedSystem.java     | 122 ++++---
 .../internal/direct/DirectChannel.java          |   3 +-
 .../gms/messenger/JGroupsMessenger.java         |   3 +-
 .../admin/remote/AlertLevelChangeMessage.java   |   2 +-
 .../internal/logging/ManagerLogWriter.java      |  24 +-
 .../logging/SecurityManagerLogWriter.java       |  12 +-
 .../internal/logging/log4j/AlertAppender.java   | 321 ++++---------------
 .../logging/log4j/LogWriterAppenders.java       |   8 +-
 .../apache/geode/internal/tcp/Connection.java   |   3 +-
 .../geode/internal/tcp/ConnectionTable.java     |   6 +-
 .../internal/tcp/ReenteredConnectException.java |   2 +-
 .../apache/geode/internal/tcp/TCPConduit.java   |   4 +-
 .../internal/alerting/AlertHandler.java         |  30 ++
 .../internal/alerting/AlertLevel.java           |  65 ++++
 .../internal/alerting/AlertService.java         |  37 +++
 .../internal/alerting/AlertServiceListener.java |  23 ++
 .../internal/alerting/AlertSubscriber.java      |  63 ++++
 .../messenger/JGroupsMessengerJUnitTest.java    |   6 +-
 .../internal/logging/TestLogWriterFactory.java  |   4 +-
 .../logging/log4j/AlertAppenderJUnitTest.java   |  16 +-
 24 files changed, 635 insertions(+), 365 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/geode/blob/d9815b6d/geode-core/src/main/java/org/apache/geode/admin/AdminDistributedSystem.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/admin/AdminDistributedSystem.java b/geode-core/src/main/java/org/apache/geode/admin/AdminDistributedSystem.java
index cd4dc7e..60a620c 100755
--- a/geode-core/src/main/java/org/apache/geode/admin/AdminDistributedSystem.java
+++ b/geode-core/src/main/java/org/apache/geode/admin/AdminDistributedSystem.java
@@ -94,13 +94,13 @@ public interface AdminDistributedSystem {
   public String getAlertLevelAsString();
 
   /**
-   * Registers an <code>AlertListener</code> that will receive all alerts that are at or above the
+   * Registers an <code>AlertSubscriber</code> that will receive all alerts that are at or above the
    * {@linkplain #setAlertLevel alert level}.
    */
   public void addAlertListener(AlertListener listener);
 
   /**
-   * Unregisters an <code>AlertListener</code>
+   * Unregisters an <code>AlertSubscriber</code>
    */
   public void removeAlertListener(AlertListener listener);
 

http://git-wip-us.apache.org/repos/asf/geode/blob/d9815b6d/geode-core/src/main/java/org/apache/geode/admin/internal/AdminDistributedSystemImpl.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/admin/internal/AdminDistributedSystemImpl.java b/geode-core/src/main/java/org/apache/geode/admin/internal/AdminDistributedSystemImpl.java
index ed0c015..58daa36 100755
--- a/geode-core/src/main/java/org/apache/geode/admin/internal/AdminDistributedSystemImpl.java
+++ b/geode-core/src/main/java/org/apache/geode/admin/internal/AdminDistributedSystemImpl.java
@@ -1181,7 +1181,7 @@ public class AdminDistributedSystemImpl implements org.apache.geode.admin.AdminD
     }
   }
 
-  // ----------- org.apache.geode.internal.admin.AlertListener -----------
+  // ----------- org.apache.geode.internal.admin.AlertSubscriber -----------
   /**
    * Listener callback for when a SystemMember of this DistributedSystem has crashed.
    *

http://git-wip-us.apache.org/repos/asf/geode/blob/d9815b6d/geode-core/src/main/java/org/apache/geode/admin/jmx/internal/AdminDistributedSystemJmxImpl.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/admin/jmx/internal/AdminDistributedSystemJmxImpl.java b/geode-core/src/main/java/org/apache/geode/admin/jmx/internal/AdminDistributedSystemJmxImpl.java
index 1bfddf2..d569341 100755
--- a/geode-core/src/main/java/org/apache/geode/admin/jmx/internal/AdminDistributedSystemJmxImpl.java
+++ b/geode-core/src/main/java/org/apache/geode/admin/jmx/internal/AdminDistributedSystemJmxImpl.java
@@ -517,7 +517,7 @@ public class AdminDistributedSystemJmxImpl extends AdminDistributedSystemImpl
     }
   }
 
-  // ----------- org.apache.geode.internal.admin.AlertListener -----------
+  // ----------- org.apache.geode.internal.admin.AlertSubscriber -----------
   /**
    * Listener callback for when a SystemMember of this DistributedSystem has crashed.
    * <p>

http://git-wip-us.apache.org/repos/asf/geode/blob/d9815b6d/geode-core/src/main/java/org/apache/geode/distributed/internal/DistributedAlertService.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/distributed/internal/DistributedAlertService.java b/geode-core/src/main/java/org/apache/geode/distributed/internal/DistributedAlertService.java
new file mode 100644
index 0000000..f5356df
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/distributed/internal/DistributedAlertService.java
@@ -0,0 +1,238 @@
+/*
+ * 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.geode.distributed.internal;
+
+import static org.apache.geode.internal.admin.remote.AlertListenerMessage.create;
+import static org.apache.geode.management.internal.alerting.AlertLevel.*;
+
+import org.apache.geode.distributed.DistributedMember;
+import org.apache.geode.internal.admin.Alert;
+import org.apache.geode.internal.admin.remote.AlertListenerMessage;
+import org.apache.geode.internal.logging.LogService;
+import org.apache.geode.internal.tcp.ReenteredConnectException;
+import org.apache.geode.management.internal.alerting.AlertServiceListener;
+import org.apache.geode.management.internal.alerting.AlertSubscriber;
+import org.apache.geode.management.internal.alerting.AlertService;
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.core.config.LoggerConfig;
+import org.apache.logging.log4j.status.StatusLogger;
+
+import java.util.Date;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+public class DistributedAlertService implements AlertService {
+  private static final StatusLogger logger = StatusLogger.getLogger();
+
+  /** Is this thread in the process of alerting? */
+  private static final ThreadLocal<Boolean> alerting = new ThreadLocal<Boolean>() {
+    @Override
+    protected Boolean initialValue() {
+      return Boolean.FALSE;
+    }
+  };
+
+  // Listeners are ordered with the narrowest levels (e.g. FATAL) at the end
+  private final CopyOnWriteArrayList<AlertSubscriber> alertSubscribers = new CopyOnWriteArrayList<>();
+
+  private final InternalDistributedSystem system;
+  private final DistributionManager dm;
+
+  private volatile boolean enabled;
+
+  public DistributedAlertService(final InternalDistributedSystem system) {
+    this.system = system;
+    this.system.addDisconnectListener((final InternalDistributedSystem disconnected) -> {
+      enabled = false;
+    });
+    this.dm = (DistributionManager)this.system.getDistributionManager();
+    this.enabled = !this.system.getDistributionManager().isLoner(); // loner is disabled
+  }
+
+  /**
+   * Returns true if the current thread is in the process of delivering an alert message.
+   */
+  public static boolean isThreadAlerting() {
+    return alerting.get();
+  }
+
+  public static void setIsAlerting(boolean isAlerting) {
+    alerting.set(isAlerting ? Boolean.TRUE : Boolean.FALSE);
+  }
+
+  public synchronized void shuttingDown() {
+    this.alertSubscribers.clear();
+  }
+
+  private AlertServiceListener alertServiceListener;
+
+  public synchronized void addAlertListener(final DistributedMember member, final int alertLevel) {
+    final Level level = LogService.toLevel(alertLevelToLogLevel(alertLevel));
+
+    if (this.alertSubscribers.size() == 0) {
+      alertServiceListener.clear();
+    }
+
+    addListenerToSortedList(new AlertSubscriber(level, member));
+
+    LoggerConfig loggerConfig = this.appenderContext.getLoggerConfig();
+    loggerConfig.addAppender(this, this.alertSubscribers.get(0).getLevel(), null);
+    if (logger.isDebugEnabled()) {
+      logger.debug("Added/Replaced alert listener for member {} at level {}", member, level);
+    }
+  }
+
+  public synchronized boolean removeAlertListener(final DistributedMember member) {
+    final boolean memberWasFound = this.listeners.remove(new AlertSubscriber(null, member));
+
+    if (memberWasFound) {
+      if (this.alertSubscribers.size() == 0) {
+        this.appenderContext.getLoggerContext().removePropertyChangeListener(this);
+        this.appenderContext.getLoggerConfig().removeAppender(APPENDER_NAME);
+
+      } else {
+        LoggerConfig loggerConfig = this.appenderContext.getLoggerConfig();
+        loggerConfig.addAppender(this, this.alertSubscribers.get(0).getLevel(), null);
+      }
+      if (logger.isDebugEnabled()) {
+        logger.debug("Removed alert listener for member {}", member);
+      }
+    }
+
+    return memberWasFound;
+  }
+
+  public synchronized boolean hasAlertListener(final DistributedMember member,
+                                               final int alertLevel) {
+    final Level level = LogService.toLevel(alertLevelToLogLevel(alertLevel));
+
+    for (AlertSubscriber listener : this.alertSubscribers) {
+      if (listener.getMember().equals(member) && listener.getLevel().equals(level)) {
+        return true;
+      }
+    }
+
+    // Special case for alert level Alert.OFF (NONE_LEVEL), because we can never have an actual
+    // listener with
+    // this level (see AlertLevelChangeMessage.process()).
+    if (alertLevel == Alert.OFF) {
+      for (AlertSubscriber listener : this.alertSubscribers) {
+        if (listener.getMember().equals(member)) {
+          return false;
+        }
+      }
+      return true;
+    }
+
+    return false;
+  }
+
+  /**
+   * Will add (or replace) a listener to the list of sorted listeners such that listeners with a
+   * narrower level (e.g. FATAL) will be at the end of the list.
+   *
+   * @param listener The listener to add to the list.
+   */
+  private void addListenerToSortedList(final AlertSubscriber listener) {
+    if (this.alertSubscribers.contains(listener)) {
+      this.alertSubscribers.remove(listener);
+    }
+
+    for (int i = 0; i < this.alertSubscribers.size(); i++) {
+      if (listener.getLevel().compareTo(this.alertSubscribers.get(i).getLevel()) >= 0) {
+        this.alertSubscribers.add(i, listener);
+        return;
+      }
+    }
+
+    this.alertSubscribers.add(listener);
+  }
+
+  public boolean isEnabled() {
+    return this.enabled;
+  }
+
+  public DistributionManager getDistributionManager() {
+    return this.dm;
+  }
+
+  public String getMemberName() {
+    return system.getName();
+  }
+
+  @Override
+  public boolean isAlerting() {
+    return false;
+  }
+
+  @Override
+  public void handleAlert(final AlertService alertProvider,
+                          final int intLevel,
+                          final Date date,
+                          final String threadName,
+                          final String logMessage,
+                          final String stackTrace,
+                          final String connectionName,
+                          final AlertSubscriber listener) {
+
+    DistributionManager dm = alertProvider.getDistributionManager();
+    if (dm == null) {
+      logger.debug("Did not append alert event because the distributed system is set to null.");
+      return;
+    }
+
+    try {
+      setIsAlerting(true);
+
+      AlertListenerMessage alertMessage = create(
+          listener.getMember(),
+          intLevel,
+          date,
+          connectionName,
+          threadName,
+          Thread.currentThread().getId(),
+          logMessage,
+          stackTrace);
+
+      if (listener.getMember().equals(dm.getDistributionManagerId())) {
+        logger.debug("Processing local alert message: {}, {}, {}, {}, {}, [{}], [{}].",
+            listener.getMember(),
+            intLevel,
+            date,
+            connectionName,
+            threadName,
+            logMessage,
+            stackTrace);
+        alertMessage.process(dm);
+      } else {
+        logger.debug("Sending remote alert message: {}, {}, {}, {}, {}, [{}], [{}].",
+            listener.getMember(),
+            intLevel,
+            date,
+            connectionName,
+            threadName,
+            logMessage,
+            stackTrace);
+        dm.putOutgoing(alertMessage);
+      }
+    } catch (ReenteredConnectException e) {
+      // OK. We can't send to this recipient because we're in the middle of
+      // trying to connect to it.
+    } finally {
+      setIsAlerting(false);
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/geode/blob/d9815b6d/geode-core/src/main/java/org/apache/geode/distributed/internal/InternalDistributedSystem.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/distributed/internal/InternalDistributedSystem.java b/geode-core/src/main/java/org/apache/geode/distributed/internal/InternalDistributedSystem.java
index fad9206..eb98f99 100644
--- a/geode-core/src/main/java/org/apache/geode/distributed/internal/InternalDistributedSystem.java
+++ b/geode-core/src/main/java/org/apache/geode/distributed/internal/InternalDistributedSystem.java
@@ -40,6 +40,7 @@ import java.util.concurrent.atomic.AtomicLong;
 import java.util.concurrent.atomic.AtomicReference;
 
 import org.apache.geode.cache.CacheXmlException;
+import org.apache.geode.management.internal.alerting.AlertService;
 import org.apache.logging.log4j.Logger;
 
 import org.apache.geode.CancelCriterion;
@@ -570,50 +571,9 @@ public class InternalDistributedSystem extends DistributedSystem
     try {
       SocketCreatorFactory.setDistributionConfig(config);
 
-      // LOG: create LogWriterAppender(s) if log-file or security-log-file is specified
-      final boolean hasLogFile =
-          this.config.getLogFile() != null && !this.config.getLogFile().equals(new File(""));
-      final boolean hasSecurityLogFile = this.config.getSecurityLogFile() != null
-          && !this.config.getSecurityLogFile().equals(new File(""));
-      LogService.configureLoggers(hasLogFile, hasSecurityLogFile);
-      if (hasLogFile || hasSecurityLogFile) {
-
-        // main log file
-        if (hasLogFile) {
-          // if log-file then create logWriterAppender
-          this.logWriterAppender = LogWriterAppenders.getOrCreateAppender(
-              LogWriterAppenders.Identifier.MAIN, this.isLoner, this.config, true);
-        }
-
-        // security log file
-        if (hasSecurityLogFile) {
-          // if security-log-file then create securityLogWriterAppender
-          this.securityLogWriterAppender = LogWriterAppenders.getOrCreateAppender(
-              LogWriterAppenders.Identifier.SECURITY, this.isLoner, this.config, false);
-        } else {
-          // let security route to regular log-file or stdout
-        }
-      }
-
-      // LOG: create LogWriterLogger(s) for backwards compatibility of getLogWriter and
-      // getSecurityLogWriter
-      if (this.logWriter == null) {
-        this.logWriter =
-            LogWriterFactory.createLogWriterLogger(this.isLoner, false, this.config, true);
-        this.logWriter.fine("LogWriter is created.");
-      }
-
-      // logWriter.info("Created log writer for IDS@"+System.identityHashCode(this));
-
-      if (this.securityLogWriter == null) {
-        // LOG: whole new LogWriterLogger instance for security
-        this.securityLogWriter =
-            LogWriterFactory.createLogWriterLogger(this.isLoner, true, this.config, false);
-        this.securityLogWriter.fine("SecurityLogWriter is created.");
-      }
+      initializeAlerting();
 
-      Services.setLogWriter(this.logWriter);
-      Services.setSecurityLogWriter(this.securityLogWriter);
+      initializeLogging();
 
       this.clock = new DSClock(this.isLoner);
 
@@ -736,12 +696,7 @@ public class InternalDistributedSystem extends DistributedSystem
         this.sampler.start();
       }
 
-      if (this.logWriterAppender != null) {
-        LogWriterAppenders.startupComplete(LogWriterAppenders.Identifier.MAIN);
-      }
-      if (this.securityLogWriterAppender != null) {
-        LogWriterAppenders.startupComplete(LogWriterAppenders.Identifier.SECURITY);
-      }
+      postInitializeLogging();
 
       // this.logger.info("ds created", new RuntimeException("DEBUG: STACK"));
 
@@ -759,6 +714,75 @@ public class InternalDistributedSystem extends DistributedSystem
   }
 
   /**
+   * Extract this to a Service Initializer object that is passed into IDS
+   */
+  private void initializeAlerting() {
+    AlertService.install(new DistributedAlertService(this));
+  }
+
+  /**
+   * Extract this to a Service Initializer object that is passed into IDS
+   */
+  private void initializeLogging() {
+    // LOG: create LogWriterAppender(s) if log-file or security-log-file is specified
+    final boolean hasLogFile =
+        this.config.getLogFile() != null && !this.config.getLogFile().equals(new File(""));
+    final boolean hasSecurityLogFile = this.config.getSecurityLogFile() != null
+        && !this.config.getSecurityLogFile().equals(new File(""));
+    LogService.configureLoggers(hasLogFile, hasSecurityLogFile);
+    if (hasLogFile || hasSecurityLogFile) {
+
+      // main log file
+      if (hasLogFile) {
+        // if log-file then create logWriterAppender
+        this.logWriterAppender = LogWriterAppenders.getOrCreateAppender(
+            LogWriterAppenders.Identifier.MAIN, this.isLoner, this.config, true);
+      }
+
+      // security log file
+      if (hasSecurityLogFile) {
+        // if security-log-file then create securityLogWriterAppender
+        this.securityLogWriterAppender = LogWriterAppenders.getOrCreateAppender(
+            LogWriterAppenders.Identifier.SECURITY, this.isLoner, this.config, false);
+      } else {
+        // let security route to regular log-file or stdout
+      }
+    }
+
+    // LOG: create LogWriterLogger(s) for backwards compatibility of getLogWriter and
+    // getSecurityLogWriter
+    if (this.logWriter == null) {
+      this.logWriter =
+          LogWriterFactory.createLogWriterLogger(this.isLoner, false, this.config, true);
+      this.logWriter.fine("LogWriter is created.");
+    }
+
+    // logWriter.info("Created log writer for IDS@"+System.identityHashCode(this));
+
+    if (this.securityLogWriter == null) {
+      // LOG: whole new LogWriterLogger instance for security
+      this.securityLogWriter =
+          LogWriterFactory.createLogWriterLogger(this.isLoner, true, this.config, false);
+      this.securityLogWriter.fine("SecurityLogWriter is created.");
+    }
+
+    Services.setLogWriter(this.logWriter);
+    Services.setSecurityLogWriter(this.securityLogWriter);
+  }
+
+  /**
+   * TODO: move this to lifecycle callbacks in Service Initializers
+   */
+  private void postInitializeLogging() {
+    if (this.logWriterAppender != null) {
+      LogWriterAppenders.startupComplete(LogWriterAppenders.Identifier.MAIN);
+    }
+    if (this.securityLogWriterAppender != null) {
+      LogWriterAppenders.startupComplete(LogWriterAppenders.Identifier.SECURITY);
+    }
+  }
+
+  /**
    * @since GemFire 5.7
    */
   private void startInitLocator() throws InterruptedException {

http://git-wip-us.apache.org/repos/asf/geode/blob/d9815b6d/geode-core/src/main/java/org/apache/geode/distributed/internal/direct/DirectChannel.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/distributed/internal/direct/DirectChannel.java b/geode-core/src/main/java/org/apache/geode/distributed/internal/direct/DirectChannel.java
index cc88e95..6e9e9c7 100644
--- a/geode-core/src/main/java/org/apache/geode/distributed/internal/direct/DirectChannel.java
+++ b/geode-core/src/main/java/org/apache/geode/distributed/internal/direct/DirectChannel.java
@@ -27,7 +27,6 @@ import org.apache.geode.internal.net.SocketCreator;
 import org.apache.geode.internal.cache.DirectReplyMessage;
 import org.apache.geode.internal.i18n.LocalizedStrings;
 import org.apache.geode.internal.logging.LogService;
-import org.apache.geode.internal.logging.log4j.AlertAppender;
 import org.apache.geode.internal.logging.log4j.LocalizedMessage;
 import org.apache.geode.internal.logging.log4j.LogMarker;
 import org.apache.geode.internal.tcp.*;
@@ -211,7 +210,7 @@ public class DirectChannel {
   boolean threadOwnsResources() {
     DM d = getDM();
     if (d != null) {
-      return d.getSystem().threadOwnsResources() && !AlertAppender.isThreadAlerting();
+      return d.getSystem().threadOwnsResources() && !DistributedAlertService.isThreadAlerting();
     }
     return false;
 

http://git-wip-us.apache.org/repos/asf/geode/blob/d9815b6d/geode-core/src/main/java/org/apache/geode/distributed/internal/membership/gms/messenger/JGroupsMessenger.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/distributed/internal/membership/gms/messenger/JGroupsMessenger.java b/geode-core/src/main/java/org/apache/geode/distributed/internal/membership/gms/messenger/JGroupsMessenger.java
index e99eff2..1e10227 100644
--- a/geode-core/src/main/java/org/apache/geode/distributed/internal/membership/gms/messenger/JGroupsMessenger.java
+++ b/geode-core/src/main/java/org/apache/geode/distributed/internal/membership/gms/messenger/JGroupsMessenger.java
@@ -93,7 +93,6 @@ import org.apache.geode.internal.admin.remote.RemoteTransportConfig;
 import org.apache.geode.internal.cache.DirectReplyMessage;
 import org.apache.geode.internal.cache.DistributedCacheOperation;
 import org.apache.geode.internal.i18n.LocalizedStrings;
-import org.apache.geode.internal.logging.log4j.AlertAppender;
 import org.apache.geode.internal.logging.log4j.LocalizedMessage;
 import org.apache.geode.internal.tcp.MemberShunnedException;
 
@@ -939,7 +938,7 @@ public class JGroupsMessenger implements Messenger {
     msg.setFlag(Flag.DONT_BUNDLE);
 
     if (gfmsg.getProcessorType() == DistributionManager.HIGH_PRIORITY_EXECUTOR
-        || gfmsg instanceof HighPriorityDistributionMessage || AlertAppender.isThreadAlerting()) {
+        || gfmsg instanceof HighPriorityDistributionMessage || DistributedAlertService.isThreadAlerting()) {
       msg.setFlag(Flag.NO_FC);
       msg.setFlag(Flag.SKIP_BARRIER);
     }

http://git-wip-us.apache.org/repos/asf/geode/blob/d9815b6d/geode-core/src/main/java/org/apache/geode/internal/admin/remote/AlertLevelChangeMessage.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/internal/admin/remote/AlertLevelChangeMessage.java b/geode-core/src/main/java/org/apache/geode/internal/admin/remote/AlertLevelChangeMessage.java
index c3a293f..1148c53 100644
--- a/geode-core/src/main/java/org/apache/geode/internal/admin/remote/AlertLevelChangeMessage.java
+++ b/geode-core/src/main/java/org/apache/geode/internal/admin/remote/AlertLevelChangeMessage.java
@@ -64,7 +64,7 @@ public final class AlertLevelChangeMessage extends SerialDistributionMessage {
     if (this.newLevel != Alert.OFF) {
       AlertAppender.getInstance().addAlertListener(this.getSender(), this.newLevel);
       if (logger.isTraceEnabled(LogMarker.DM)) {
-        logger.trace(LogMarker.DM, "Added new AlertListener to application log writer");
+        logger.trace(LogMarker.DM, "Added new AlertSubscriber to application log writer");
       }
     }
   }

http://git-wip-us.apache.org/repos/asf/geode/blob/d9815b6d/geode-core/src/main/java/org/apache/geode/internal/logging/ManagerLogWriter.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/internal/logging/ManagerLogWriter.java b/geode-core/src/main/java/org/apache/geode/internal/logging/ManagerLogWriter.java
index 4b333ed..46e806d 100644
--- a/geode-core/src/main/java/org/apache/geode/internal/logging/ManagerLogWriter.java
+++ b/geode-core/src/main/java/org/apache/geode/internal/logging/ManagerLogWriter.java
@@ -46,6 +46,9 @@ public class ManagerLogWriter extends LocalLogWriter {
 
   private final RollingFileHandler rollingFileHandler;
 
+  // Don't redirect sysouts/syserrs to client log file. See #49492.
+  private final boolean redirectStdOut;
+
   private LogConfig cfg = null;
 
   private LocalLogWriter mainLogger = this;
@@ -67,16 +70,6 @@ public class ManagerLogWriter extends LocalLogWriter {
 
   /**
    * Creates a writer that logs to <code>System.out</code>.
-   * 
-   * @param level only messages greater than or equal to this value will be logged.
-   * @throws IllegalArgumentException if level is not in legal range
-   */
-  public ManagerLogWriter(int level, PrintStream out) {
-    this(level, out, null);
-  }
-
-  /**
-   * Creates a writer that logs to <code>System.out</code>.
    *
    * @param level only messages greater than or equal to this value will be logged.
    * @param connectionName Name of the connection associated with this logger
@@ -85,10 +78,11 @@ public class ManagerLogWriter extends LocalLogWriter {
    *
    * @since GemFire 3.5
    */
-  public ManagerLogWriter(int level, PrintStream out, String connectionName) {
+  public ManagerLogWriter(int level, PrintStream out, String connectionName, boolean redirectStdOut) {
     super(level, out, connectionName);
     this.fileSizeLimitInKB = Boolean.getBoolean(TEST_FILE_SIZE_LIMIT_IN_KB_PROPERTY);
     this.rollingFileHandler = new MainWithChildrenRollingFileHandler();
+    this.redirectStdOut = redirectStdOut;
   }
 
   /**
@@ -244,8 +238,7 @@ public class ManagerLogWriter extends LocalLogWriter {
               File tmpLogDir = rollingFileHandler.getParentFile(this.cfg.getLogFile());
               tmpFile = File.createTempFile("mlw", null, tmpLogDir);
               // close the old guy down before we do the rename
-              PrintStream tmpps = OSProcess.redirectOutput(tmpFile,
-                  AlertAppender.getInstance().isAlertingDisabled()/* See #49492 */);
+              PrintStream tmpps = OSProcess.redirectOutput(tmpFile, this.redirectStdOut);
               PrintWriter oldPW = this.setTarget(new PrintWriter(tmpps, true));
               if (oldPW != null) {
                 oldPW.close();
@@ -263,10 +256,7 @@ public class ManagerLogWriter extends LocalLogWriter {
           }
         }
         this.activeLogFile = new File(oldName);
-        // Don't redirect sysouts/syserrs to client log file. See #49492.
-        // IMPORTANT: This assumes that only a loner would have sendAlert set to false.
-        PrintStream ps = OSProcess.redirectOutput(activeLogFile,
-            AlertAppender.getInstance().isAlertingDisabled());
+        PrintStream ps = OSProcess.redirectOutput(activeLogFile, this.redirectStdOut);
         PrintWriter oldPW = this.setTarget(new PrintWriter(ps, true), this.activeLogFile.length());
         if (oldPW != null) {
           oldPW.close();

http://git-wip-us.apache.org/repos/asf/geode/blob/d9815b6d/geode-core/src/main/java/org/apache/geode/internal/logging/SecurityManagerLogWriter.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/internal/logging/SecurityManagerLogWriter.java b/geode-core/src/main/java/org/apache/geode/internal/logging/SecurityManagerLogWriter.java
index 3e62f7d..77ff87e 100644
--- a/geode-core/src/main/java/org/apache/geode/internal/logging/SecurityManagerLogWriter.java
+++ b/geode-core/src/main/java/org/apache/geode/internal/logging/SecurityManagerLogWriter.java
@@ -31,20 +31,12 @@ import java.io.PrintStream;
  */
 public final class SecurityManagerLogWriter extends ManagerLogWriter {
 
-  public SecurityManagerLogWriter(int level, PrintStream stream) {
-
-    super(level, stream);
-  }
-
-  public SecurityManagerLogWriter(int level, PrintStream stream, String connectionName) {
-
-    super(level, stream, connectionName);
+  public SecurityManagerLogWriter(int level, PrintStream stream, String connectionName, boolean redirectStdOut) {
+    super(level, stream, connectionName, redirectStdOut);
   }
 
-
   @Override
   public void setConfig(LogConfig config) {
-
     if (config instanceof DistributionConfig) {
       config = new SecurityLogConfig((DistributionConfig) config);
     }

http://git-wip-us.apache.org/repos/asf/geode/blob/d9815b6d/geode-core/src/main/java/org/apache/geode/internal/logging/log4j/AlertAppender.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/internal/logging/log4j/AlertAppender.java b/geode-core/src/main/java/org/apache/geode/internal/logging/log4j/AlertAppender.java
index 258f1d5..1a12b5a 100644
--- a/geode-core/src/main/java/org/apache/geode/internal/logging/log4j/AlertAppender.java
+++ b/geode-core/src/main/java/org/apache/geode/internal/logging/log4j/AlertAppender.java
@@ -17,96 +17,59 @@ package org.apache.geode.internal.logging.log4j;
 import java.beans.PropertyChangeEvent;
 import java.beans.PropertyChangeListener;
 import java.util.Date;
-import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.concurrent.atomic.AtomicReference;
 
-import org.apache.logging.log4j.Level;
-import org.apache.logging.log4j.Logger;
+import org.apache.geode.distributed.internal.DistributedAlertService;
+import org.apache.geode.management.internal.alerting.AlertLevel;
+import org.apache.geode.management.internal.alerting.AlertServiceListener;
+import org.apache.geode.management.internal.alerting.AlertSubscriber;
+import org.apache.geode.management.internal.alerting.DefaultAlertHandler;
+import org.apache.geode.management.internal.alerting.AlertService;
 import org.apache.logging.log4j.core.LogEvent;
 import org.apache.logging.log4j.core.LoggerContext;
 import org.apache.logging.log4j.core.appender.AbstractAppender;
 import org.apache.logging.log4j.core.config.LoggerConfig;
 import org.apache.logging.log4j.core.layout.PatternLayout;
 
-import org.apache.geode.distributed.DistributedMember;
 import org.apache.geode.distributed.internal.DistributionManager;
-import org.apache.geode.distributed.internal.InternalDistributedSystem;
-import org.apache.geode.internal.admin.Alert;
-import org.apache.geode.internal.admin.remote.AlertListenerMessage;
 import org.apache.geode.internal.lang.ThreadUtils;
 import org.apache.geode.internal.logging.LogService;
-import org.apache.geode.internal.tcp.ReenteredConnectException;
+import org.apache.logging.log4j.status.StatusLogger;
 
 /**
  * A Log4j Appender which will notify listeners whenever a message of the requested level is written
  * to the log file.
  */
-public final class AlertAppender extends AbstractAppender implements PropertyChangeListener {
-  // TODO: appender impl should use StatusLogger
-  private static final Logger logger = LogService.getLogger();
+public final class AlertAppender extends AbstractAppender implements PropertyChangeListener,
+    AlertServiceListener {
+  private static final StatusLogger logger = StatusLogger.getLogger();
 
   private static final String APPENDER_NAME = AlertAppender.class.getName();
   private static final AlertAppender instance = createAlertAppender();
+  private static final NullAlertProvider nullAlertProvider = new NullAlertProvider();
 
-  /** Is this thread in the process of alerting? */
-  private static final ThreadLocal<Boolean> alerting = new ThreadLocal<Boolean>() {
-    @Override
-    protected Boolean initialValue() {
-      return Boolean.FALSE;
-    }
-  };
-
-  private static final AtomicReference<InternalDistributedSystem> system = new AtomicReference<>();
-
-  // Listeners are ordered with the narrowest levels (e.g. FATAL) at the end
-  private final CopyOnWriteArrayList<Listener> listeners = new CopyOnWriteArrayList<>();
-
-  private final AppenderContext appenderContext = LogService.getAppenderContext();
+  private final AtomicReference<AlertService> alertServiceRef = new AtomicReference<>(nullAlertProvider);
 
-  // This can be set by a loner distributed system to disable alerting
-  private volatile boolean alertingDisabled = false;
+  private final AppenderContext appenderContext;
 
   private static AlertAppender createAlertAppender() {
-    InternalDistributedSystem
-        .addConnectListener((final InternalDistributedSystem connectedSystem) -> {
-          system.set(connectedSystem);
-          connectedSystem
-              .addDisconnectListener((final InternalDistributedSystem disconnectedSystem) -> {
-                system.compareAndSet(connectedSystem, null);
-              });
-        });
-
-    AlertAppender alertAppender = new AlertAppender();
+    AlertAppender alertAppender = new AlertAppender(LogService.getAppenderContext());
     alertAppender.start();
 
     return alertAppender;
   }
 
-  private AlertAppender() {
+  private AlertAppender(final AppenderContext appenderContext) {
     super(APPENDER_NAME, null, PatternLayout.createDefaultLayout());
+    this.appenderContext = appenderContext;
   }
 
   public static AlertAppender getInstance() {
     return instance;
   }
 
-  /**
-   * Returns true if the current thread is in the process of delivering an alert message.
-   */
-  public static boolean isThreadAlerting() {
-    return alerting.get();
-  }
-
-  public boolean isAlertingDisabled() {
-    return alertingDisabled;
-  }
-
-  public void setAlertingDisabled(final boolean alertingDisabled) {
-    this.alertingDisabled = alertingDisabled;
-  }
-
-  public static void setIsAlerting(boolean isAlerting) {
-    alerting.set(isAlerting ? Boolean.TRUE : Boolean.FALSE);
+  public static void updateAlertService(final AlertService service) {
+    instance.alertServiceRef.set(service);
   }
 
   /**
@@ -116,140 +79,49 @@ public final class AlertAppender extends AbstractAppender implements PropertyCha
    */
   @Override
   public void append(final LogEvent event) {
-    if (this.alertingDisabled) {
+    AlertService alertService = this.alertServiceRef.get();
+    if (!alertService.isEnabled()) {
       return;
     }
 
     // If already appending then don't send to avoid infinite recursion
-    if ((alerting.get())) {
+    if (alertService.isAlerting()) {
       return;
     }
-    setIsAlerting(true);
+    DistributedAlertService.setIsAlerting(true);
 
     try {
+      logger.debug("Delivering an alert event: {}", event);
 
-      final boolean isDebugEnabled = logger.isDebugEnabled();
-      if (isDebugEnabled) {
-        logger.debug("Delivering an alert event: {}", event);
-      }
+      int intLevel = AlertLevel.logLevelToAlertLevel(event.getLevel().intLevel());
+      Date date = new Date(event.getTimeMillis());
+      String threadName = event.getThreadName();
+      String logMessage = event.getMessage().getFormattedMessage();
+      String stackTrace = ThreadUtils.stackTraceToString(event.getThrown(), true);
+      String connectionName = alertService.getMemberName();
 
-      InternalDistributedSystem ds = system.get();
-      if (ds == null) {
-        logger.debug("Did not append alert event because the distributed system is set to null.");
-        return;
-      }
-      DistributionManager distMgr = (DistributionManager) ds.getDistributionManager();
-
-      final int intLevel = logLevelToAlertLevel(event.getLevel().intLevel());
-      final Date date = new Date(event.getTimeMillis());
-      final String threadName = event.getThreadName();
-      final String logMessage = event.getMessage().getFormattedMessage();
-      final String stackTrace = ThreadUtils.stackTraceToString(event.getThrown(), true);
-      final String connectionName = ds.getConfig().getName();
-
-      for (Listener listener : this.listeners) {
+      for (AlertSubscriber listener : this.listeners) {
         if (event.getLevel().intLevel() > listener.getLevel().intLevel()) {
           break;
         }
 
-        try {
-          AlertListenerMessage alertMessage =
-              AlertListenerMessage.create(listener.getMember(), intLevel, date, connectionName,
-                  threadName, Thread.currentThread().getId(), logMessage, stackTrace);
-
-          if (listener.getMember().equals(distMgr.getDistributionManagerId())) {
-            if (isDebugEnabled) {
-              logger.debug("Delivering local alert message: {}, {}, {}, {}, {}, [{}], [{}].",
-                  listener.getMember(), intLevel, date, connectionName, threadName, logMessage,
-                  stackTrace);
-            }
-            alertMessage.process(distMgr);
-          } else {
-            if (isDebugEnabled) {
-              logger.debug("Delivering remote alert message: {}, {}, {}, {}, {}, [{}], [{}].",
-                  listener.getMember(), intLevel, date, connectionName, threadName, logMessage,
-                  stackTrace);
-            }
-            distMgr.putOutgoing(alertMessage);
-          }
-        } catch (ReenteredConnectException e) {
-          // OK. We can't send to this recipient because we're in the middle of
-          // trying to connect to it.
-        }
+        alertService
+            .handleAlert(alertService, intLevel, date, threadName, logMessage, stackTrace,
+            connectionName,
+            listener);
       }
     } finally {
-      setIsAlerting(false);
-    }
-  }
-
-  public synchronized void addAlertListener(final DistributedMember member, final int alertLevel) {
-    final Level level = LogService.toLevel(alertLevelToLogLevel(alertLevel));
-
-    if (this.listeners.size() == 0) {
-      this.appenderContext.getLoggerContext().addPropertyChangeListener(this);
-    }
-
-    addListenerToSortedList(new Listener(level, member));
-
-    LoggerConfig loggerConfig = this.appenderContext.getLoggerConfig();
-    loggerConfig.addAppender(this, this.listeners.get(0).getLevel(), null);
-    if (logger.isDebugEnabled()) {
-      logger.debug("Added/Replaced alert listener for member {} at level {}", member, level);
-    }
-  }
-
-  public synchronized boolean removeAlertListener(final DistributedMember member) {
-    final boolean memberWasFound = this.listeners.remove(new Listener(null, member));
-
-    if (memberWasFound) {
-      if (this.listeners.size() == 0) {
-        this.appenderContext.getLoggerContext().removePropertyChangeListener(this);
-        this.appenderContext.getLoggerConfig().removeAppender(APPENDER_NAME);
-
-      } else {
-        LoggerConfig loggerConfig = this.appenderContext.getLoggerConfig();
-        loggerConfig.addAppender(this, this.listeners.get(0).getLevel(), null);
-      }
-      if (logger.isDebugEnabled()) {
-        logger.debug("Removed alert listener for member {}", member);
-      }
-    }
-
-    return memberWasFound;
-  }
-
-  public synchronized boolean hasAlertListener(final DistributedMember member,
-      final int alertLevel) {
-    final Level level = LogService.toLevel(alertLevelToLogLevel(alertLevel));
-
-    for (Listener listener : this.listeners) {
-      if (listener.getMember().equals(member) && listener.getLevel().equals(level)) {
-        return true;
-      }
-    }
-
-    // Special case for alert level Alert.OFF (NONE_LEVEL), because we can never have an actual
-    // listener with
-    // this level (see AlertLevelChangeMessage.process()).
-    if (alertLevel == Alert.OFF) {
-      for (Listener listener : this.listeners) {
-        if (listener.getMember().equals(member)) {
-          return false;
-        }
-      }
-      return true;
+      DistributedAlertService.setIsAlerting(false);
     }
-
-    return false;
   }
 
   @Override
-  public synchronized void propertyChange(final PropertyChangeEvent evt) {
+  public synchronized void propertyChange(final PropertyChangeEvent event) {
     if (logger.isDebugEnabled()) {
       logger.debug("Responding to a property change event. Property name is {}.",
-          evt.getPropertyName());
+          event.getPropertyName());
     }
-    if (evt.getPropertyName().equals(LoggerContext.PROPERTY_CONFIG)) {
+    if (event.getPropertyName().equals(LoggerContext.PROPERTY_CONFIG)) {
       LoggerConfig loggerConfig = this.appenderContext.getLoggerConfig();
       if (!loggerConfig.getAppenders().containsKey(APPENDER_NAME)) {
         loggerConfig.addAppender(this, this.listeners.get(0).getLevel(), null);
@@ -257,115 +129,56 @@ public final class AlertAppender extends AbstractAppender implements PropertyCha
     }
   }
 
-  /**
-   * Will add (or replace) a listener to the list of sorted listeners such that listeners with a
-   * narrower level (e.g. FATAL) will be at the end of the list.
-   * 
-   * @param listener The listener to add to the list.
-   */
-  private void addListenerToSortedList(final Listener listener) {
-    if (this.listeners.contains(listener)) {
-      this.listeners.remove(listener);
-    }
-
-    for (int i = 0; i < this.listeners.size(); i++) {
-      if (listener.getLevel().compareTo(this.listeners.get(i).getLevel()) >= 0) {
-        this.listeners.add(i, listener);
-        return;
-      }
-    }
-
-    this.listeners.add(listener);
+  public synchronized void shuttingDown() {
+    this.alertServiceRef.get().shuttingDown();
+    this.appenderContext.getLoggerContext().removePropertyChangeListener(this);
+    this.appenderContext.getLoggerConfig().removeAppender(APPENDER_NAME);
   }
 
-  /**
-   * Converts an int alert level to an int log level.
-   * 
-   * @param alertLevel The int value for the alert level
-   * @return The int value for the matching log level
-   * @throws java.lang.IllegalArgumentException If there is no matching log level
-   */
-  public static int alertLevelToLogLevel(final int alertLevel) {
-    switch (alertLevel) {
-      case Alert.SEVERE:
-        return Level.FATAL.intLevel();
-      case Alert.ERROR:
-        return Level.ERROR.intLevel();
-      case Alert.WARNING:
-        return Level.WARN.intLevel();
-      case Alert.OFF:
-        return Level.OFF.intLevel();
-    }
-
-    throw new IllegalArgumentException("Unknown Alert level [" + alertLevel + "].");
+  @Override
+  public void clear() {
+    this.appenderContext.getLoggerContext().addPropertyChangeListener(this);
   }
 
-  /**
-   * Converts an int log level to an int alert level.
-   * 
-   * @param logLevel The int value for the log level
-   * @return The int value for the matching alert level
-   * @throws java.lang.IllegalArgumentException If there is no matching log level
-   */
-  public static int logLevelToAlertLevel(final int logLevel) {
-    if (logLevel == Level.FATAL.intLevel()) {
-      return Alert.SEVERE;
-    } else if (logLevel == Level.ERROR.intLevel()) {
-      return Alert.ERROR;
-    } else if (logLevel == Level.WARN.intLevel()) {
-      return Alert.WARNING;
-    } else if (logLevel == Level.OFF.intLevel()) {
-      return Alert.OFF;
-    }
+  @Override
+  public void lowestLevel(final int level) {
 
-    throw new IllegalArgumentException("Unknown Log level [" + logLevel + "].");
   }
 
-  public synchronized void shuttingDown() {
-    this.listeners.clear();
-    this.appenderContext.getLoggerContext().removePropertyChangeListener(this);
-    this.appenderContext.getLoggerConfig().removeAppender(APPENDER_NAME);
-  }
+  private static class NullAlertProvider implements AlertService {
 
-  /**
-   * Simple value object which holds a DistributedMember and Level pair.
-   */
-  static class Listener {
-    private Level level;
-    private DistributedMember member;
-
-    public Level getLevel() {
-      return this.level;
+    @Override
+    public boolean isEnabled() {
+      return false;
     }
 
-    public DistributedMember getMember() {
-      return this.member;
+    @Override
+    public DistributionManager getDistributionManager() {
+      return null;
     }
 
-    Listener(final Level level, final DistributedMember member) {
-      this.level = level;
-      this.member = member;
+    @Override
+    public String getMemberName() {
+      return null;
     }
 
-    /**
-     * Never used, but maintain the hashCode/equals contract.
-     */
     @Override
-    public int hashCode() {
-      return 31 + ((this.member == null) ? 0 : this.member.hashCode());
+    public boolean isAlerting() {
+      return false;
     }
 
-    /**
-     * Ignore the level when determining equality.
-     */
     @Override
-    public boolean equals(Object other) {
-      return (this.member.equals(((Listener) other).member)) ? true : false;
+    public void shuttingDown() {
+
     }
 
     @Override
-    public String toString() {
-      return "Listener [level=" + this.level + ", member=" + this.member + "]";
+    public void handleAlert(final AlertService alertProvider, final int intLevel, final Date date,
+                            final String threadName,
+                            final String logMessage, final String stackTrace,
+                            final String connectionName,
+                            final AlertSubscriber listener) {
+
     }
   }
 }

http://git-wip-us.apache.org/repos/asf/geode/blob/d9815b6d/geode-core/src/main/java/org/apache/geode/internal/logging/log4j/LogWriterAppenders.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/internal/logging/log4j/LogWriterAppenders.java b/geode-core/src/main/java/org/apache/geode/internal/logging/log4j/LogWriterAppenders.java
index 310deb2..f4d3ae9 100644
--- a/geode-core/src/main/java/org/apache/geode/internal/logging/log4j/LogWriterAppenders.java
+++ b/geode-core/src/main/java/org/apache/geode/internal/logging/log4j/LogWriterAppenders.java
@@ -108,8 +108,6 @@ public class LogWriterAppenders {
     String firstMsg = null;
     boolean firstMsgWarning = false;
 
-    AlertAppender.getInstance().setAlertingDisabled(isLoner);
-
     // security-log-file is specified in DistributionConfig
     if (isSecurity) {
       if (isDistributionConfig) {
@@ -171,10 +169,11 @@ public class LogWriterAppenders {
     ManagerLogWriter mlw = null;
     String logWriterLoggerName = null;
     if (isSecurity) {
-      mlw = new SecurityManagerLogWriter(dsConfig.getSecurityLogLevel(), out, config.getName());
+      // use false for redirectStdOut fixes bug in which some dunits redirected stdout to security
+      mlw = new SecurityManagerLogWriter(dsConfig.getSecurityLogLevel(), out, config.getName(), false);
       logWriterLoggerName = LogService.SECURITY_LOGGER_NAME;
     } else {
-      mlw = new ManagerLogWriter(config.getLogLevel(), out, config.getName());
+      mlw = new ManagerLogWriter(config.getLogLevel(), out, config.getName(), isLoner);
       logWriterLoggerName = LogService.MAIN_LOGGER_NAME;
     }
 
@@ -249,7 +248,6 @@ public class LogWriterAppenders {
   }
 
   public synchronized static void destroy(final Identifier id) {
-    // TODO:LOG:KIRK: new Exception("KIRK destroy called for " + id).printStackTrace();
     LogWriterAppender appender = appenders.get(id);
     if (appender == null) {
       return;

http://git-wip-us.apache.org/repos/asf/geode/blob/d9815b6d/geode-core/src/main/java/org/apache/geode/internal/tcp/Connection.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/internal/tcp/Connection.java b/geode-core/src/main/java/org/apache/geode/internal/tcp/Connection.java
index ca5cce1..bc76a9a 100644
--- a/geode-core/src/main/java/org/apache/geode/internal/tcp/Connection.java
+++ b/geode-core/src/main/java/org/apache/geode/internal/tcp/Connection.java
@@ -29,7 +29,6 @@ import org.apache.geode.internal.SystemTimer.SystemTimerTask;
 import org.apache.geode.internal.i18n.LocalizedStrings;
 import org.apache.geode.internal.logging.LogService;
 import org.apache.geode.internal.logging.LoggingThreadGroup;
-import org.apache.geode.internal.logging.log4j.AlertAppender;
 import org.apache.geode.internal.logging.log4j.LocalizedMessage;
 import org.apache.geode.internal.net.*;
 import org.apache.geode.internal.tcp.MsgReader.Header;
@@ -1062,7 +1061,7 @@ public class Connection implements Runnable {
         } else {
           // if we're sending an alert and can't connect, bail out. A sick
           // alert listener should not prevent cache operations from continuing
-          if (AlertAppender.isThreadAlerting()) {
+          if (DistributedAlertService.isThreadAlerting()) {
             // do not change the text of this exception - it is looked for in exception handlers
             throw new IOException("Cannot form connection to alert listener " + remoteAddr);
           }

http://git-wip-us.apache.org/repos/asf/geode/blob/d9815b6d/geode-core/src/main/java/org/apache/geode/internal/tcp/ConnectionTable.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/internal/tcp/ConnectionTable.java b/geode-core/src/main/java/org/apache/geode/internal/tcp/ConnectionTable.java
index 6307c52..41fe939 100644
--- a/geode-core/src/main/java/org/apache/geode/internal/tcp/ConnectionTable.java
+++ b/geode-core/src/main/java/org/apache/geode/internal/tcp/ConnectionTable.java
@@ -37,6 +37,7 @@ import java.util.concurrent.ThreadPoolExecutor;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicReference;
 
+import org.apache.geode.distributed.internal.DistributedAlertService;
 import org.apache.logging.log4j.Logger;
 
 import org.apache.geode.SystemFailure;
@@ -53,7 +54,6 @@ import org.apache.geode.internal.SystemTimer;
 import org.apache.geode.internal.i18n.LocalizedStrings;
 import org.apache.geode.internal.logging.LogService;
 import org.apache.geode.internal.logging.LoggingThreadGroup;
-import org.apache.geode.internal.logging.log4j.AlertAppender;
 import org.apache.geode.internal.logging.log4j.LocalizedMessage;
 
 /**
@@ -187,7 +187,7 @@ public class ConnectionTable {
   boolean threadOwnsResources() {
     DM d = getDM();
     if (d != null) {
-      return d.getSystem().threadOwnsResources() && !AlertAppender.isThreadAlerting();
+      return d.getSystem().threadOwnsResources() && !DistributedAlertService.isThreadAlerting();
     }
     return false;
 
@@ -478,7 +478,7 @@ public class ConnectionTable {
     } else { // we have existing connection
       if (mEntry instanceof PendingConnection) {
 
-        if (AlertAppender.isThreadAlerting()) {
+        if (DistributedAlertService.isThreadAlerting()) {
           // do not change the text of this exception - it is looked for in exception handlers
           throw new IOException("Cannot form connection to alert listener " + id);
         }

http://git-wip-us.apache.org/repos/asf/geode/blob/d9815b6d/geode-core/src/main/java/org/apache/geode/internal/tcp/ReenteredConnectException.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/internal/tcp/ReenteredConnectException.java b/geode-core/src/main/java/org/apache/geode/internal/tcp/ReenteredConnectException.java
index 39554cb..dc71bb2 100644
--- a/geode-core/src/main/java/org/apache/geode/internal/tcp/ReenteredConnectException.java
+++ b/geode-core/src/main/java/org/apache/geode/internal/tcp/ReenteredConnectException.java
@@ -20,7 +20,7 @@ import org.apache.geode.GemFireException;
  * An exception indicating that the same thread that is in the middle of trying to connect has tried
  * to obtain a connection to the same member further down the call stack.
  * 
- * This condition has been observered when using an AlertListener, because we try to transmit
+ * This condition has been observered when using an AlertSubscriber, because we try to transmit
  * messages logged during a connection to the very member we're trying to connect to.
  *
  */

http://git-wip-us.apache.org/repos/asf/geode/blob/d9815b6d/geode-core/src/main/java/org/apache/geode/internal/tcp/TCPConduit.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/internal/tcp/TCPConduit.java b/geode-core/src/main/java/org/apache/geode/internal/tcp/TCPConduit.java
index 1398070..d9ffd6d 100644
--- a/geode-core/src/main/java/org/apache/geode/internal/tcp/TCPConduit.java
+++ b/geode-core/src/main/java/org/apache/geode/internal/tcp/TCPConduit.java
@@ -36,6 +36,7 @@ import java.util.concurrent.ThreadPoolExecutor;
 import java.util.concurrent.TimeUnit;
 import javax.net.ssl.SSLException;
 
+import org.apache.geode.distributed.internal.DistributedAlertService;
 import org.apache.logging.log4j.Logger;
 
 import org.apache.geode.CancelCriterion;
@@ -54,7 +55,6 @@ import org.apache.geode.distributed.internal.membership.MembershipManager;
 import org.apache.geode.internal.i18n.LocalizedStrings;
 import org.apache.geode.internal.logging.LogService;
 import org.apache.geode.internal.logging.LoggingThreadGroup;
-import org.apache.geode.internal.logging.log4j.AlertAppender;
 import org.apache.geode.internal.logging.log4j.LocalizedMessage;
 import org.apache.geode.internal.logging.log4j.LogMarker;
 import org.apache.geode.internal.net.SocketCreator;
@@ -1054,7 +1054,7 @@ public class TCPConduit implements Runnable {
         } catch (IOException e) {
           problem = e;
           // bug #43962 don't keep trying to connect to an alert listener
-          if (AlertAppender.isThreadAlerting()) {
+          if (DistributedAlertService.isThreadAlerting()) {
             if (logger.isDebugEnabled()) {
               logger.debug("Giving up connecting to alert listener {}", memberAddress);
             }

http://git-wip-us.apache.org/repos/asf/geode/blob/d9815b6d/geode-core/src/main/java/org/apache/geode/management/internal/alerting/AlertHandler.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/alerting/AlertHandler.java b/geode-core/src/main/java/org/apache/geode/management/internal/alerting/AlertHandler.java
new file mode 100644
index 0000000..f247132
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/management/internal/alerting/AlertHandler.java
@@ -0,0 +1,30 @@
+/*
+ * 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.geode.management.internal.alerting;
+
+import java.util.Date;
+
+public interface AlertHandler {
+  void handleAlert(AlertService alertProvider,
+                          int intLevel,
+                          Date date,
+                          String threadName,
+                          String logMessage,
+                          String stackTrace,
+                          String connectionName,
+                          AlertSubscriber listener);
+}

http://git-wip-us.apache.org/repos/asf/geode/blob/d9815b6d/geode-core/src/main/java/org/apache/geode/management/internal/alerting/AlertLevel.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/alerting/AlertLevel.java b/geode-core/src/main/java/org/apache/geode/management/internal/alerting/AlertLevel.java
new file mode 100644
index 0000000..09ad869
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/management/internal/alerting/AlertLevel.java
@@ -0,0 +1,65 @@
+/*
+ * 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.geode.management.internal.alerting;
+
+import org.apache.geode.internal.admin.Alert;
+import org.apache.logging.log4j.Level;
+
+public class AlertLevel {
+  /**
+   * Converts an int alert level to an int log level.
+   *
+   * @param alertLevel The int value for the alert level
+   * @return The int value for the matching log level
+   * @throws IllegalArgumentException If there is no matching log level
+   */
+  public static int alertLevelToLogLevel(final int alertLevel) {
+    switch (alertLevel) {
+      case Alert.SEVERE:
+        return Level.FATAL.intLevel();
+      case Alert.ERROR:
+        return Level.ERROR.intLevel();
+      case Alert.WARNING:
+        return Level.WARN.intLevel();
+      case Alert.OFF:
+        return Level.OFF.intLevel();
+    }
+
+    throw new IllegalArgumentException("Unknown Alert level [" + alertLevel + "].");
+  }
+
+  /**
+   * Converts an int log level to an int alert level.
+   *
+   * @param logLevel The int value for the log level
+   * @return The int value for the matching alert level
+   * @throws IllegalArgumentException If there is no matching log level
+   */
+  public static int logLevelToAlertLevel(final int logLevel) {
+    if (logLevel == Level.FATAL.intLevel()) {
+      return Alert.SEVERE;
+    } else if (logLevel == Level.ERROR.intLevel()) {
+      return Alert.ERROR;
+    } else if (logLevel == Level.WARN.intLevel()) {
+      return Alert.WARNING;
+    } else if (logLevel == Level.OFF.intLevel()) {
+      return Alert.OFF;
+    }
+
+    throw new IllegalArgumentException("Unknown Log level [" + logLevel + "].");
+  }
+}

http://git-wip-us.apache.org/repos/asf/geode/blob/d9815b6d/geode-core/src/main/java/org/apache/geode/management/internal/alerting/AlertService.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/alerting/AlertService.java b/geode-core/src/main/java/org/apache/geode/management/internal/alerting/AlertService.java
new file mode 100644
index 0000000..2c4f272
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/management/internal/alerting/AlertService.java
@@ -0,0 +1,37 @@
+/*
+ * 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.geode.management.internal.alerting;
+
+import org.apache.geode.distributed.internal.DistributionManager;
+import org.apache.geode.internal.logging.log4j.AlertAppender;
+
+public interface AlertService extends AlertHandler {
+
+  static void install(final AlertService service) {
+    AlertAppender.updateAlertService(service);
+  }
+
+  boolean isEnabled();
+
+  DistributionManager getDistributionManager();
+
+  String getMemberName();
+
+  boolean isAlerting();
+
+  void shuttingDown();
+}

http://git-wip-us.apache.org/repos/asf/geode/blob/d9815b6d/geode-core/src/main/java/org/apache/geode/management/internal/alerting/AlertServiceListener.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/alerting/AlertServiceListener.java b/geode-core/src/main/java/org/apache/geode/management/internal/alerting/AlertServiceListener.java
new file mode 100644
index 0000000..9e44bb1
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/management/internal/alerting/AlertServiceListener.java
@@ -0,0 +1,23 @@
+/*
+ * 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.geode.management.internal.alerting;
+
+public interface AlertServiceListener {
+
+  void clear();
+  void lowestLevel(int level);
+}

http://git-wip-us.apache.org/repos/asf/geode/blob/d9815b6d/geode-core/src/main/java/org/apache/geode/management/internal/alerting/AlertSubscriber.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/alerting/AlertSubscriber.java b/geode-core/src/main/java/org/apache/geode/management/internal/alerting/AlertSubscriber.java
new file mode 100644
index 0000000..b7b64b0
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/management/internal/alerting/AlertSubscriber.java
@@ -0,0 +1,63 @@
+/*
+ * 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.geode.management.internal.alerting;
+
+import org.apache.geode.distributed.DistributedMember;
+import org.apache.logging.log4j.Level;
+
+/**
+ * Simple value object which holds a DistributedMember and Level pair.
+ */
+public class AlertSubscriber {
+
+  private final Level level;
+  private final DistributedMember member;
+
+  public AlertSubscriber(final Level level, final DistributedMember member) {
+    this.level = level;
+    this.member = member;
+  }
+
+  public Level getLevel() {
+    return this.level;
+  }
+
+  public DistributedMember getMember() {
+    return this.member;
+  }
+
+  /**
+   * Never used, but maintain the hashCode/equals contract.
+   */
+  @Override
+  public int hashCode() {
+    return 31 + ((this.member == null) ? 0 : this.member.hashCode());
+  }
+
+  /**
+   * Ignore the level when determining equality.
+   */
+  @Override
+  public boolean equals(Object other) {
+    return (this.member.equals(((AlertSubscriber) other).member)) ? true : false;
+  }
+
+  @Override
+  public String toString() {
+    return "AlertSubscriber [level=" + this.level + ", member=" + this.member + "]";
+  }
+}

http://git-wip-us.apache.org/repos/asf/geode/blob/d9815b6d/geode-core/src/test/java/org/apache/geode/distributed/internal/membership/gms/messenger/JGroupsMessengerJUnitTest.java
----------------------------------------------------------------------
diff --git a/geode-core/src/test/java/org/apache/geode/distributed/internal/membership/gms/messenger/JGroupsMessengerJUnitTest.java b/geode-core/src/test/java/org/apache/geode/distributed/internal/membership/gms/messenger/JGroupsMessengerJUnitTest.java
index 307b594..b1914db 100755
--- a/geode-core/src/test/java/org/apache/geode/distributed/internal/membership/gms/messenger/JGroupsMessengerJUnitTest.java
+++ b/geode-core/src/test/java/org/apache/geode/distributed/internal/membership/gms/messenger/JGroupsMessengerJUnitTest.java
@@ -36,10 +36,8 @@ import org.apache.geode.distributed.internal.membership.gms.messages.JoinRequest
 import org.apache.geode.distributed.internal.membership.gms.messages.JoinResponseMessage;
 import org.apache.geode.distributed.internal.membership.gms.messages.LeaveRequestMessage;
 import org.apache.geode.distributed.internal.membership.gms.messenger.JGroupsMessenger.JGroupsReceiver;
-import org.apache.geode.internal.*;
 import org.apache.geode.internal.admin.remote.RemoteTransportConfig;
 import org.apache.geode.internal.cache.DistributedCacheOperation;
-import org.apache.geode.internal.logging.log4j.AlertAppender;
 import org.apache.geode.test.junit.categories.IntegrationTest;
 
 import static org.apache.geode.distributed.ConfigurationProperties.*;
@@ -223,13 +221,13 @@ public class JGroupsMessengerJUnitTest {
     messenger.setMessageFlags(dmsg, jgmsg);
     assertFalse("expected no_fc to not be set in " + jgmsg.getFlags(),
         jgmsg.isFlagSet(Message.Flag.NO_FC));
-    AlertAppender.setIsAlerting(true);
+    DistributedAlertService.setIsAlerting(true);
     try {
       messenger.setMessageFlags(dmsg, jgmsg);
       assertTrue("expected no_fc to be set in " + jgmsg.getFlags(),
           jgmsg.isFlagSet(Message.Flag.NO_FC));
     } finally {
-      AlertAppender.setIsAlerting(false);
+      DistributedAlertService.setIsAlerting(false);
     }
   }
 

http://git-wip-us.apache.org/repos/asf/geode/blob/d9815b6d/geode-core/src/test/java/org/apache/geode/internal/logging/TestLogWriterFactory.java
----------------------------------------------------------------------
diff --git a/geode-core/src/test/java/org/apache/geode/internal/logging/TestLogWriterFactory.java b/geode-core/src/test/java/org/apache/geode/internal/logging/TestLogWriterFactory.java
index 87f370c..f84c518 100644
--- a/geode-core/src/test/java/org/apache/geode/internal/logging/TestLogWriterFactory.java
+++ b/geode-core/src/test/java/org/apache/geode/internal/logging/TestLogWriterFactory.java
@@ -90,9 +90,9 @@ public class TestLogWriterFactory extends Assert {
       }
 
       if (isSecurityLog) {
-        mlw = new SecurityManagerLogWriter(config.getSecurityLogLevel(), out, config.getName());
+        mlw = new SecurityManagerLogWriter(config.getSecurityLogLevel(), out, config.getName(), false);
       } else {
-        mlw = new ManagerLogWriter(config.getLogLevel(), out, config.getName());
+        mlw = new ManagerLogWriter(config.getLogLevel(), out, config.getName(), false);
       }
       ((ManagerLogWriter) mlw).setConfig(config);
     }

http://git-wip-us.apache.org/repos/asf/geode/blob/d9815b6d/geode-core/src/test/java/org/apache/geode/internal/logging/log4j/AlertAppenderJUnitTest.java
----------------------------------------------------------------------
diff --git a/geode-core/src/test/java/org/apache/geode/internal/logging/log4j/AlertAppenderJUnitTest.java b/geode-core/src/test/java/org/apache/geode/internal/logging/log4j/AlertAppenderJUnitTest.java
index 5717253..1a440b1 100644
--- a/geode-core/src/test/java/org/apache/geode/internal/logging/log4j/AlertAppenderJUnitTest.java
+++ b/geode-core/src/test/java/org/apache/geode/internal/logging/log4j/AlertAppenderJUnitTest.java
@@ -23,6 +23,7 @@ import java.util.List;
 import java.util.Set;
 import java.util.concurrent.CopyOnWriteArrayList;
 
+import org.apache.geode.management.internal.alerting.AlertSubscriber;
 import org.apache.logging.log4j.Level;
 import org.apache.logging.log4j.core.config.LoggerConfig;
 import org.junit.After;
@@ -43,12 +44,13 @@ import org.apache.geode.test.junit.categories.UnitTest;
 @Category(UnitTest.class)
 public class AlertAppenderJUnitTest {
 
-  private final List<DistributedMember> members = new ArrayList<DistributedMember>();
+  private List<DistributedMember> members;
   private Level previousLogLevel;
 
   @Before
   public void setUp() {
     this.previousLogLevel = LogService.getBaseLogLevel();
+    this.members = new ArrayList<>();
   }
 
   @After
@@ -62,10 +64,6 @@ public class AlertAppenderJUnitTest {
     }
   }
 
-  private DistributedMember createTestDistributedMember(String name) {
-    return new TestDistributedMember(name);
-  }
-
   /**
    * Verify that adding/removing/replacing listeners works correctly.
    */
@@ -91,8 +89,8 @@ public class AlertAppenderJUnitTest {
     listenersField.setAccessible(true);
 
     @SuppressWarnings("unchecked")
-    final CopyOnWriteArrayList<AlertAppender.Listener> listeners =
-        (CopyOnWriteArrayList<AlertAppender.Listener>) listenersField
+    final CopyOnWriteArrayList<AlertSubscriber> listeners =
+        (CopyOnWriteArrayList<AlertSubscriber>) listenersField
             .get(AlertAppender.getInstance());
 
     // Verify add
@@ -186,6 +184,10 @@ public class AlertAppenderJUnitTest {
     assertFalse(loggerConfig.getAppenders().containsKey(appenderName));
   }
 
+  private DistributedMember createTestDistributedMember(final String name) {
+    return new TestDistributedMember(name);
+  }
+
   private static class TestDistributedMember implements DistributedMember {
     private final String name;
 


Mime
View raw message