You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@geode.apache.org by ji...@apache.org on 2017/02/13 19:05:42 UTC

[2/2] geode git commit: GEODE-2416: Collect together artifacts from individual servers into a single zip file

GEODE-2416: Collect together artifacts from individual servers into a single zip file

 - GEODE-2414: Determine a mechanism to stream a zip file from server to locator
 - GEODE-2415: Write a function to return a zip file for a single server


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

Branch: refs/heads/feature/GEODE-2267
Commit: d5d7e80011d1cc11468be769acd2bc1b517b766d
Parents: 652e9a5
Author: Jared Stewart <js...@pivotal.io>
Authored: Sun Feb 12 11:30:58 2017 -0800
Committer: Jinmei Liao <ji...@pivotal.io>
Committed: Mon Feb 13 10:58:47 2017 -0800

----------------------------------------------------------------------
 .../cli/commands/MiscellaneousCommands.java     | 312 ++++---------------
 .../cli/functions/ExportLogsFunction.java       | 185 +++++++++++
 .../cli/util/ExportLogsCacheWriter.java         |  75 +++++
 .../internal/cli/util/LogExporter.java          | 136 ++++++++
 .../management/internal/cli/util/LogFilter.java | 113 +++++++
 .../internal/cli/util/LogLevelExtractor.java    |  65 ++++
 .../internal/configuration/utils/ZipUtils.java  |   3 +-
 .../internal/cli/commands/ExportLogsDUnit.java  | 122 ++++++++
 ...laneousCommandsExportLogsPart1DUnitTest.java | 137 --------
 ...laneousCommandsExportLogsPart2DUnitTest.java | 141 ---------
 ...laneousCommandsExportLogsPart3DUnitTest.java | 155 ---------
 ...laneousCommandsExportLogsPart4DUnitTest.java | 137 --------
 .../cli/functions/ExportLogsFunctionTest.java   | 118 +++++++
 .../internal/cli/util/LogExporterTest.java      | 104 +++++++
 .../internal/cli/util/LogFilterTest.java        | 126 ++++++++
 .../cli/util/LogLevelExtractorTest.java         |  61 ++++
 .../configuration/EventTestCacheWriter.java     |  36 +++
 .../test/dunit/rules/LocatorStarterRule.java    |  11 +
 .../test/dunit/rules/ServerStarterRule.java     |   9 +-
 19 files changed, 1222 insertions(+), 824 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/geode/blob/d5d7e800/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/MiscellaneousCommands.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/MiscellaneousCommands.java b/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/MiscellaneousCommands.java
index bbf0542..b30633a 100644
--- a/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/MiscellaneousCommands.java
+++ b/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/MiscellaneousCommands.java
@@ -24,10 +24,10 @@ import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.io.OutputStream;
 import java.io.PrintWriter;
-import java.sql.Time;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
 import java.text.MessageFormat;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -38,7 +38,6 @@ import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
-import java.util.StringTokenizer;
 import java.util.concurrent.Callable;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
@@ -50,9 +49,11 @@ import java.util.zip.DataFormatException;
 import java.util.zip.GZIPInputStream;
 import javax.management.ObjectName;
 
+import org.apache.commons.io.FileUtils;
 import org.apache.geode.LogWriter;
 import org.apache.geode.cache.Cache;
 import org.apache.geode.cache.CacheFactory;
+import org.apache.geode.cache.Region;
 import org.apache.geode.cache.execute.Execution;
 import org.apache.geode.cache.execute.Function;
 import org.apache.geode.cache.execute.FunctionException;
@@ -65,8 +66,7 @@ import org.apache.geode.distributed.internal.deadlock.Dependency;
 import org.apache.geode.distributed.internal.deadlock.DependencyGraph;
 import org.apache.geode.distributed.internal.deadlock.GemFireDeadlockDetector;
 import org.apache.geode.internal.cache.GemFireCacheImpl;
-import org.apache.geode.internal.i18n.LocalizedStrings;
-import org.apache.geode.internal.logging.InternalLogWriter;
+import org.apache.geode.internal.logging.LogService;
 import org.apache.geode.internal.logging.LogWriterImpl;
 import org.apache.geode.management.CacheServerMXBean;
 import org.apache.geode.management.DistributedRegionMXBean;
@@ -89,9 +89,9 @@ import org.apache.geode.management.internal.cli.GfshParser;
 import org.apache.geode.management.internal.cli.LogWrapper;
 import org.apache.geode.management.internal.cli.domain.StackTracesPerMember;
 import org.apache.geode.management.internal.cli.functions.ChangeLogLevelFunction;
+import org.apache.geode.management.internal.cli.functions.ExportLogsFunction;
 import org.apache.geode.management.internal.cli.functions.GarbageCollectionFunction;
 import org.apache.geode.management.internal.cli.functions.GetStackTracesFunction;
-import org.apache.geode.management.internal.cli.functions.LogFileFunction;
 import org.apache.geode.management.internal.cli.functions.NetstatFunction;
 import org.apache.geode.management.internal.cli.functions.NetstatFunction.NetstatFunctionArgument;
 import org.apache.geode.management.internal.cli.functions.NetstatFunction.NetstatFunctionResult;
@@ -109,17 +109,19 @@ import org.apache.geode.management.internal.cli.result.ResultDataException;
 import org.apache.geode.management.internal.cli.result.TabularResultData;
 import org.apache.geode.management.internal.cli.shell.Gfsh;
 import org.apache.geode.management.internal.cli.util.MergeLogs;
+import org.apache.geode.management.internal.cli.util.ExportLogsCacheWriter;
+import org.apache.geode.management.internal.configuration.utils.ZipUtils;
 import org.apache.geode.management.internal.security.ResourceOperation;
 import org.apache.geode.security.ResourcePermission.Operation;
 import org.apache.geode.security.ResourcePermission.Resource;
 
+import org.apache.logging.log4j.Logger;
 import org.springframework.shell.core.CommandMarker;
 import org.springframework.shell.core.annotation.CliAvailabilityIndicator;
 import org.springframework.shell.core.annotation.CliCommand;
 import org.springframework.shell.core.annotation.CliOption;
 
 /**
- *
  * @since GemFire 7.0
  */
 public class MiscellaneousCommands implements CommandMarker {
@@ -145,8 +147,6 @@ public class MiscellaneousCommands implements CommandMarker {
 
       logger.info("Gfsh executing shutdown on members " + includeMembers);
 
-
-
       Callable<String> shutdownNodes = new Callable<String>() {
 
         @Override
@@ -210,8 +210,6 @@ public class MiscellaneousCommands implements CommandMarker {
 
       locators.removeAll(dataNodes);
 
-
-
       if (!shutdownLocators && numDataNodes == 0) {
         return ResultBuilder.createInfoResult(CliStrings.SHUTDOWN__MSG__NO_DATA_NODE_FOUND);
       }
@@ -248,8 +246,8 @@ public class MiscellaneousCommands implements CommandMarker {
       }
 
       if (locators.contains(manager) && !shutdownLocators) { // This means manager is a locator and
-                                                             // shutdownLocators is false. Hence we
-                                                             // should not stop the manager
+        // shutdownLocators is false. Hence we
+        // should not stop the manager
         return ResultBuilder.createInfoResult("Shutdown is triggered");
       }
       // now shut down this manager
@@ -272,12 +270,9 @@ public class MiscellaneousCommands implements CommandMarker {
   }
 
   /**
-   * 
    * @param timeout user specified timeout
    * @param nodesToBeStopped list of nodes to be stopped
    * @return Elapsed time to shutdown the given nodes;
-   * @throws ExecutionException
-   * @throws InterruptedException
    */
   private long shutDownNodeWithTimeOut(long timeout, Set<DistributedMember> nodesToBeStopped)
       throws TimeoutException, InterruptedException, ExecutionException {
@@ -290,9 +285,9 @@ public class MiscellaneousCommands implements CommandMarker {
     long timeElapsed = shutDownTimeEnd - shutDownTimeStart;
 
     if (timeElapsed > timeout || Boolean.getBoolean("ThrowTimeoutException")) { // The second check
-                                                                                // for
-                                                                                // ThrowTimeoutException
-                                                                                // is a test hook
+      // for
+      // ThrowTimeoutException
+      // is a test hook
       throw new TimeoutException();
     }
     return timeElapsed;
@@ -532,7 +527,7 @@ public class MiscellaneousCommands implements CommandMarker {
         }
         resultData.addAsFile(saveToFile, resultInfo.toString(),
             CliStrings.NETSTAT__MSG__SAVED_OUTPUT_IN_0, false); // Note: substitution for {0} will
-                                                                // happen on client side.
+        // happen on client side.
       } else {
         resultData.addLine(resultInfo.toString());
       }
@@ -694,87 +689,6 @@ public class MiscellaneousCommands implements CommandMarker {
     return result;
   }
 
-  Result exportLogsPreprocessing(String dirName, String[] groups, String memberId, String logLevel,
-      boolean onlyLogLevel, boolean mergeLog, String start, String end,
-      int numOfLogFilesForTesting) {
-    Result result = null;
-    try {
-      LogWrapper.getInstance().fine("Exporting logs");
-      Cache cache = CacheFactory.getAnyInstance();
-      Set<DistributedMember> dsMembers = new HashSet<DistributedMember>();
-      Time startTime = null, endTime = null;
-
-      if (logLevel == null || logLevel.length() == 0) {
-        // set default log level
-        logLevel = LogWriterImpl.levelToString(InternalLogWriter.INFO_LEVEL);
-      }
-      if (start != null && end == null) {
-        startTime = parseDate(start);
-        endTime = new Time(System.currentTimeMillis());
-      }
-
-      if (end != null && start == null) {
-        endTime = parseDate(end);
-        startTime = new Time(0);
-      }
-      if (start != null && end != null) {
-        startTime = parseDate(start);
-        endTime = parseDate(end);
-        if (endTime.getTime() - startTime.getTime() <= 0) {
-          result =
-              ResultBuilder.createUserErrorResult(CliStrings.EXPORT_LOGS__MSG__INVALID_TIMERANGE);
-        }
-      }
-      if (end == null && start == null) {
-        // set default time range as 1 day.
-        endTime = new Time(System.currentTimeMillis());
-        startTime = new Time(endTime.getTime() - 24 * 60 * 60 * 1000);
-      }
-      LogWrapper.getInstance().fine(
-          "Exporting logs startTime=" + startTime.toGMTString() + " " + startTime.toLocaleString());
-      LogWrapper.getInstance()
-          .fine("Exporting logs endTime=" + endTime.toGMTString() + " " + endTime.toLocaleString());
-      if (groups != null && memberId != null) {
-        result =
-            ResultBuilder.createUserErrorResult(CliStrings.EXPORT_LOGS__MSG__SPECIFY_ONE_OF_OPTION);
-      } else if (groups != null && groups.length > 0) {
-        for (String group : groups) {
-          Set<DistributedMember> groupMembers = cache.getDistributedSystem().getGroupMembers(group);
-          if (groupMembers != null && groupMembers.size() > 0) {
-            dsMembers.addAll(groupMembers);
-          }
-        }
-        if (dsMembers.size() == 0) {
-          result = ResultBuilder
-              .createUserErrorResult(CliStrings.EXPORT_LOGS__MSG__NO_GROUPMEMBER_FOUND);
-        }
-        result = export(cache, dsMembers, dirName, logLevel, onlyLogLevel ? "true" : "false",
-            mergeLog, startTime, endTime, numOfLogFilesForTesting);
-      } else if (memberId != null) {
-        DistributedMember member = CliUtil.getDistributedMemberByNameOrId(memberId);
-        if (member == null) {
-          result = ResultBuilder.createUserErrorResult(
-              CliStrings.format(CliStrings.EXPORT_LOGS__MSG__INVALID_MEMBERID, memberId));
-        }
-        dsMembers.add(member);
-        result = export(cache, dsMembers, dirName, logLevel, onlyLogLevel ? "true" : "false",
-            mergeLog, startTime, endTime, numOfLogFilesForTesting);
-      } else {
-        // run in entire DS members and get all including locators
-        dsMembers.addAll(CliUtil.getAllMembers(cache));
-        result = export(cache, dsMembers, dirName, logLevel, onlyLogLevel ? "true" : "false",
-            mergeLog, startTime, endTime, numOfLogFilesForTesting);
-      }
-    } catch (ParseException ex) {
-      LogWrapper.getInstance().fine(ex.getMessage());
-      result = ResultBuilder.createUserErrorResult(ex.getMessage());
-    } catch (Exception ex) {
-      LogWrapper.getInstance().fine(ex.getMessage());
-      result = ResultBuilder.createUserErrorResult(ex.getMessage());
-    }
-    return result;
-  }
-
   @CliCommand(value = CliStrings.EXPORT_LOGS, help = CliStrings.EXPORT_LOGS__HELP)
   @CliMetaData(shellOnly = false,
       relatedTopic = {CliStrings.TOPIC_GEODE_SERVER, CliStrings.TOPIC_GEODE_DEBUG_UTIL})
@@ -789,7 +703,7 @@ public class MiscellaneousCommands implements CommandMarker {
       @CliOption(key = CliStrings.EXPORT_LOGS__MEMBER,
           unspecifiedDefaultValue = CliMetaData.ANNOTATION_NULL_VALUE,
           optionContext = ConverterHint.ALL_MEMBER_IDNAME,
-          help = CliStrings.EXPORT_LOGS__MEMBER__HELP) String memberId,
+          help = CliStrings.EXPORT_LOGS__MEMBER__HELP) String[] memberIds,
       @CliOption(key = CliStrings.EXPORT_LOGS__LOGLEVEL,
           unspecifiedDefaultValue = CliMetaData.ANNOTATION_NULL_VALUE,
           optionContext = ConverterHint.LOG_LEVEL,
@@ -806,151 +720,51 @@ public class MiscellaneousCommands implements CommandMarker {
           help = CliStrings.EXPORT_LOGS__ENDTIME__HELP) String end) {
     Result result = null;
     try {
-      result = exportLogsPreprocessing(dirName, groups, memberId, logLevel, onlyLogLevel, mergeLog,
-          start, end, 0);
-    } catch (Exception ex) {
-      LogWrapper.getInstance().fine(ex.getMessage());
-      result = ResultBuilder.createUserErrorResult(ex.getMessage());
-    }
-    LogWrapper.getInstance().fine("Exporting logs returning =" + result);
-    return result;
-  }
+      GemFireCacheImpl cache = GemFireCacheImpl.getInstance();
+      Region region = ExportLogsFunction.createOrGetExistingExportLogsRegion(true);
+      ExportLogsCacheWriter cacheWriter =
+          (ExportLogsCacheWriter) region.getAttributes().getCacheWriter();
 
-  Time parseDate(String dateString) throws ParseException {
-    Time time = null;
-    try {
-      SimpleDateFormat df = new SimpleDateFormat(MiscellaneousCommands.FORMAT);
-      time = new Time(df.parse(dateString).getTime());
-    } catch (Exception e) {
-      SimpleDateFormat df = new SimpleDateFormat(MiscellaneousCommands.ONLY_DATE_FORMAT);
-      time = new Time(df.parse(dateString).getTime());
-    }
-    return time;
-  }
+      // TODO: Make this work for locators as well
+      Set<DistributedMember> targetServers = CliUtil.findMembers(groups, memberIds);
+      Logger logger = LogService.getLogger();
 
-  Result export(Cache cache, Set<DistributedMember> dsMembers, String dirName, String logLevel,
-      String onlyLogLevel, boolean mergeLog, Time startTime, Time endTime,
-      int numOfLogFilesForTesting) {
-    LogWrapper.getInstance()
-        .fine("Exporting logs in export membersize = " + dsMembers.size() + " dirname=" + dirName
-            + " logLevel=" + logLevel + " onlyLogLevel=" + onlyLogLevel + " mergeLog=" + mergeLog
-            + " startTime=" + startTime.toGMTString() + "endTime=" + endTime.toGMTString());
-    Function function = new LogFileFunction();
-    FunctionService.registerFunction(function);
-    try {
-      List<?> resultList = null;
+      Map<String, Path> zipFilesFromMembers = new HashMap<>();
+      for (DistributedMember server : targetServers) {
+        cacheWriter.startFile(server.getName());
 
-      List<String> logsToMerge = new ArrayList<String>();
-
-      Iterator<DistributedMember> it = dsMembers.iterator();
-      Object[] args = new Object[6];
-      args[0] = dirName;
-      args[1] = logLevel;
-      args[2] = onlyLogLevel;
-      args[3] = startTime.getTime();
-      args[4] = endTime.getTime();
-      args[5] = numOfLogFilesForTesting;
-
-      while (it.hasNext()) {
-        boolean toContinueForRestOfmembers = false;
-        DistributedMember member = it.next();
-        LogWrapper.getInstance().fine("Exporting logs copy the logs for member=" + member.getId());
-        try {
-          resultList = (ArrayList<?>) CliUtil.executeFunction(function, args, member).getResult();
-        } catch (Exception ex) {
-          LogWrapper.getInstance()
-              .fine(CliStrings.format(
-                  CliStrings.EXPORT_LOGS__MSG__FAILED_TO_EXPORT_LOG_FILES_FOR_MEMBER_0,
-                  member.getId()), ex);
-          // try for other members
-          continue;
-        }
+        CliUtil
+            .executeFunction(new ExportLogsFunction(),
+                new ExportLogsFunction.Args(start, end, logLevel, onlyLogLevel), server)
+            .getResult();
+        Path zipFile = cacheWriter.endFile();
+        logger.info("Recieved zip file from member " + server.getId() + ": " + zipFile.toString());
+        zipFilesFromMembers.put(server.getId(), zipFile);
+      }
 
-        if (resultList != null && !resultList.isEmpty()) {
-          for (int i = 0; i < resultList.size(); i++) {
-            Object object = resultList.get(i);
-            if (object instanceof Exception) {
-              ResultBuilder.createGemFireErrorResult(CliStrings.format(
-                  CliStrings.EXPORT_LOGS__MSG__FAILED_TO_EXPORT_LOG_FILES_FOR_MEMBER_0,
-                  member.getId()));
-              LogWrapper.getInstance().fine("Exporting logs for member=" + member.getId()
-                  + " exception=" + ((Throwable) object).getMessage(), ((Throwable) object));
-              toContinueForRestOfmembers = true;
-              break;
-            } else if (object instanceof Throwable) {
-              ResultBuilder.createGemFireErrorResult(CliStrings.format(
-                  CliStrings.EXPORT_LOGS__MSG__FAILED_TO_EXPORT_LOG_FILES_FOR_MEMBER_0,
-                  member.getId()));
-
-              Throwable th = (Throwable) object;
-              LogWrapper.getInstance().fine(CliUtil.stackTraceAsString((th)));
-              LogWrapper.getInstance().fine("Exporting logs for member=" + member.getId()
-                  + " exception=" + ((Throwable) object).getMessage(), ((Throwable) object));
-              toContinueForRestOfmembers = true;
-              break;
-            }
-          }
-        } else {
-          LogWrapper.getInstance().fine("Exporting logs for member=" + member.getId()
-              + " resultList is either null or empty");
-          continue;
-        }
+      Path tempDir = Files.createTempDirectory("exportedLogs");
+      Path exportedLogsDir = tempDir.resolve("exportedLogs");
 
-        if (toContinueForRestOfmembers == true) {
-          LogWrapper.getInstance().fine("Exporting logs for member=" + member.getId()
-              + " toContinueForRestOfmembers=" + toContinueForRestOfmembers);
-          // proceed for rest of the member
-          continue;
-        }
+      for (Path zipFile : zipFilesFromMembers.values()) {
+        Path unzippedMemberDir =
+            exportedLogsDir.resolve(zipFile.getFileName().toString().replace(".zip", ""));
+        ZipUtils.unzip(zipFile.toAbsolutePath().toString(), unzippedMemberDir.toString());
+        FileUtils.deleteQuietly(zipFile.toFile());
+      }
 
-        String rstList = (String) resultList.get(0);
-        LogWrapper.getInstance().fine("for member=" + member.getId()
-            + "Successfully exported to directory=" + dirName + " rstList=" + rstList);
-        if (rstList == null || rstList.length() == 0) {
-          ResultBuilder.createGemFireErrorResult(CliStrings.format(
-              CliStrings.EXPORT_LOGS__MSG__FAILED_TO_EXPORT_LOG_FILES_FOR_MEMBER_0,
-              member.getId()));
-          LogWrapper.getInstance().fine("for member=" + member.getId() + "rstList is null");
-          continue;
-        } else if (rstList.contains("does not exist or cannot be created")) {
-          LogWrapper.getInstance()
-              .fine("for member=" + member.getId() + " does not exist or cannot be created");
-          return ResultBuilder.createInfoResult(CliStrings
-              .format(CliStrings.EXPORT_LOGS__MSG__TARGET_DIR_CANNOT_BE_CREATED, dirName));
-        } else if (rstList.contains(
-            LocalizedStrings.InternalDistributedSystem_THIS_CONNECTION_TO_A_DISTRIBUTED_SYSTEM_HAS_BEEN_DISCONNECTED
-                .toLocalizedString())) {
-          LogWrapper.getInstance()
-              .fine("for member=" + member.getId()
-                  + LocalizedStrings.InternalDistributedSystem_THIS_CONNECTION_TO_A_DISTRIBUTED_SYSTEM_HAS_BEEN_DISCONNECTED
-                      .toLocalizedString());
-          // proceed for rest of the members
-          continue;
-        }
+      Path exportedLogsZipFile = Paths.get("exportedLogs[" + System.currentTimeMillis() + "].zip");
+
+      logger.info("Zipping into: " + exportedLogsZipFile.toString());
+      ZipUtils.zipDirectory(exportedLogsDir, exportedLogsZipFile);
+      FileUtils.deleteDirectory(tempDir.toFile());
 
-        // maintain list of log files to merge only when merge option is true.
-        if (mergeLog == true) {
-          StringTokenizer st = new StringTokenizer(rstList, ";");
-          while (st.hasMoreTokens()) {
-            logsToMerge.add(st.nextToken());
-          }
-        }
-      }
-      // merge log files
-      if (mergeLog == true) {
-        LogWrapper.getInstance().fine("Successfully exported to directory=" + dirName
-            + " and now merging logsToMerge=" + logsToMerge.size());
-        mergeLogs(logsToMerge);
-        return ResultBuilder
-            .createInfoResult("Successfully exported and merged in directory " + dirName);
-      }
-      LogWrapper.getInstance().fine("Successfully exported to directory without merging" + dirName);
-      return ResultBuilder.createInfoResult("Successfully exported to directory " + dirName);
     } catch (Exception ex) {
-      LogWrapper.getInstance().info(ex.getMessage(), ex);
-      return ResultBuilder.createUserErrorResult(
-          CliStrings.EXPORT_LOGS__MSG__FUNCTION_EXCEPTION + ((LogFileFunction) function).getId());
+      LogWrapper.getInstance().fine(ex.getMessage());
+      result = ResultBuilder.createUserErrorResult(ex.getMessage());
     }
+
+    LogWrapper.getInstance().fine("Exporting logs returning =" + result);
+    return result;
   }
 
   Result mergeLogs(List<String> logsToMerge) {
@@ -1112,7 +926,7 @@ public class MiscellaneousCommands implements CommandMarker {
 
   /***
    * Writes the Stack traces member-wise to a text file
-   * 
+   *
    * @param dumps - Map containing key : member , value : zipped stack traces
    * @param fileName - Name of the file to which the stack-traces are written to
    * @return Canonical path of the file which contains the stack-traces
@@ -1266,7 +1080,6 @@ public class MiscellaneousCommands implements CommandMarker {
         csvBuilder.append('\n');
       }
 
-
       CompositeResultData crd = ResultBuilder.createCompositeResultData();
       SectionResultData section = crd.addSection();
       TabularResultData metricsTable = section.addTable();
@@ -1410,7 +1223,6 @@ public class MiscellaneousCommands implements CommandMarker {
         Set<String> checkSet = new HashSet<String>(categoriesMap.keySet());
         Set<String> userCategories = getSetDifference(categories, checkSet);
 
-
         // Checking if the categories specified by the user are valid or not
         if (userCategories.isEmpty()) {
           for (String category : checkSet) {
@@ -1604,7 +1416,7 @@ public class MiscellaneousCommands implements CommandMarker {
             memberMxBean.getDiskFlushAvgLatency(), csvBuilder);
         writeToTableAndCsv(metricsTable, "", "totalQueueSize",
             memberMxBean.getTotalDiskTasksWaiting(), csvBuilder); // deadcoded to workaround bug
-                                                                  // 46397
+        // 46397
         writeToTableAndCsv(metricsTable, "", "totalBackupInProgress",
             memberMxBean.getTotalBackupInProgress(), csvBuilder);
       }
@@ -2059,7 +1871,7 @@ public class MiscellaneousCommands implements CommandMarker {
 
   /***
    * Writes an entry to a TabularResultData and writes a comma separated entry to a string builder
-   * 
+   *
    * @param metricsTable
    * @param type
    * @param metricName
@@ -2116,7 +1928,7 @@ public class MiscellaneousCommands implements CommandMarker {
 
   /****
    * Defines and returns map of categories for System metrics.
-   * 
+   *
    * @return map with categories for system metrics and display flag set to true
    */
   private Map<String, Boolean> getSystemMetricsCategories() {
@@ -2130,7 +1942,7 @@ public class MiscellaneousCommands implements CommandMarker {
 
   /****
    * Defines and returns map of categories for Region Metrics
-   * 
+   *
    * @return map with categories for region metrics and display flag set to true
    */
   private Map<String, Boolean> getRegionMetricsCategories() {
@@ -2149,7 +1961,7 @@ public class MiscellaneousCommands implements CommandMarker {
 
   /****
    * Defines and returns map of categories for system-wide region metrics
-   * 
+   *
    * @return map with categories for system wide region metrics and display flag set to true
    */
   private Map<String, Boolean> getSystemRegionMetricsCategories() {
@@ -2160,7 +1972,7 @@ public class MiscellaneousCommands implements CommandMarker {
 
   /*****
    * Defines and returns map of categories for member metrics
-   * 
+   *
    * @return map with categories for member metrics and display flag set to true
    */
   private Map<String, Boolean> getMemberMetricsCategories() {
@@ -2214,7 +2026,7 @@ public class MiscellaneousCommands implements CommandMarker {
 
   /***
    * Writes to a TabularResultData and also appends a CSV string to a String builder
-   * 
+   *
    * @param metricsTable
    * @param type
    * @param metricName
@@ -2280,7 +2092,6 @@ public class MiscellaneousCommands implements CommandMarker {
       Set<DistributedMember> dsMembers = new HashSet<DistributedMember>();
       Set<DistributedMember> ds = CliUtil.getAllMembers(cache);
 
-
       if (grps != null && grps.length > 0) {
         for (String grp : grps) {
           dsMembers.addAll(cache.getDistributedSystem().getGroupMembers(grp));
@@ -2359,7 +2170,6 @@ public class MiscellaneousCommands implements CommandMarker {
   }
 
 
-
   @CliAvailabilityIndicator({CliStrings.SHUTDOWN, CliStrings.GC, CliStrings.SHOW_DEADLOCK,
       CliStrings.SHOW_METRICS, CliStrings.SHOW_LOG, CliStrings.EXPORT_STACKTRACE,
       CliStrings.NETSTAT, CliStrings.EXPORT_LOGS, CliStrings.CHANGE_LOGLEVEL})

http://git-wip-us.apache.org/repos/asf/geode/blob/d5d7e800/geode-core/src/main/java/org/apache/geode/management/internal/cli/functions/ExportLogsFunction.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/cli/functions/ExportLogsFunction.java b/geode-core/src/main/java/org/apache/geode/management/internal/cli/functions/ExportLogsFunction.java
new file mode 100644
index 0000000..7d196e6
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/management/internal/cli/functions/ExportLogsFunction.java
@@ -0,0 +1,185 @@
+/*
+ * 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.cli.functions;
+
+import static java.util.stream.Collectors.toSet;
+
+import org.apache.geode.cache.AttributesFactory;
+import org.apache.geode.cache.DataPolicy;
+import org.apache.geode.cache.Region;
+import org.apache.geode.cache.Scope;
+import org.apache.geode.cache.execute.Function;
+import org.apache.geode.cache.execute.FunctionContext;
+import org.apache.geode.internal.InternalEntity;
+import org.apache.geode.internal.cache.GemFireCacheImpl;
+import org.apache.geode.internal.cache.InternalRegionArguments;
+import org.apache.geode.internal.lang.StringUtils;
+import org.apache.geode.internal.logging.InternalLogWriter;
+import org.apache.geode.internal.logging.LogService;
+import org.apache.geode.internal.logging.LogWriterImpl;
+import org.apache.geode.management.internal.cli.commands.MiscellaneousCommands;
+import org.apache.geode.management.internal.cli.util.LogExporter;
+import org.apache.geode.management.internal.cli.util.LogFilter;
+import org.apache.geode.management.internal.cli.util.ExportLogsCacheWriter;
+import org.apache.geode.management.internal.configuration.domain.Configuration;
+import org.apache.logging.log4j.Logger;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.Serializable;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.util.Arrays;
+import java.util.Set;
+import java.util.stream.Stream;
+
+public class ExportLogsFunction implements Function, InternalEntity {
+  public static final String EXPORT_LOGS_REGION = "__exportLogsRegion";
+  private static final Logger LOGGER = LogService.getLogger();
+  private static final long serialVersionUID = 1L;
+  private static final int BUFFER_SIZE = 1024;
+
+
+  @Override
+  public void execute(final FunctionContext context) {
+    try {
+      // TODO: change this to get cache from FunctionContext when it becomes available
+      GemFireCacheImpl cache = GemFireCacheImpl.getInstance();
+
+      String memberId = cache.getDistributedSystem().getMemberId();
+      LOGGER.info("ExportLogsFunction started for member {}", memberId);
+      Region exportLogsRegion = createOrGetExistingExportLogsRegion(false);
+
+      Args args = (Args) context.getArguments();
+      LogFilter logFilter =
+          new LogFilter(args.getPermittedLogLevels(), args.getStartTime(), args.getEndTime());
+      Path workingDir = Paths.get(System.getProperty("user.dir"));
+
+      Path exportedZipFile = new LogExporter(logFilter).export(workingDir);
+
+      LOGGER.info("Streaming zipped file: " + exportedZipFile.toString());
+      try (FileInputStream inputStream = new FileInputStream(exportedZipFile.toFile())) {
+        byte[] buffer = new byte[BUFFER_SIZE];
+
+        int bytesRead;
+        while ((bytesRead = inputStream.read(buffer)) > 0) {
+          if (bytesRead == BUFFER_SIZE) {
+            exportLogsRegion.put(memberId, buffer);
+          } else {
+            exportLogsRegion.put(memberId, Arrays.copyOfRange(buffer, 0, bytesRead));
+          }
+        }
+      }
+
+      context.getResultSender().lastResult(null);
+
+    } catch (Exception e) {
+      LOGGER.error(e);
+      context.getResultSender().sendException(e);
+    }
+  }
+
+  public static Region createOrGetExistingExportLogsRegion(boolean isLocator)
+      throws IOException, ClassNotFoundException {
+    GemFireCacheImpl cache = GemFireCacheImpl.getInstance();
+
+    Region exportLogsRegion = cache.getRegion(EXPORT_LOGS_REGION);
+    if (exportLogsRegion == null) {
+      AttributesFactory<String, Configuration> regionAttrsFactory =
+          new AttributesFactory<String, Configuration>();
+      regionAttrsFactory.setDataPolicy(DataPolicy.EMPTY);
+      regionAttrsFactory.setScope(Scope.DISTRIBUTED_ACK);
+
+      if (isLocator) {
+        regionAttrsFactory.setCacheWriter(new ExportLogsCacheWriter());
+      }
+      InternalRegionArguments internalArgs = new InternalRegionArguments();
+      internalArgs.setIsUsedForMetaRegion(true);
+      exportLogsRegion =
+          cache.createVMRegion(EXPORT_LOGS_REGION, regionAttrsFactory.create(), internalArgs);
+    }
+
+    return exportLogsRegion;
+  }
+
+  @Override
+  public boolean isHA() {
+    return false;
+  }
+
+  public static class Args implements Serializable {
+    private String startTime;
+    private String endTime;
+    private String logLevel;
+    private boolean logLevelOnly;
+
+    public Args(String startTime, String endTime, String logLevel, boolean logLevelOnly) {
+      this.startTime = startTime;
+      this.endTime = endTime;
+      this.logLevel = logLevel;
+      this.logLevelOnly = logLevelOnly;
+    }
+
+    public LocalDateTime getStartTime() {
+      return parseTime(startTime);
+    }
+
+    public LocalDateTime getEndTime() {
+      return parseTime(endTime);
+    }
+
+    public Set<String> getPermittedLogLevels() {
+      if (logLevel == null || StringUtils.isBlank(logLevel)) {
+        return LogFilter.allLogLevels();
+      }
+
+      if (logLevelOnly) {
+        return Stream.of(logLevel).collect(toSet());
+      }
+
+      // Return all log levels lower than or equal to the specified logLevel
+      return Arrays.stream(InternalLogWriter.levelNames).filter((String level) -> {
+        int logLevelCode = LogWriterImpl.levelNameToCode(level);
+        int logLevelCodeThreshold = LogWriterImpl.levelNameToCode(logLevel);
+
+        return logLevelCode <= logLevelCodeThreshold;
+      }).collect(toSet());
+    }
+
+    private static LocalDateTime parseTime(String dateString) {
+      if (dateString == null) {
+        return null;
+      }
+
+      try {
+        SimpleDateFormat df = new SimpleDateFormat(MiscellaneousCommands.FORMAT);
+        return df.parse(dateString).toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
+      } catch (ParseException e) {
+        try {
+          SimpleDateFormat df = new SimpleDateFormat(MiscellaneousCommands.ONLY_DATE_FORMAT);
+          return df.parse(dateString).toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
+        } catch (ParseException e1) {
+          return null;
+        }
+      }
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/geode/blob/d5d7e800/geode-core/src/main/java/org/apache/geode/management/internal/cli/util/ExportLogsCacheWriter.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/cli/util/ExportLogsCacheWriter.java b/geode-core/src/main/java/org/apache/geode/management/internal/cli/util/ExportLogsCacheWriter.java
new file mode 100644
index 0000000..5bff192
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/management/internal/cli/util/ExportLogsCacheWriter.java
@@ -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.geode.management.internal.cli.util;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.geode.cache.CacheWriterException;
+import org.apache.geode.cache.EntryEvent;
+import org.apache.geode.cache.util.CacheWriterAdapter;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.Serializable;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Map;
+
+public class ExportLogsCacheWriter extends CacheWriterAdapter implements Serializable {
+  private Path currentFile;
+  private BufferedOutputStream currentOutputStream;
+
+  @Override
+  public void beforeCreate(EntryEvent event) throws CacheWriterException {
+    if (currentOutputStream == null) {
+      throw new IllegalStateException("No file is open for writing.");
+    }
+
+    try {
+      Object newValue = event.getNewValue();
+      if (!(newValue instanceof byte[])) {
+        throw new IllegalArgumentException(
+            "Value must be a byte[].  Recieved " + newValue.getClass().getCanonicalName());
+      }
+      currentOutputStream.write((byte[]) newValue);
+    } catch (IOException e) {
+      throw new CacheWriterException(e);
+    }
+  }
+
+  public void startFile(String memberId) throws IOException {
+    if (currentFile != null || currentOutputStream != null) {
+      throw new IllegalStateException("Cannot open more than one file at once");
+    }
+
+    currentFile = Files.createTempDirectory(memberId).resolve(memberId + ".zip");
+    currentOutputStream = new BufferedOutputStream(new FileOutputStream(currentFile.toFile()));
+  }
+
+  public Path endFile() {
+    Path completedFile = currentFile;
+
+    IOUtils.closeQuietly(currentOutputStream);
+    currentOutputStream = null;
+    currentFile = null;
+
+    return completedFile;
+  }
+}

http://git-wip-us.apache.org/repos/asf/geode/blob/d5d7e800/geode-core/src/main/java/org/apache/geode/management/internal/cli/util/LogExporter.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/cli/util/LogExporter.java b/geode-core/src/main/java/org/apache/geode/management/internal/cli/util/LogExporter.java
new file mode 100644
index 0000000..f30d686
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/management/internal/cli/util/LogExporter.java
@@ -0,0 +1,136 @@
+/*
+ * 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.cli.util;
+
+import static java.util.stream.Collectors.toList;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.geode.cache.AttributesFactory;
+import org.apache.geode.cache.DataPolicy;
+import org.apache.geode.cache.Region;
+import org.apache.geode.cache.Scope;
+import org.apache.geode.internal.cache.GemFireCacheImpl;
+import org.apache.geode.internal.cache.InternalRegionArguments;
+import org.apache.geode.internal.logging.LogService;
+import org.apache.geode.management.internal.configuration.domain.Configuration;
+import org.apache.geode.management.internal.configuration.utils.ZipUtils;
+import org.apache.logging.log4j.Logger;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.text.ParseException;
+import java.util.List;
+import java.util.function.Predicate;
+import java.util.stream.Stream;
+
+public class LogExporter {
+  private static final Logger LOGGER = LogService.getLogger();
+
+  private final LogFilter logFilter;
+
+  public LogExporter(LogFilter logFilter) throws ParseException {
+    this.logFilter = logFilter;
+  }
+
+  public Path export(Path workingDir) throws IOException {
+    LOGGER.debug("Working directory is {}", workingDir);
+
+    Path tempDirectory = Files.createTempDirectory("exportLogs");
+
+    for (Path logFile : findLogFiles(workingDir)) {
+      Path filteredLogFile = tempDirectory.resolve(logFile.getFileName());
+
+      if (this.logFilter == null) {
+        Files.copy(logFile, filteredLogFile);
+      } else {
+        writeFilteredLogFile(logFile, filteredLogFile);
+      }
+    }
+
+    for (Path statFile : findStatFiles(workingDir)) {
+      Files.copy(statFile, tempDirectory);
+    }
+
+    Path zipFile = Files.createTempFile("logExport", ".zip");
+    ZipUtils.zipDirectory(tempDirectory, zipFile);
+    LOGGER.info("Zipped files to: " + zipFile);
+
+
+    FileUtils.deleteDirectory(tempDirectory.toFile());
+
+    return zipFile;
+  }
+
+  protected void writeFilteredLogFile(Path originalLogFile, Path filteredLogFile)
+      throws IOException {
+    this.logFilter.startNewFile();
+
+    try (BufferedReader reader = new BufferedReader(new FileReader(originalLogFile.toFile()))) {
+      try (BufferedWriter writer = new BufferedWriter(new FileWriter(filteredLogFile.toFile()))) {
+
+        String line;
+        while ((line = reader.readLine()) != null) {
+          LogFilter.LineFilterResult result = this.logFilter.acceptsLine(line);
+
+          if (result == LogFilter.LineFilterResult.REMAINDER_OF_FILE_REJECTED) {
+            break;
+          }
+
+          if (result == LogFilter.LineFilterResult.LINE_ACCEPTED) {
+            writeLine(line, writer);
+          }
+        }
+      }
+    }
+  }
+
+  private void writeLine(String line, BufferedWriter writer) {
+    try {
+      writer.write(line);
+      writer.newLine();
+    } catch (IOException e) {
+      throw new RuntimeException("Unable to write to log file", e);
+    }
+  }
+
+  protected List<Path> findLogFiles(Path workingDir) throws IOException {
+    Predicate<Path> logFileSelector = (Path file) -> file.toString().toLowerCase().endsWith(".log");
+    return findFiles(workingDir, logFileSelector);
+  }
+
+
+  protected List<Path> findStatFiles(Path workingDir) throws IOException {
+    Predicate<Path> statFileSelector =
+        (Path file) -> file.toString().toLowerCase().endsWith(".gfs");
+    return findFiles(workingDir, statFileSelector);
+  }
+
+  private List<Path> findFiles(Path workingDir, Predicate<Path> fileSelector) throws IOException {
+    Stream<Path> selectedFiles =
+        Files.list(workingDir).filter(fileSelector).filter(this.logFilter::acceptsFile);
+
+    return selectedFiles.collect(toList());
+  }
+
+
+}

http://git-wip-us.apache.org/repos/asf/geode/blob/d5d7e800/geode-core/src/main/java/org/apache/geode/management/internal/cli/util/LogFilter.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/cli/util/LogFilter.java b/geode-core/src/main/java/org/apache/geode/management/internal/cli/util/LogFilter.java
new file mode 100644
index 0000000..2436530
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/management/internal/cli/util/LogFilter.java
@@ -0,0 +1,113 @@
+/*
+ * 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.cli.util;
+
+import static java.util.stream.Collectors.toSet;
+
+import org.apache.geode.internal.logging.InternalLogWriter;
+import org.apache.geode.internal.logging.LogService;
+import org.apache.logging.log4j.Logger;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.util.Arrays;
+import java.util.Set;
+
+public class LogFilter {
+  public enum LineFilterResult {
+    LINE_ACCEPTED, LINE_REJECTED, REMAINDER_OF_FILE_REJECTED
+  }
+
+  private static final Logger LOGGER = LogService.getLogger();
+
+  private final Set<String> permittedLogLevels;
+  private final LocalDateTime startDate;
+  private final LocalDateTime endDate;
+
+  private LineFilterResult resultOfPreviousLine = LineFilterResult.LINE_ACCEPTED;
+
+  public LogFilter(Set<String> permittedLogLevels, LocalDateTime startDate, LocalDateTime endDate) {
+    this.permittedLogLevels = (permittedLogLevels == null || permittedLogLevels.isEmpty())
+        ? allLogLevels() : permittedLogLevels;
+    this.startDate = startDate;
+    this.endDate = endDate;
+  }
+
+  public void startNewFile() {
+    this.resultOfPreviousLine = LineFilterResult.LINE_ACCEPTED;
+  }
+
+  public LineFilterResult acceptsLine(String logLine) {
+    LogLevelExtractor.Result result = LogLevelExtractor.extract(logLine);
+
+    return acceptsLogEntry(result);
+  }
+
+  protected LineFilterResult acceptsLogEntry(LogLevelExtractor.Result result) {
+    if (result == null) {
+      return resultOfPreviousLine;
+    }
+
+    return acceptsLogEntry(result.getLogLevel(), result.getLogTimestamp());
+  }
+
+  protected LineFilterResult acceptsLogEntry(String logLevel, LocalDateTime logTimestamp) {
+    if (logTimestamp == null || logLevel == null) {
+      throw new IllegalArgumentException();
+    }
+
+    LineFilterResult result;
+
+    if (endDate != null && logTimestamp.isAfter(endDate)) {
+      result = LineFilterResult.REMAINDER_OF_FILE_REJECTED;
+    } else if (startDate != null && logTimestamp.isBefore(startDate)) {
+      result = LineFilterResult.LINE_REJECTED;
+    } else {
+      result = permittedLogLevels.contains(logLevel) ? LineFilterResult.LINE_ACCEPTED
+          : LineFilterResult.LINE_REJECTED;
+    }
+
+    resultOfPreviousLine = result;
+
+    return result;
+  }
+
+  public boolean acceptsFile(Path file) {
+    if (startDate == null) {
+      return true;
+    }
+    try {
+      return (getEndTimeOf(file).isAfter(startDate));
+    } catch (IOException e) {
+      LOGGER.error("Unable to determine lastModified time", e);
+      return true;
+    }
+  }
+
+  private static LocalDateTime getEndTimeOf(Path file) throws IOException {
+    long lastModifiedMillis = file.toFile().lastModified();
+    return Instant.ofEpochMilli(lastModifiedMillis).atZone(ZoneId.systemDefault())
+        .toLocalDateTime();
+  }
+
+  public static Set<String> allLogLevels() {
+    return Arrays.stream(InternalLogWriter.levelNames).collect(toSet());
+  }
+}

http://git-wip-us.apache.org/repos/asf/geode/blob/d5d7e800/geode-core/src/main/java/org/apache/geode/management/internal/cli/util/LogLevelExtractor.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/cli/util/LogLevelExtractor.java b/geode-core/src/main/java/org/apache/geode/management/internal/cli/util/LogLevelExtractor.java
new file mode 100644
index 0000000..6c15d15
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/management/internal/cli/util/LogLevelExtractor.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.cli.util;
+
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class LogLevelExtractor {
+  private static Pattern LOG_PATTERN =
+      Pattern.compile("^\\[(\\S*)\\s+([\\d\\/]+)\\s+([\\d:\\.]+)\\s+(\\S+)");
+
+  private static DateTimeFormatter LOG_TIMESTAMP_FORMATTER =
+      DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss.SSS zzz");
+  private static final String SPACE = " ";
+
+  public static Result extract(String logLine) {
+    Matcher m = LOG_PATTERN.matcher(logLine);
+    if (!m.find()) {
+      return null;
+    }
+
+    String logLevel = m.group(1);
+    String logTimestamp = m.group(2) + SPACE + m.group(3) + SPACE + m.group(4);
+
+    LocalDateTime timestamp = LocalDateTime.parse(logTimestamp, LOG_TIMESTAMP_FORMATTER);
+
+    return new Result(logLevel, timestamp);
+  }
+
+  public static class Result {
+    private String logLevel;
+    private LocalDateTime logTimestamp;
+
+    public Result(String logLevel, LocalDateTime logTimestamp) {
+      this.logLevel = logLevel;
+      this.logTimestamp = logTimestamp;
+    }
+
+    public String getLogLevel() {
+      return logLevel;
+    }
+
+    public LocalDateTime getLogTimestamp() {
+      return logTimestamp;
+    }
+
+  }
+}
+

http://git-wip-us.apache.org/repos/asf/geode/blob/d5d7e800/geode-core/src/main/java/org/apache/geode/management/internal/configuration/utils/ZipUtils.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/configuration/utils/ZipUtils.java b/geode-core/src/main/java/org/apache/geode/management/internal/configuration/utils/ZipUtils.java
index 81161d6..6c546a7 100644
--- a/geode-core/src/main/java/org/apache/geode/management/internal/configuration/utils/ZipUtils.java
+++ b/geode-core/src/main/java/org/apache/geode/management/internal/configuration/utils/ZipUtils.java
@@ -41,8 +41,7 @@ import org.apache.commons.io.IOUtils;
 public class ZipUtils {
 
   public static void zipDirectory(Path sourceDirectory, Path targetFile) throws IOException {
-    Path p = Files.createFile(targetFile);
-    try (ZipOutputStream zs = new ZipOutputStream(Files.newOutputStream(p))) {
+    try (ZipOutputStream zs = new ZipOutputStream(Files.newOutputStream(targetFile))) {
       Files.walk(sourceDirectory).filter(path -> !Files.isDirectory(path)).forEach(path -> {
         ZipEntry zipEntry = new ZipEntry(sourceDirectory.relativize(path).toString());
         try {

http://git-wip-us.apache.org/repos/asf/geode/blob/d5d7e800/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/ExportLogsDUnit.java
----------------------------------------------------------------------
diff --git a/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/ExportLogsDUnit.java b/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/ExportLogsDUnit.java
new file mode 100644
index 0000000..5258891
--- /dev/null
+++ b/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/ExportLogsDUnit.java
@@ -0,0 +1,122 @@
+/*
+ * 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.cli.commands;
+
+import static java.util.stream.Collectors.toList;
+import static java.util.stream.Collectors.toSet;
+import static org.assertj.core.api.Assertions.assertThat;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.geode.cache.AttributesFactory;
+import org.apache.geode.cache.DataPolicy;
+import org.apache.geode.cache.Region;
+import org.apache.geode.cache.RegionShortcut;
+import org.apache.geode.cache.Scope;
+import org.apache.geode.internal.cache.GemFireCacheImpl;
+import org.apache.geode.internal.cache.InternalRegionArguments;
+import org.apache.geode.internal.logging.LogService;
+import org.apache.geode.management.internal.cli.functions.ExportLogsFunction;
+import org.apache.geode.management.internal.cli.result.CommandResult;
+import org.apache.geode.management.internal.configuration.EventTestCacheWriter;
+import org.apache.geode.management.internal.configuration.domain.Configuration;
+import org.apache.geode.management.internal.configuration.utils.ZipUtils;
+import org.apache.geode.test.dunit.rules.GfshShellConnectionRule;
+import org.apache.geode.test.dunit.rules.Locator;
+import org.apache.geode.test.dunit.rules.LocatorServerStartupRule;
+import org.apache.geode.test.dunit.rules.Server;
+import org.apache.logging.log4j.core.Appender;
+import org.junit.Rule;
+import org.junit.Test;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.stream.Stream;
+
+
+public class ExportLogsDUnit {
+  @Rule
+  public LocatorServerStartupRule lsRule = new LocatorServerStartupRule();
+
+  @Rule
+  public GfshShellConnectionRule gfshConnector = new GfshShellConnectionRule();
+
+  private Locator locator;
+
+  @Test
+  public void testExport() throws Exception {
+    locator = lsRule.startLocatorVM(0);
+
+    Server server = lsRule.startServerVM(1, locator.getPort());
+    Server server2 = lsRule.startServerVM(2, locator.getPort());
+
+    gfshConnector.connectAndVerify(locator);
+
+    CommandResult result = gfshConnector.executeAndVerifyCommand(
+        "export logs  --dir=" + lsRule.getTempFolder().getRoot().getCanonicalPath());
+
+    File locatorWorkingDir = locator.getWorkingDir();
+    List<File> zipFilesInDir = Stream.of(locatorWorkingDir.listFiles())
+        .filter(f -> f.getName().endsWith(".zip")).collect(toList());
+
+    assertThat(zipFilesInDir).hasSize(1);
+
+    File unzippedLogFileDir = lsRule.getTempFolder().newFolder("unzippedLogs");
+    ZipUtils.unzip(zipFilesInDir.get(0).getCanonicalPath(), unzippedLogFileDir.getCanonicalPath());
+
+    Set<File> actualDirs =
+        Stream.of(unzippedLogFileDir.listFiles()).filter(File::isDirectory).collect(toSet());
+
+    assertThat(actualDirs).hasSize(2);
+
+    Set<String> expectedDirNames = Stream.of(server.getName(), server2.getName()).collect(toSet());
+    Set<String> actualDirNames = actualDirs.stream().map(File::getName).collect(toSet());
+
+    assertThat(actualDirNames).isEqualTo(expectedDirNames);
+
+    System.out.println("Unzipped artifacts:");
+    for (File dir : actualDirs) {
+      Set<String> fileNamesInDir = Stream.of(dir.listFiles()).map(File::getName).collect(toSet());
+
+      System.out.println(dir.getCanonicalPath() + " : " + fileNamesInDir);
+      assertThat(fileNamesInDir).contains(dir.getName() + ".log");
+      assertThat(fileNamesInDir).hasSize(1);
+      // TODO: Verify contents of files. (Write tests for logs containing multiple log levels,
+      // where some lines get through a filter and some do not
+    }
+
+    // Ensure export logs region does not accumulate data
+    server.invoke(() -> {
+      Region exportLogsRegion = ExportLogsFunction.createOrGetExistingExportLogsRegion(false);
+      assertThat(exportLogsRegion.size()).isEqualTo(0);
+    });
+    server2.invoke(() -> {
+      Region exportLogsRegion = ExportLogsFunction.createOrGetExistingExportLogsRegion(false);
+      assertThat(exportLogsRegion.size()).isEqualTo(0);
+    });
+    locator.invoke(() -> {
+      Region exportLogsRegion = ExportLogsFunction.createOrGetExistingExportLogsRegion(true);
+      assertThat(exportLogsRegion.size()).isEqualTo(0);
+    });
+  }
+
+
+}

http://git-wip-us.apache.org/repos/asf/geode/blob/d5d7e800/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/MiscellaneousCommandsExportLogsPart1DUnitTest.java
----------------------------------------------------------------------
diff --git a/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/MiscellaneousCommandsExportLogsPart1DUnitTest.java b/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/MiscellaneousCommandsExportLogsPart1DUnitTest.java
deleted file mode 100644
index 2fc8e84..0000000
--- a/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/MiscellaneousCommandsExportLogsPart1DUnitTest.java
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * 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.cli.commands;
-
-import static org.apache.geode.test.dunit.Assert.*;
-import static org.apache.geode.test.dunit.LogWriterUtils.*;
-
-import java.io.File;
-import java.io.IOException;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-
-import org.apache.geode.cache.Cache;
-import org.apache.geode.cache.Region;
-import org.apache.geode.cache.RegionFactory;
-import org.apache.geode.cache.RegionShortcut;
-import org.apache.geode.internal.FileUtil;
-import org.apache.geode.internal.logging.LogWriterImpl;
-import org.apache.geode.management.cli.Result;
-import org.apache.geode.management.internal.cli.result.CommandResult;
-import org.apache.geode.test.dunit.Host;
-import org.apache.geode.test.dunit.SerializableRunnable;
-import org.apache.geode.test.dunit.VM;
-import org.apache.geode.test.junit.categories.DistributedTest;
-import org.apache.geode.test.junit.categories.FlakyTest;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-
-/**
- * Dunit class for testing gemfire function commands : export logs
- */
-@Category(DistributedTest.class)
-public class MiscellaneousCommandsExportLogsPart1DUnitTest extends CliCommandTestBase {
-
-  private static final long serialVersionUID = 1L;
-
-  void setupForExportLogs() {
-    final VM vm1 = Host.getHost(0).getVM(1);
-    setUpJmxManagerOnVm0ThenConnect(null);
-
-    vm1.invoke(new SerializableRunnable() {
-      public void run() {
-        // no need to close cache as it will be closed as part of teardown2
-        Cache cache = getCache();
-
-        RegionFactory<Integer, Integer> dataRegionFactory =
-            cache.createRegionFactory(RegionShortcut.PARTITION);
-        Region region = dataRegionFactory.create("testRegion");
-        for (int i = 0; i < 5; i++) {
-          region.put("key" + (i + 200), "value" + (i + 200));
-        }
-      }
-    });
-  }
-
-  String getCurrentTimeString() {
-    SimpleDateFormat sf = new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss_SSS_z");
-    Date startDate = new Date(System.currentTimeMillis());
-    String formattedStartDate = sf.format(startDate);
-    return ("_" + formattedStartDate);
-  }
-
-  @Test
-  public void testExportLogs() throws IOException {
-    Date startDate = new Date(System.currentTimeMillis() - 2 * 60 * 1000);
-    SimpleDateFormat sf = new SimpleDateFormat("yyyy/MM/dd");
-    String start = sf.format(startDate);
-
-    Date enddate = new Date(System.currentTimeMillis() + 2 * 60 * 60 * 1000);
-    String end = sf.format(enddate);
-    String dir = getCurrentTimeString();
-
-    setupForExportLogs();
-    String logLevel = LogWriterImpl.levelToString(LogWriterImpl.INFO_LEVEL);
-
-    MiscellaneousCommands misc = new MiscellaneousCommands();
-    getCache();
-
-    Result cmdResult = misc.exportLogsPreprocessing("./testExportLogs" + dir, null, null, logLevel,
-        false, false, start, end, 1);
-
-    getLogWriter().info("testExportLogs command result =" + cmdResult);
-
-    if (cmdResult != null) {
-      String cmdStringRsult = commandResultToString((CommandResult) cmdResult);
-      getLogWriter().info("testExportLogs cmdStringRsult=" + cmdStringRsult);
-      assertEquals(Result.Status.OK, cmdResult.getStatus());
-    } else {
-      fail("testExportLogs failed as did not get CommandResult");
-    }
-    FileUtil.delete(new File("./testExportLogs" + dir));
-  }
-
-  @Category(FlakyTest.class) // GEODE-1477 (http)
-  @Test
-  public void testExportLogsForMerge() throws IOException {
-    setupForExportLogs();
-    Date startDate = new Date(System.currentTimeMillis() - 2 * 60 * 1000);
-    SimpleDateFormat sf = new SimpleDateFormat("yyyy/MM/dd");
-    String start = sf.format(startDate);
-
-    Date enddate = new Date(System.currentTimeMillis() + 2 * 60 * 60 * 1000);
-    String end = sf.format(enddate);
-    String dir = getCurrentTimeString();
-
-    String logLevel = LogWriterImpl.levelToString(LogWriterImpl.INFO_LEVEL);
-
-    MiscellaneousCommands misc = new MiscellaneousCommands();
-    getCache();
-
-    Result cmdResult = misc.exportLogsPreprocessing("./testExportLogsForMerge" + dir, null, null,
-        logLevel, false, true, start, end, 1);
-    getLogWriter().info("testExportLogsForMerge command=" + cmdResult);
-
-    if (cmdResult != null) {
-      String cmdStringRsult = commandResultToString((CommandResult) cmdResult);
-      getLogWriter().info("testExportLogsForMerge cmdStringRsult=" + cmdStringRsult);
-
-      assertEquals(Result.Status.OK, cmdResult.getStatus());
-    } else {
-      fail("testExportLogsForMerge failed as did not get CommandResult");
-    }
-    FileUtil.delete(new File("./testExportLogsForMerge" + dir));
-  }
-}

http://git-wip-us.apache.org/repos/asf/geode/blob/d5d7e800/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/MiscellaneousCommandsExportLogsPart2DUnitTest.java
----------------------------------------------------------------------
diff --git a/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/MiscellaneousCommandsExportLogsPart2DUnitTest.java b/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/MiscellaneousCommandsExportLogsPart2DUnitTest.java
deleted file mode 100644
index d1f44d1..0000000
--- a/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/MiscellaneousCommandsExportLogsPart2DUnitTest.java
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- * 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.cli.commands;
-
-import static org.apache.geode.test.dunit.Assert.*;
-import static org.apache.geode.test.dunit.LogWriterUtils.*;
-
-import java.io.File;
-import java.io.IOException;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-
-import org.apache.geode.cache.Cache;
-import org.apache.geode.cache.Region;
-import org.apache.geode.cache.RegionFactory;
-import org.apache.geode.cache.RegionShortcut;
-import org.apache.geode.internal.FileUtil;
-import org.apache.geode.internal.logging.LogWriterImpl;
-import org.apache.geode.management.cli.Result;
-import org.apache.geode.management.internal.cli.result.CommandResult;
-import org.apache.geode.test.dunit.Host;
-import org.apache.geode.test.dunit.SerializableRunnable;
-import org.apache.geode.test.dunit.VM;
-import org.apache.geode.test.junit.categories.DistributedTest;
-
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-
-/**
- * Dunit class for testing gemfire function commands : export logs
- */
-@Category(DistributedTest.class)
-public class MiscellaneousCommandsExportLogsPart2DUnitTest extends CliCommandTestBase {
-
-  private static final long serialVersionUID = 1L;
-
-  void setupForExportLogs() {
-    final VM vm1 = Host.getHost(0).getVM(1);
-    setUpJmxManagerOnVm0ThenConnect(null);
-
-    vm1.invoke(new SerializableRunnable() {
-      public void run() {
-        // no need to close cache as it will be closed as part of teardown2
-        Cache cache = getCache();
-
-        RegionFactory<Integer, Integer> dataRegionFactory =
-            cache.createRegionFactory(RegionShortcut.PARTITION);
-        Region region = dataRegionFactory.create("testRegion");
-        for (int i = 0; i < 5; i++) {
-          region.put("key" + (i + 200), "value" + (i + 200));
-        }
-      }
-    });
-  }
-
-  String getCurrentTimeString() {
-    SimpleDateFormat sf = new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss_SSS_z");
-    Date startDate = new Date(System.currentTimeMillis());
-    String formattedStartDate = sf.format(startDate);
-    return ("_" + formattedStartDate);
-  }
-
-  @Test
-  public void testExportLogsForLogLevel() throws IOException {
-    setupForExportLogs();
-
-    Date startDate = new Date(System.currentTimeMillis() - 60 * 1000);
-    SimpleDateFormat sf = new SimpleDateFormat("yyyy/MM/dd");
-    String start = sf.format(startDate);
-
-    Date enddate = new Date(System.currentTimeMillis() + 60 * 1000);
-    String end = sf.format(enddate);
-    String dir = getCurrentTimeString();
-
-    String logLevel = LogWriterImpl.levelToString(LogWriterImpl.CONFIG_LEVEL);
-
-    MiscellaneousCommands misc = new MiscellaneousCommands();
-    getCache();
-
-    Result cmdResult = misc.exportLogsPreprocessing("./testExportLogsForLogLevel" + dir, null, null,
-        logLevel, false, false, start, end, 1);
-
-    getLogWriter().info("testExportLogsForLogLevel command=" + cmdResult);
-
-    if (cmdResult != null) {
-      String cmdStringRsult = commandResultToString((CommandResult) cmdResult);
-      getLogWriter().info("testExportLogsForLogLevel cmdStringRsult=" + cmdStringRsult);
-      assertEquals(Result.Status.OK, cmdResult.getStatus());
-    } else {
-      fail("testExportLogsForLogLevel failed as did not get CommandResult");
-    }
-    FileUtil.delete(new File("testExportLogsForLogLevel" + dir));
-  }
-
-  @Test
-  public void testExportLogsForLogLevelWithUPTOLOGLEVEL() throws IOException {
-    setupForExportLogs();
-
-    Date startDate = new Date(System.currentTimeMillis() - 2 * 60 * 1000);
-    SimpleDateFormat sf = new SimpleDateFormat("yyyy/MM/dd");
-    String start = sf.format(startDate);
-
-    Date enddate = new Date(System.currentTimeMillis() + 2 * 60 * 60 * 1000);
-    String end = sf.format(enddate);
-    String dir = getCurrentTimeString();
-
-    String logLevel = LogWriterImpl.levelToString(LogWriterImpl.SEVERE_LEVEL);
-
-    MiscellaneousCommands misc = new MiscellaneousCommands();
-    getCache();
-
-    Result cmdResult =
-        misc.exportLogsPreprocessing("./testExportLogsForLogLevelWithUPTOLOGLEVEL" + dir, null,
-            null, logLevel, true, false, start, end, 1);
-
-    getLogWriter().info("testExportLogsForLogLevelWithUPTOLOGLEVEL command=" + cmdResult);
-
-    if (cmdResult != null) {
-      String cmdStringRsult = commandResultToString((CommandResult) cmdResult);
-      getLogWriter()
-          .info("testExportLogsForLogLevelWithUPTOLOGLEVEL cmdStringRsult=" + cmdStringRsult);
-
-      assertEquals(Result.Status.OK, cmdResult.getStatus());
-    } else {
-      fail("testExportLogsForLogLevelWithUPTOLOGLEVEL failed as did not get CommandResult");
-    }
-    FileUtil.delete(new File("testExportLogsForLogLevelWithUPTOLOGLEVEL" + dir));
-  }
-}

http://git-wip-us.apache.org/repos/asf/geode/blob/d5d7e800/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/MiscellaneousCommandsExportLogsPart3DUnitTest.java
----------------------------------------------------------------------
diff --git a/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/MiscellaneousCommandsExportLogsPart3DUnitTest.java b/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/MiscellaneousCommandsExportLogsPart3DUnitTest.java
deleted file mode 100644
index b6a4a5b..0000000
--- a/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/MiscellaneousCommandsExportLogsPart3DUnitTest.java
+++ /dev/null
@@ -1,155 +0,0 @@
-/*
- * 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.cli.commands;
-
-import org.apache.geode.cache.Cache;
-import org.apache.geode.cache.Region;
-import org.apache.geode.cache.RegionFactory;
-import org.apache.geode.cache.RegionShortcut;
-import org.apache.geode.internal.FileUtil;
-import org.apache.geode.internal.logging.LogWriterImpl;
-import org.apache.geode.management.cli.Result;
-import org.apache.geode.management.internal.cli.result.CommandResult;
-import org.apache.geode.test.dunit.Host;
-import org.apache.geode.test.dunit.SerializableRunnable;
-import org.apache.geode.test.dunit.VM;
-import org.apache.geode.test.junit.categories.DistributedTest;
-import org.apache.geode.test.junit.categories.FlakyTest;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-
-import java.io.File;
-import java.io.IOException;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-import java.util.Properties;
-
-import static org.apache.geode.test.dunit.Assert.assertEquals;
-import static org.apache.geode.test.dunit.Assert.fail;
-import static org.apache.geode.test.dunit.LogWriterUtils.getLogWriter;
-import static org.apache.geode.distributed.ConfigurationProperties.*;
-
-/**
- * Dunit class for testing gemfire function commands : export logs
- */
-@Category(DistributedTest.class)
-public class MiscellaneousCommandsExportLogsPart3DUnitTest extends CliCommandTestBase {
-
-  private static final long serialVersionUID = 1L;
-
-  public String getMemberId() {
-    Cache cache = getCache();
-    return cache.getDistributedSystem().getDistributedMember().getId();
-  }
-
-  void setupForExportLogs() {
-    final VM vm1 = Host.getHost(0).getVM(1);
-    setUpJmxManagerOnVm0ThenConnect(null);
-
-    vm1.invoke(new SerializableRunnable() {
-      public void run() {
-        // no need to close cache as it will be closed as part of teardown2
-        Cache cache = getCache();
-
-        RegionFactory<Integer, Integer> dataRegionFactory =
-            cache.createRegionFactory(RegionShortcut.PARTITION);
-        Region region = dataRegionFactory.create("testRegion");
-        for (int i = 0; i < 5; i++) {
-          region.put("key" + (i + 200), "value" + (i + 200));
-        }
-      }
-    });
-  }
-
-  String getCurrentTimeString() {
-    SimpleDateFormat sf = new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss_SSS_z");
-    Date startDate = new Date(System.currentTimeMillis());
-    String formattedStartDate = sf.format(startDate);
-    return ("_" + formattedStartDate);
-  }
-
-  @Category(FlakyTest.class) // GEODE-672: random ports, java.rmi.server.ExportException: Port
-                             // already in use, HeadlessGfsh, disk IO
-  @Test
-  public void testExportLogsForGroup() throws IOException {
-    Properties localProps = new Properties();
-    localProps.setProperty(NAME, "Manager");
-    localProps.setProperty(GROUPS, "Group1");
-    setUpJmxManagerOnVm0ThenConnect(localProps);
-    String dir = getCurrentTimeString();
-
-    Date startDate = new Date(System.currentTimeMillis() - 2 * 60 * 1000);
-    SimpleDateFormat sf = new SimpleDateFormat("yyyy/MM/dd");
-    String start = sf.format(startDate);
-
-    Date enddate = new Date(System.currentTimeMillis() + 2 * 60 * 60 * 1000);
-    String end = sf.format(enddate);
-
-    String logLevel = LogWriterImpl.levelToString(LogWriterImpl.INFO_LEVEL);
-
-    MiscellaneousCommands misc = new MiscellaneousCommands();
-    getCache();
-    String[] groups = new String[1];
-    groups[0] = "Group1";
-
-    Result cmdResult = misc.exportLogsPreprocessing("./testExportLogsForGroup" + dir, groups, null,
-        logLevel, false, false, start, end, 1);
-
-    getLogWriter().info("testExportLogsForGroup command result =" + cmdResult);
-    if (cmdResult != null) {
-      String cmdStringRsult = commandResultToString((CommandResult) cmdResult);
-      getLogWriter().info("testExportLogsForGroup cmdStringRsult=" + cmdStringRsult);
-      assertEquals(Result.Status.OK, cmdResult.getStatus());
-    } else {
-      fail("testExportLogsForGroup failed as did not get CommandResult");
-    }
-    FileUtil.delete(new File("testExportLogsForGroup" + dir));
-  }
-
-  @Test
-  public void testExportLogsForMember() throws IOException {
-    setUpJmxManagerOnVm0ThenConnect(null);
-
-    Date startDate = new Date(System.currentTimeMillis() - 2 * 60 * 1000);
-    SimpleDateFormat sf = new SimpleDateFormat("yyyy/MM/dd");
-    String start = sf.format(startDate);
-
-    Date enddate = new Date(System.currentTimeMillis() + 2 * 60 * 60 * 1000);
-    String end = sf.format(enddate);
-
-    final VM vm1 = Host.getHost(0).getVM(1);
-    final String vm1MemberId = (String) vm1.invoke(() -> getMemberId());
-    String dir = getCurrentTimeString();
-
-    String logLevel = LogWriterImpl.levelToString(LogWriterImpl.INFO_LEVEL);
-
-    MiscellaneousCommands misc = new MiscellaneousCommands();
-    getCache();
-
-    Result cmdResult = misc.exportLogsPreprocessing("./testExportLogsForMember" + dir, null,
-        vm1MemberId, logLevel, false, false, start, end, 1);
-
-    getLogWriter().info("testExportLogsForMember command result =" + cmdResult);
-
-    if (cmdResult != null) {
-      String cmdStringRsult = commandResultToString((CommandResult) cmdResult);
-      getLogWriter().info("testExportLogsForMember cmdStringRsult=" + cmdStringRsult);
-      assertEquals(Result.Status.OK, cmdResult.getStatus());
-    } else {
-      fail("testExportLogsForMember failed as did not get CommandResult");
-    }
-    FileUtil.delete(new File("testExportLogsForMember" + dir));
-  }
-}

http://git-wip-us.apache.org/repos/asf/geode/blob/d5d7e800/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/MiscellaneousCommandsExportLogsPart4DUnitTest.java
----------------------------------------------------------------------
diff --git a/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/MiscellaneousCommandsExportLogsPart4DUnitTest.java b/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/MiscellaneousCommandsExportLogsPart4DUnitTest.java
deleted file mode 100644
index b712785..0000000
--- a/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/MiscellaneousCommandsExportLogsPart4DUnitTest.java
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * 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.cli.commands;
-
-import static org.apache.geode.test.dunit.Assert.*;
-import static org.apache.geode.test.dunit.LogWriterUtils.*;
-
-import java.io.File;
-import java.io.IOException;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-
-import org.apache.geode.cache.Cache;
-import org.apache.geode.cache.Region;
-import org.apache.geode.cache.RegionFactory;
-import org.apache.geode.cache.RegionShortcut;
-import org.apache.geode.internal.FileUtil;
-import org.apache.geode.internal.logging.LogWriterImpl;
-import org.apache.geode.management.cli.Result;
-import org.apache.geode.management.internal.cli.result.CommandResult;
-import org.apache.geode.test.dunit.Host;
-import org.apache.geode.test.dunit.SerializableRunnable;
-import org.apache.geode.test.dunit.VM;
-import org.apache.geode.test.junit.categories.DistributedTest;
-import org.apache.geode.test.junit.categories.FlakyTest;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-
-/**
- * Dunit class for testing gemfire function commands : export logs
- */
-@Category(DistributedTest.class)
-public class MiscellaneousCommandsExportLogsPart4DUnitTest extends CliCommandTestBase {
-
-  private static final long serialVersionUID = 1L;
-
-  void setupForExportLogs() {
-    final VM vm1 = Host.getHost(0).getVM(1);
-    setUpJmxManagerOnVm0ThenConnect(null);
-
-    vm1.invoke(new SerializableRunnable() {
-      public void run() {
-        // no need to close cache as it will be closed as part of teardown2
-        Cache cache = getCache();
-
-        RegionFactory<Integer, Integer> dataRegionFactory =
-            cache.createRegionFactory(RegionShortcut.PARTITION);
-        Region region = dataRegionFactory.create("testRegion");
-        for (int i = 0; i < 5; i++) {
-          region.put("key" + (i + 200), "value" + (i + 200));
-        }
-      }
-    });
-  }
-
-  String getCurrentTimeString() {
-    SimpleDateFormat sf = new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss_SSS_z");
-    Date startDate = new Date(System.currentTimeMillis());
-    String formattedStartDate = sf.format(startDate);
-    return ("_" + formattedStartDate);
-  }
-
-  @Test
-  public void testExportLogsForTimeRange1() throws IOException {
-    setupForExportLogs();
-    Date startDate = new Date(System.currentTimeMillis() - 1 * 60 * 1000);
-    SimpleDateFormat sf = new SimpleDateFormat("yyyy/MM/dd");
-    String start = sf.format(startDate);
-
-    Date enddate = new Date(System.currentTimeMillis() + 1 * 60 * 60 * 1000);
-    String end = sf.format(enddate);
-    String dir = getCurrentTimeString();
-
-    String logLevel = LogWriterImpl.levelToString(LogWriterImpl.INFO_LEVEL);
-
-    MiscellaneousCommands misc = new MiscellaneousCommands();
-    getCache();
-
-    Result cmdResult = misc.exportLogsPreprocessing("./testExportLogsForTimeRange1" + dir, null,
-        null, logLevel, false, false, start, end, 1);
-
-    getLogWriter().info("testExportLogsForTimeRange1 command result =" + cmdResult);
-
-    if (cmdResult != null) {
-      String cmdStringRsult = commandResultToString((CommandResult) cmdResult);
-      getLogWriter().info("testExportLogsForTimeRange1 cmdStringRsult=" + cmdStringRsult);
-      assertEquals(Result.Status.OK, cmdResult.getStatus());
-    } else {
-      fail("testExportLogsForTimeRange1 failed as did not get CommandResult");
-    }
-    FileUtil.delete(new File("testExportLogsForTimeRange1" + dir));
-  }
-
-  @Category(FlakyTest.class) // GEODE-1500 (http)
-  @Test
-  public void testExportLogsForTimeRangeForOnlyStartTime() throws IOException {
-    setupForExportLogs();
-    Date date = new Date();
-    date.setTime(System.currentTimeMillis() - 30 * 1000);
-    SimpleDateFormat sf = new SimpleDateFormat("yyyy/MM/dd/HH:mm");
-    String s = sf.format(date);
-    String dir = getCurrentTimeString();
-
-    String logLevel = LogWriterImpl.levelToString(LogWriterImpl.INFO_LEVEL);
-
-    MiscellaneousCommands misc = new MiscellaneousCommands();
-    getCache();
-
-    Result cmdResult =
-        misc.exportLogsPreprocessing("./testExportLogsForTimeRangeForOnlyStartTime" + dir, null,
-            null, logLevel, false, false, s, null, 1);
-
-    getLogWriter().info("testExportLogsForTimeRangeForOnlyStartTime command result =" + cmdResult);
-
-    if (cmdResult != null) {
-      String cmdStringRsult = commandResultToString((CommandResult) cmdResult);
-      getLogWriter()
-          .info("testExportLogsForTimeRangeForOnlyStartTime cmdStringRsult=" + cmdStringRsult);
-      assertEquals(Result.Status.OK, cmdResult.getStatus());
-    } else {
-      fail("testExportLogsForTimeRangeForOnlyStartTime failed as did not get CommandResult");
-    }
-    FileUtil.delete(new File("testExportLogsForTimeRangeForOnlyStartTime" + dir));
-  }
-}

http://git-wip-us.apache.org/repos/asf/geode/blob/d5d7e800/geode-core/src/test/java/org/apache/geode/management/internal/cli/functions/ExportLogsFunctionTest.java
----------------------------------------------------------------------
diff --git a/geode-core/src/test/java/org/apache/geode/management/internal/cli/functions/ExportLogsFunctionTest.java b/geode-core/src/test/java/org/apache/geode/management/internal/cli/functions/ExportLogsFunctionTest.java
new file mode 100644
index 0000000..023c0a2
--- /dev/null
+++ b/geode-core/src/test/java/org/apache/geode/management/internal/cli/functions/ExportLogsFunctionTest.java
@@ -0,0 +1,118 @@
+/*
+ * 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.cli.functions;
+
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.verify;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.geode.cache.Cache;
+import org.apache.geode.cache.execute.FunctionContext;
+import org.apache.geode.cache.execute.ResultSender;
+import org.apache.geode.internal.cache.GemFireCacheImpl;
+import org.apache.geode.internal.cache.execute.FunctionContextImpl;
+import org.apache.geode.test.dunit.rules.ServerStarterRule;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.contrib.java.lang.system.RestoreSystemProperties;
+import org.junit.rules.TemporaryFolder;
+
+import java.io.File;
+import java.util.Properties;
+
+public class ExportLogsFunctionTest {
+
+  @Rule
+  public TemporaryFolder temporaryFolder = new TemporaryFolder();
+
+  @Rule
+  public RestoreSystemProperties restoreSystemProperties = new RestoreSystemProperties();
+
+  private ServerStarterRule serverStarterRule;
+  private File serverWorkingDir;
+
+  @Before
+  public void setup() throws Exception {
+    serverWorkingDir = temporaryFolder.newFolder("serverWorkingDir");
+    System.setProperty("user.dir", serverWorkingDir.getCanonicalPath());
+
+    serverStarterRule = new ServerStarterRule(new Properties());
+    serverStarterRule.startServer();
+  }
+
+  @After
+  public void teardown() {
+    serverStarterRule.after();
+  }
+
+  @Test
+  public void execute() throws Throwable {
+    File logFile1 = new File(serverWorkingDir, "server1.log");
+    FileUtils.writeStringToFile(logFile1, "some log for server1 \n some other log line");
+    File logFile2 = new File(serverWorkingDir, "server2.log");
+    FileUtils.writeStringToFile(logFile2, "some log for server2 \n some other log line");
+
+    File notALogFile = new File(serverWorkingDir, "foo.txt");
+    FileUtils.writeStringToFile(notALogFile, "some text");
+
+    ExportLogsFunction.Args args = new ExportLogsFunction.Args(null, null, "info", false);
+
+    CapturingResultSender resultSender = new CapturingResultSender();
+    FunctionContext context = new FunctionContextImpl("functionId", args, resultSender);
+
+    new ExportLogsFunction().execute(context);
+
+    if (resultSender.getThrowable() != null) {
+      throw resultSender.getThrowable();
+    }
+  }
+
+  @Test
+  public void createOrGetExistingExportLogsRegionDoesNotBlowUp() throws Exception {
+    ExportLogsFunction.createOrGetExistingExportLogsRegion(false);
+
+    Cache cache = GemFireCacheImpl.getInstance();
+    assertThat(cache.getRegion(ExportLogsFunction.EXPORT_LOGS_REGION)).isNotNull();
+  }
+
+  private static class CapturingResultSender implements ResultSender {
+    private Throwable t;
+
+    public Throwable getThrowable() {
+      return t;
+    }
+
+    @Override
+    public void sendResult(Object oneResult) {
+
+    }
+
+    @Override
+    public void lastResult(Object lastResult) {
+
+    }
+
+    @Override
+    public void sendException(Throwable t) {
+      this.t = t;
+    }
+  }
+}