You are viewing a plain text version of this content. The canonical link for it is here.
Posted to common-commits@hadoop.apache.org by ju...@apache.org on 2015/04/14 02:23:28 UTC

hadoop git commit: YARN-3347. Improve YARN log command to get AMContainer logs as well as running containers logs. Contributed by Xuan Gong. (cherry picked from commit a77d628339afaf2f5a085c73fd81a805b18348c9)

Repository: hadoop
Updated Branches:
  refs/heads/branch-2 1afea8a15 -> a64b8915d


YARN-3347. Improve YARN log command to get AMContainer logs as well as running containers logs. Contributed by Xuan Gong.
(cherry picked from commit a77d628339afaf2f5a085c73fd81a805b18348c9)


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

Branch: refs/heads/branch-2
Commit: a64b8915de47d034150bdb054da9dd27dc9405c9
Parents: 1afea8a
Author: Junping Du <ju...@apache.org>
Authored: Mon Apr 13 17:25:32 2015 -0700
Committer: Junping Du <ju...@apache.org>
Committed: Mon Apr 13 17:41:02 2015 -0700

----------------------------------------------------------------------
 hadoop-yarn-project/CHANGES.txt                 |   3 +
 .../apache/hadoop/yarn/client/cli/LogsCLI.java  | 484 +++++++++++++++++--
 .../hadoop/yarn/client/cli/TestLogsCLI.java     |  85 +++-
 .../logaggregation/AggregatedLogFormat.java     |  54 +++
 .../yarn/logaggregation/LogCLIHelpers.java      |  57 ++-
 5 files changed, 627 insertions(+), 56 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/hadoop/blob/a64b8915/hadoop-yarn-project/CHANGES.txt
----------------------------------------------------------------------
diff --git a/hadoop-yarn-project/CHANGES.txt b/hadoop-yarn-project/CHANGES.txt
index 72d7eb7..34a8ae3 100644
--- a/hadoop-yarn-project/CHANGES.txt
+++ b/hadoop-yarn-project/CHANGES.txt
@@ -18,6 +18,9 @@ Release 2.8.0 - UNRELEASED
     YARN-3348. Add a 'yarn top' tool to help understand cluster usage. (Varun
     Vasudev via jianhe)
 
+    YARN-3347. Improve YARN log command to get AMContainer logs as well as 
+    running containers logs. (Xuan Gong via junping_du)
+
   IMPROVEMENTS
 
     YARN-1880. Cleanup TestApplicationClientProtocolOnHA

http://git-wip-us.apache.org/repos/asf/hadoop/blob/a64b8915/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/main/java/org/apache/hadoop/yarn/client/cli/LogsCLI.java
----------------------------------------------------------------------
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/main/java/org/apache/hadoop/yarn/client/cli/LogsCLI.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/main/java/org/apache/hadoop/yarn/client/cli/LogsCLI.java
index d3b44a7..25481f8 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/main/java/org/apache/hadoop/yarn/client/cli/LogsCLI.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/main/java/org/apache/hadoop/yarn/client/cli/LogsCLI.java
@@ -19,6 +19,12 @@
 package org.apache.hadoop.yarn.client.cli;
 
 import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import javax.ws.rs.core.MediaType;
 
 import org.apache.commons.cli.CommandLine;
 import org.apache.commons.cli.CommandLineParser;
@@ -27,6 +33,7 @@ import org.apache.commons.cli.HelpFormatter;
 import org.apache.commons.cli.Option;
 import org.apache.commons.cli.Options;
 import org.apache.commons.cli.ParseException;
+import org.apache.commons.lang.StringUtils;
 import org.apache.hadoop.classification.InterfaceAudience.Public;
 import org.apache.hadoop.classification.InterfaceStability.Evolving;
 import org.apache.hadoop.conf.Configuration;
@@ -35,13 +42,25 @@ import org.apache.hadoop.security.UserGroupInformation;
 import org.apache.hadoop.util.Tool;
 import org.apache.hadoop.yarn.api.records.ApplicationId;
 import org.apache.hadoop.yarn.api.records.ApplicationReport;
+import org.apache.hadoop.yarn.api.records.ContainerReport;
+import org.apache.hadoop.yarn.api.records.YarnApplicationState;
 import org.apache.hadoop.yarn.client.api.YarnClient;
 import org.apache.hadoop.yarn.conf.YarnConfiguration;
 import org.apache.hadoop.yarn.exceptions.YarnException;
 import org.apache.hadoop.yarn.logaggregation.LogCLIHelpers;
 import org.apache.hadoop.yarn.util.ConverterUtils;
+import org.apache.hadoop.yarn.util.Times;
+import org.apache.hadoop.yarn.webapp.util.WebAppUtils;
+import org.codehaus.jettison.json.JSONArray;
+import org.codehaus.jettison.json.JSONException;
+import org.codehaus.jettison.json.JSONObject;
 
 import com.google.common.annotations.VisibleForTesting;
+import com.sun.jersey.api.client.Client;
+import com.sun.jersey.api.client.ClientHandlerException;
+import com.sun.jersey.api.client.ClientResponse;
+import com.sun.jersey.api.client.UniformInterfaceException;
+import com.sun.jersey.api.client.WebResource;
 
 @Public
 @Evolving
@@ -51,6 +70,8 @@ public class LogsCLI extends Configured implements Tool {
   private static final String APPLICATION_ID_OPTION = "applicationId";
   private static final String NODE_ADDRESS_OPTION = "nodeAddress";
   private static final String APP_OWNER_OPTION = "appOwner";
+  private static final String AM_CONTAINER_OPTION = "am";
+  private static final String CONTAINER_LOG_FILES = "logFiles";
   public static final String HELP_CMD = "help";
 
   @Override
@@ -62,22 +83,47 @@ public class LogsCLI extends Configured implements Tool {
         new Option(APPLICATION_ID_OPTION, true, "ApplicationId (required)");
     appIdOpt.setRequired(true);
     opts.addOption(appIdOpt);
-    opts.addOption(CONTAINER_ID_OPTION, true,
-      "ContainerId (must be specified if node address is specified)");
+    opts.addOption(CONTAINER_ID_OPTION, true, "ContainerId. "
+        + "By default, it will only print syslog if the application is runing."
+        + " Work with -logFiles to get other logs.");
     opts.addOption(NODE_ADDRESS_OPTION, true, "NodeAddress in the format "
-      + "nodename:port (must be specified if container id is specified)");
+      + "nodename:port");
     opts.addOption(APP_OWNER_OPTION, true,
       "AppOwner (assumed to be current user if not specified)");
+    Option amOption = new Option(AM_CONTAINER_OPTION, true, 
+      "Prints the AM Container logs for this application. "
+      + "Specify comma-separated value to get logs for related AM Container. "
+      + "For example, If we specify -am 1,2, we will get the logs for "
+      + "the first AM Container as well as the second AM Container. "
+      + "To get logs for all AM Containers, use -am ALL. "
+      + "To get logs for the latest AM Container, use -am -1. "
+      + "By default, it will only print out syslog. Work with -logFiles "
+      + "to get other logs");
+    amOption.setValueSeparator(',');
+    amOption.setArgs(Option.UNLIMITED_VALUES);
+    amOption.setArgName("AM Containers");
+    opts.addOption(amOption);
+    Option logFileOpt = new Option(CONTAINER_LOG_FILES, true,
+      "Work with -am/-containerId and specify comma-separated value "
+      + "to get specified Container log files");
+    logFileOpt.setValueSeparator(',');
+    logFileOpt.setArgs(Option.UNLIMITED_VALUES);
+    logFileOpt.setArgName("Log File Name");
+    opts.addOption(logFileOpt);
+
     opts.getOption(APPLICATION_ID_OPTION).setArgName("Application ID");
     opts.getOption(CONTAINER_ID_OPTION).setArgName("Container ID");
     opts.getOption(NODE_ADDRESS_OPTION).setArgName("Node Address");
     opts.getOption(APP_OWNER_OPTION).setArgName("Application Owner");
+    opts.getOption(AM_CONTAINER_OPTION).setArgName("AM Containers");
 
     Options printOpts = new Options();
     printOpts.addOption(opts.getOption(HELP_CMD));
     printOpts.addOption(opts.getOption(CONTAINER_ID_OPTION));
     printOpts.addOption(opts.getOption(NODE_ADDRESS_OPTION));
     printOpts.addOption(opts.getOption(APP_OWNER_OPTION));
+    printOpts.addOption(opts.getOption(AM_CONTAINER_OPTION));
+    printOpts.addOption(opts.getOption(CONTAINER_LOG_FILES));
 
     if (args.length < 1) {
       printHelpMessage(printOpts);
@@ -92,12 +138,46 @@ public class LogsCLI extends Configured implements Tool {
     String containerIdStr = null;
     String nodeAddress = null;
     String appOwner = null;
+    boolean getAMContainerLogs = false;
+    String[] logFiles = null;
+    List<String> amContainersList = new ArrayList<String>();
     try {
       CommandLine commandLine = parser.parse(opts, args, true);
       appIdStr = commandLine.getOptionValue(APPLICATION_ID_OPTION);
       containerIdStr = commandLine.getOptionValue(CONTAINER_ID_OPTION);
       nodeAddress = commandLine.getOptionValue(NODE_ADDRESS_OPTION);
       appOwner = commandLine.getOptionValue(APP_OWNER_OPTION);
+      getAMContainerLogs = commandLine.hasOption(AM_CONTAINER_OPTION);
+      if (getAMContainerLogs) {
+        String[] amContainers = commandLine.getOptionValues(AM_CONTAINER_OPTION);
+        for (String am : amContainers) {
+          boolean errorInput = false;
+          if (!am.trim().equalsIgnoreCase("ALL")) {
+            try {
+              int id = Integer.parseInt(am.trim());
+              if (id != -1 && id <= 0) {
+                errorInput = true;
+              }
+            } catch (NumberFormatException ex) {
+              errorInput = true;
+            }
+            if (errorInput) {
+              System.err.println(
+                "Invalid input for option -am. Valid inputs are 'ALL', -1 "
+                + "and any other integer which is larger than 0.");
+              printHelpMessage(printOpts);
+              return -1;
+            }
+            amContainersList.add(am.trim());
+          } else {
+            amContainersList.add("ALL");
+            break;
+          }
+        }
+      }
+      if (commandLine.hasOption(CONTAINER_LOG_FILES)) {
+        logFiles = commandLine.getOptionValues(CONTAINER_LOG_FILES);
+      }
     } catch (ParseException e) {
       System.err.println("options parsing failed: " + e.getMessage());
       printHelpMessage(printOpts);
@@ -118,64 +198,137 @@ public class LogsCLI extends Configured implements Tool {
       return -1;
     }
 
+    LogCLIHelpers logCliHelper = new LogCLIHelpers();
+    logCliHelper.setConf(getConf());
+
+    if (appOwner == null || appOwner.isEmpty()) {
+      appOwner = UserGroupInformation.getCurrentUser().getShortUserName();
+    }
+
+    YarnApplicationState appState = YarnApplicationState.NEW;
     try {
-      int resultCode = verifyApplicationState(appId);
-      if (resultCode != 0) {
+      appState = getApplicationState(appId);
+      if (appState == YarnApplicationState.NEW
+          || appState == YarnApplicationState.NEW_SAVING
+          || appState == YarnApplicationState.SUBMITTED) {
         System.out.println("Logs are not avaiable right now.");
-        return resultCode;
+        return -1;
       }
-    } catch (Exception e) {
+    } catch (IOException | YarnException e) {
       System.err.println("Unable to get ApplicationState."
           + " Attempting to fetch logs directly from the filesystem.");
     }
 
-    LogCLIHelpers logCliHelper = new LogCLIHelpers();
-    logCliHelper.setConf(getConf());
-    
-    if (appOwner == null || appOwner.isEmpty()) {
-      appOwner = UserGroupInformation.getCurrentUser().getShortUserName();
+    // To get am logs
+    if (getAMContainerLogs) {
+      // if we do not specify the value for CONTAINER_LOG_FILES option,
+      // we will only output syslog
+      if (logFiles == null || logFiles.length == 0) {
+        logFiles = new String[] { "syslog" };
+      }
+      // If the application is running, we will call the RM WebService
+      // to get the AppAttempts which includes the nodeHttpAddress
+      // and containerId for all the AM Containers.
+      // After that, we will call NodeManager webService to get the
+      // related logs
+      if (appState == YarnApplicationState.ACCEPTED
+          || appState == YarnApplicationState.RUNNING) {
+        return printAMContainerLogs(getConf(), appIdStr, amContainersList,
+          logFiles, logCliHelper, appOwner, false);
+      } else {
+        // If the application is in the final state, we will call RM webservice
+        // to get all AppAttempts information first. If we get nothing,
+        // we will try to call AHS webservice to get related AppAttempts
+        // which includes nodeAddress for the AM Containers.
+        // After that, we will use nodeAddress and containerId
+        // to get logs from HDFS directly.
+        if (getConf().getBoolean(YarnConfiguration.APPLICATION_HISTORY_ENABLED,
+          YarnConfiguration.DEFAULT_APPLICATION_HISTORY_ENABLED)) {
+          return printAMContainerLogs(getConf(), appIdStr, amContainersList,
+            logFiles, logCliHelper, appOwner, true);
+        } else {
+          System.out
+            .println("Can not get AMContainers logs for the application:"
+                + appId);
+          System.out.println("This application:" + appId + " is finished."
+              + " Please enable the application history service. Or Using "
+              + "yarn logs -applicationId <appId> -containerId <containerId> "
+              + "--nodeAddress <nodeHttpAddress> to get the container logs");
+          return -1;
+        }
+      }
     }
+
     int resultCode = 0;
-    if (containerIdStr == null && nodeAddress == null) {
-      resultCode = logCliHelper.dumpAllContainersLogs(appId, appOwner, System.out);
-    } else if ((containerIdStr == null && nodeAddress != null)
-        || (containerIdStr != null && nodeAddress == null)) {
-      System.out.println("ContainerId or NodeAddress cannot be null!");
-      printHelpMessage(printOpts);
-      resultCode = -1;
+    if (containerIdStr != null) {
+      // if we provide the node address and the application is in the final
+      // state, we could directly get logs from HDFS.
+      if (nodeAddress != null && isApplicationFinished(appState)) {
+        return logCliHelper.dumpAContainersLogsForALogType(appIdStr,
+            containerIdStr, nodeAddress, appOwner, logFiles == null ? null
+                : Arrays.asList(logFiles));
+      }
+      try {
+        // If the nodeAddress is not provided, we will try to get
+        // the ContainerReport. In the containerReport, we could get
+        // nodeAddress and nodeHttpAddress
+        ContainerReport report = getContainerReport(containerIdStr);
+        String nodeHttpAddress =
+            report.getNodeHttpAddress().replaceFirst(
+              WebAppUtils.getHttpSchemePrefix(getConf()), "");
+        String nodeId = report.getAssignedNode().toString();
+        // If the application is not in the final state,
+        // we will provide the NodeHttpAddress and get the container logs
+        // by calling NodeManager webservice.
+        if (!isApplicationFinished(appState)) {
+          if (logFiles == null || logFiles.length == 0) {
+            logFiles = new String[] { "syslog" };
+          }
+          printContainerLogsFromRunningApplication(getConf(), appIdStr,
+            containerIdStr, nodeHttpAddress, nodeId, logFiles, logCliHelper,
+            appOwner);
+        } else {
+          // If the application is in the final state, we will directly
+          // get the container logs from HDFS.
+          printContainerLogsForFinishedApplication(appIdStr, containerIdStr,
+            nodeId, logFiles, logCliHelper, appOwner);
+        }
+        return resultCode;
+      } catch (IOException | YarnException ex) {
+        System.err.println("Unable to get logs for this container:"
+            + containerIdStr + "for the application:" + appId);
+        if (!getConf().getBoolean(YarnConfiguration.APPLICATION_HISTORY_ENABLED,
+          YarnConfiguration.DEFAULT_APPLICATION_HISTORY_ENABLED)) {
+          System.out.println("Please enable the application history service. Or ");
+        }
+        System.out.println("Using "
+            + "yarn logs -applicationId <appId> -containerId <containerId> "
+            + "--nodeAddress <nodeHttpAddress> to get the container logs");
+        return -1;
+      }
     } else {
-      resultCode =
-          logCliHelper.dumpAContainersLogs(appIdStr, containerIdStr,
-            nodeAddress, appOwner);
+      if (nodeAddress == null) {
+        resultCode =
+            logCliHelper.dumpAllContainersLogs(appId, appOwner, System.out);
+      } else {
+        System.out.println("Should at least provide ContainerId!");
+        printHelpMessage(printOpts);
+        resultCode = -1;
+      }
     }
-
     return resultCode;
   }
 
-  private int verifyApplicationState(ApplicationId appId) throws IOException,
-      YarnException {
+  private YarnApplicationState getApplicationState(ApplicationId appId)
+      throws IOException, YarnException {
     YarnClient yarnClient = createYarnClient();
 
     try {
       ApplicationReport appReport = yarnClient.getApplicationReport(appId);
-      switch (appReport.getYarnApplicationState()) {
-      case NEW:
-      case NEW_SAVING:
-      case SUBMITTED:
-        return -1;
-      case ACCEPTED:
-      case RUNNING:
-      case FAILED:
-      case FINISHED:
-      case KILLED:
-      default:
-        break;
-
-      }
+      return appReport.getYarnApplicationState();
     } finally {
       yarnClient.close();
     }
-    return 0;
   }
   
   @VisibleForTesting
@@ -201,4 +354,255 @@ public class LogsCLI extends Configured implements Tool {
     formatter.setSyntaxPrefix("");
     formatter.printHelp("general options are:", options);
   }
+
+  private List<JSONObject> getAMContainerInfoForRMWebService(
+      Configuration conf, String appId) throws ClientHandlerException,
+      UniformInterfaceException, JSONException {
+    Client webServiceClient = Client.create();
+    String webAppAddress =
+        WebAppUtils.getWebAppBindURL(conf, YarnConfiguration.RM_BIND_HOST,
+          WebAppUtils.getRMWebAppURLWithScheme(conf));
+    WebResource webResource = webServiceClient.resource(webAppAddress);
+
+    ClientResponse response =
+        webResource.path("ws").path("v1").path("cluster").path("apps")
+          .path(appId).path("appattempts").accept(MediaType.APPLICATION_JSON)
+          .get(ClientResponse.class);
+    JSONObject json =
+        response.getEntity(JSONObject.class).getJSONObject("appAttempts");
+    JSONArray requests = json.getJSONArray("appAttempt");
+    List<JSONObject> amContainersList = new ArrayList<JSONObject>();
+    for (int i = 0; i < requests.length(); i++) {
+      amContainersList.add(requests.getJSONObject(i));
+    }
+    return amContainersList;
+  }
+
+  private List<JSONObject> getAMContainerInfoForAHSWebService(Configuration conf,
+      String appId) throws ClientHandlerException, UniformInterfaceException,
+      JSONException {
+    Client webServiceClient = Client.create();
+    String webAppAddress =
+        WebAppUtils.getHttpSchemePrefix(conf)
+            + WebAppUtils.getAHSWebAppURLWithoutScheme(conf);
+    WebResource webResource = webServiceClient.resource(webAppAddress);
+
+    ClientResponse response =
+        webResource.path("ws").path("v1").path("applicationhistory").path("apps")
+          .path(appId).path("appattempts").accept(MediaType.APPLICATION_JSON)
+          .get(ClientResponse.class);
+    JSONObject json = response.getEntity(JSONObject.class);
+    JSONArray requests = json.getJSONArray("appAttempt");
+    List<JSONObject> amContainersList = new ArrayList<JSONObject>();
+    for (int i = 0; i < requests.length(); i++) {
+      amContainersList.add(requests.getJSONObject(i));
+    }
+    Collections.reverse(amContainersList);
+    return amContainersList;
+  }
+
+  private void printContainerLogsFromRunningApplication(Configuration conf,
+      String appId, String containerIdStr, String nodeHttpAddress,
+      String nodeId, String[] logFiles, LogCLIHelpers logCliHelper,
+      String appOwner) throws IOException {
+    Client webServiceClient = Client.create();
+    String containerString = "\n\nContainer: " + containerIdStr;
+    System.out.println(containerString);
+    System.out.println(StringUtils.repeat("=", containerString.length()));
+    for (String logFile : logFiles) {
+      System.out.println("LogType:" + logFile);
+      System.out.println("Log Upload Time:"
+          + Times.format(System.currentTimeMillis()));
+      System.out.println("Log Contents:");
+      try {
+        WebResource webResource =
+            webServiceClient.resource(WebAppUtils.getHttpSchemePrefix(conf)
+                + nodeHttpAddress);
+        ClientResponse response =
+            webResource.path("ws").path("v1").path("node")
+              .path("containerlogs").path(containerIdStr).path(logFile)
+              .accept(MediaType.TEXT_PLAIN).get(ClientResponse.class);
+        System.out.println(response.getEntity(String.class));
+        System.out.println("End of LogType:" + logFile);
+      } catch (ClientHandlerException | UniformInterfaceException ex) {
+        System.out.println("Can not find the log file:" + logFile
+            + " for the container:" + containerIdStr + " in NodeManager:"
+            + nodeId);
+      }
+    }
+    // for the case, we have already uploaded partial logs in HDFS
+    logCliHelper.dumpAContainersLogsForALogType(appId, containerIdStr, nodeId,
+      appOwner, Arrays.asList(logFiles));
+  }
+
+  private void printContainerLogsForFinishedApplication(String appId,
+      String containerId, String nodeAddress, String[] logFiles,
+      LogCLIHelpers logCliHelper, String appOwner) throws IOException {
+    String containerString = "\n\nContainer: " + containerId;
+    System.out.println(containerString);
+    System.out.println(StringUtils.repeat("=", containerString.length()));
+    logCliHelper.dumpAContainersLogsForALogType(appId, containerId,
+      nodeAddress, appOwner, logFiles != null ? Arrays.asList(logFiles) : null);
+  }
+
+  private ContainerReport getContainerReport(String containerIdStr)
+      throws YarnException, IOException {
+    YarnClient yarnClient = createYarnClient();
+    try {
+      return yarnClient.getContainerReport(ConverterUtils
+        .toContainerId(containerIdStr));
+    } finally {
+      yarnClient.close();
+    }
+  }
+
+  private boolean isApplicationFinished(YarnApplicationState appState) {
+    return appState == YarnApplicationState.FINISHED
+        || appState == YarnApplicationState.FAILED
+        || appState == YarnApplicationState.KILLED; 
+  }
+
+  private int printAMContainerLogs(Configuration conf, String appId,
+      List<String> amContainers, String[] logFiles, LogCLIHelpers logCliHelper,
+      String appOwner, boolean applicationFinished) throws Exception {
+    List<JSONObject> amContainersList = null;
+    List<AMLogsRequest> requests = new ArrayList<AMLogsRequest>();
+    boolean getAMContainerLists = false;
+    String errorMessage = "";
+    try {
+      amContainersList = getAMContainerInfoForRMWebService(conf, appId);
+      if (amContainersList != null && !amContainersList.isEmpty()) {
+        getAMContainerLists = true;
+        for (JSONObject amContainer : amContainersList) {
+          AMLogsRequest request = new AMLogsRequest(applicationFinished);
+          request.setAmContainerId(amContainer.getString("containerId"));
+          request.setNodeHttpAddress(amContainer.getString("nodeHttpAddress"));
+          request.setNodeId(amContainer.getString("nodeId"));
+          requests.add(request);
+        }
+      }
+    } catch (Exception ex) {
+      errorMessage = ex.getMessage();
+      if (applicationFinished) {
+        try {
+          amContainersList = getAMContainerInfoForAHSWebService(conf, appId);
+          if (amContainersList != null && !amContainersList.isEmpty()) {
+            getAMContainerLists = true;
+            for (JSONObject amContainer : amContainersList) {
+              AMLogsRequest request = new AMLogsRequest(applicationFinished);
+              request.setAmContainerId(amContainer.getString("amContainerId"));
+              requests.add(request);
+            }
+          }
+        } catch (Exception e) {
+          errorMessage = e.getMessage();
+        }
+      }
+    }
+
+    if (!getAMContainerLists) {
+      System.err.println("Unable to get AM container informations "
+          + "for the application:" + appId);
+      System.err.println(errorMessage);
+      return -1;
+    }
+
+    if (amContainers.contains("ALL")) {
+      for (AMLogsRequest request : requests) {
+        outputAMContainerLogs(request, conf, appId, logFiles, logCliHelper,
+          appOwner);
+      }
+      System.out.println();      
+      System.out.println("Specified ALL for -am option. "
+          + "Printed logs for all am containers.");
+    } else {
+      for (String amContainer : amContainers) {
+        int amContainerId = Integer.parseInt(amContainer.trim());
+        if (amContainerId == -1) {
+          outputAMContainerLogs(requests.get(requests.size() - 1), conf, appId,
+            logFiles, logCliHelper, appOwner);
+        } else {
+          if (amContainerId <= requests.size()) {
+            outputAMContainerLogs(requests.get(amContainerId - 1), conf, appId,
+              logFiles, logCliHelper, appOwner);
+          }
+        }
+      }
+    }
+    return 0;
+  }
+
+  private void outputAMContainerLogs(AMLogsRequest request, Configuration conf,
+      String appId, String[] logFiles, LogCLIHelpers logCliHelper,
+      String appOwner) throws Exception {
+    String nodeHttpAddress = request.getNodeHttpAddress();
+    String containerId = request.getAmContainerId();
+    String nodeId = request.getNodeId();
+
+    if (request.isAppFinished()) {
+      if (containerId != null && !containerId.isEmpty()) {
+        if (nodeId == null || nodeId.isEmpty()) {
+          try {
+            nodeId =
+                getContainerReport(containerId).getAssignedNode().toString();
+          } catch (Exception ex) {
+            System.err.println(ex);
+            nodeId = null;
+          }
+        }
+        if (nodeId != null && !nodeId.isEmpty()) {
+          printContainerLogsForFinishedApplication(appId, containerId, nodeId,
+            logFiles, logCliHelper, appOwner);
+        }
+      }
+    } else {
+      if (nodeHttpAddress != null && containerId != null
+          && !nodeHttpAddress.isEmpty() && !containerId.isEmpty()) {
+        printContainerLogsFromRunningApplication(conf, appId, containerId,
+          nodeHttpAddress, nodeId, logFiles, logCliHelper, appOwner);
+      }
+    }
+  }
+
+  private static class AMLogsRequest {
+    private String amContainerId;
+    private String nodeId;
+    private String nodeHttpAddress;
+    private final boolean isAppFinished;
+
+    AMLogsRequest(boolean isAppFinished) {
+      this.isAppFinished = isAppFinished;
+      this.setAmContainerId("");
+      this.setNodeId("");
+      this.setNodeHttpAddress("");
+    }
+
+    public String getAmContainerId() {
+      return amContainerId;
+    }
+
+    public void setAmContainerId(String amContainerId) {
+      this.amContainerId = amContainerId;
+    }
+
+    public String getNodeId() {
+      return nodeId;
+    }
+
+    public void setNodeId(String nodeId) {
+      this.nodeId = nodeId;
+    }
+
+    public String getNodeHttpAddress() {
+      return nodeHttpAddress;
+    }
+
+    public void setNodeHttpAddress(String nodeHttpAddress) {
+      this.nodeHttpAddress = nodeHttpAddress;
+    }
+
+    public boolean isAppFinished() {
+      return isAppFinished;
+    }
+  }
 }

http://git-wip-us.apache.org/repos/asf/hadoop/blob/a64b8915/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/test/java/org/apache/hadoop/yarn/client/cli/TestLogsCLI.java
----------------------------------------------------------------------
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/test/java/org/apache/hadoop/yarn/client/cli/TestLogsCLI.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/test/java/org/apache/hadoop/yarn/client/cli/TestLogsCLI.java
index 7ee918e..7d20cf2 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/test/java/org/apache/hadoop/yarn/client/cli/TestLogsCLI.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/test/java/org/apache/hadoop/yarn/client/cli/TestLogsCLI.java
@@ -32,6 +32,7 @@ import java.io.IOException;
 import java.io.PrintStream;
 import java.io.PrintWriter;
 import java.io.Writer;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -145,14 +146,28 @@ public class TestLogsCLI {
     pw.println("usage: yarn logs -applicationId <application ID> [OPTIONS]");
     pw.println();
     pw.println("general options are:");
+    pw.println(" -am <AM Containers>             Prints the AM Container logs for this");
+    pw.println("                                 application. Specify comma-separated");
+    pw.println("                                 value to get logs for related AM");
+    pw.println("                                 Container. For example, If we specify -am");
+    pw.println("                                 1,2, we will get the logs for the first");
+    pw.println("                                 AM Container as well as the second AM");
+    pw.println("                                 Container. To get logs for all AM");
+    pw.println("                                 Containers, use -am ALL. To get logs for");
+    pw.println("                                 the latest AM Container, use -am -1. By");
+    pw.println("                                 default, it will only print out syslog.");
+    pw.println("                                 Work with -logFiles to get other logs");
     pw.println(" -appOwner <Application Owner>   AppOwner (assumed to be current user if");
     pw.println("                                 not specified)");
-    pw.println(" -containerId <Container ID>     ContainerId (must be specified if node");
-    pw.println("                                 address is specified)");
+    pw.println(" -containerId <Container ID>     ContainerId. By default, it will only");
+    pw.println("                                 print syslog if the application is");
+    pw.println("                                 runing. Work with -logFiles to get other");
+    pw.println("                                 logs.");
     pw.println(" -help                           Displays help for all commands.");
+    pw.println(" -logFiles <Log File Name>       Work with -am/-containerId and specify");
+    pw.println("                                 comma-separated value to get specified");
+    pw.println("                                 Container log files");
     pw.println(" -nodeAddress <Node Address>     NodeAddress in the format nodename:port");
-    pw.println("                                 (must be specified if container id is");
-    pw.println("                                 specified)");
     pw.close();
     String appReportStr = baos.toString("UTF-8");
     Assert.assertEquals(appReportStr, sysOutStream.toString());
@@ -176,6 +191,7 @@ public class TestLogsCLI {
     ContainerId containerId0 = ContainerIdPBImpl.newContainerId(appAttemptId, 0);
     ContainerId containerId1 = ContainerIdPBImpl.newContainerId(appAttemptId, 1);
     ContainerId containerId2 = ContainerIdPBImpl.newContainerId(appAttemptId, 2);
+    ContainerId containerId3 = ContainerIdPBImpl.newContainerId(appAttemptId, 3);
     NodeId nodeId = NodeId.newInstance("localhost", 1234);
 
     // create local logs
@@ -193,9 +209,15 @@ public class TestLogsCLI {
     assertTrue(fs.mkdirs(appLogsDir));
     List<String> rootLogDirs = Arrays.asList(rootLogDir);
 
+    List<String> logTypes = new ArrayList<String>();
+    logTypes.add("syslog");
     // create container logs in localLogDir
-    createContainerLogInLocalDir(appLogsDir, containerId1, fs);
-    createContainerLogInLocalDir(appLogsDir, containerId2, fs);
+    createContainerLogInLocalDir(appLogsDir, containerId1, fs, logTypes);
+    createContainerLogInLocalDir(appLogsDir, containerId2, fs, logTypes);
+
+    // create two logs for container3 in localLogDir
+    logTypes.add("stdout");
+    createContainerLogInLocalDir(appLogsDir, containerId3, fs, logTypes);
 
     Path path =
         new Path(remoteLogRootDir + ugi.getShortUserName()
@@ -217,6 +239,8 @@ public class TestLogsCLI {
       containerId1, path, fs);
     uploadContainerLogIntoRemoteDir(ugi, configuration, rootLogDirs, nodeId,
       containerId2, path, fs);
+    uploadContainerLogIntoRemoteDir(ugi, configuration, rootLogDirs, nodeId,
+      containerId3, path, fs);
 
     YarnClient mockYarnClient =
         createMockYarnClient(YarnApplicationState.FINISHED);
@@ -226,9 +250,13 @@ public class TestLogsCLI {
     int exitCode = cli.run(new String[] { "-applicationId", appId.toString() });
     assertTrue(exitCode == 0);
     assertTrue(sysOutStream.toString().contains(
-      "Hello container_0_0001_01_000001!"));
+      "Hello container_0_0001_01_000001 in syslog!"));
+    assertTrue(sysOutStream.toString().contains(
+      "Hello container_0_0001_01_000002 in syslog!"));
     assertTrue(sysOutStream.toString().contains(
-      "Hello container_0_0001_01_000002!"));
+      "Hello container_0_0001_01_000003 in syslog!"));
+    assertTrue(sysOutStream.toString().contains(
+      "Hello container_0_0001_01_000003 in stdout!"));
     sysOutStream.reset();
 
     // uploaded two logs for container1. The first log is empty.
@@ -240,7 +268,7 @@ public class TestLogsCLI {
             containerId1.toString() });
     assertTrue(exitCode == 0);
     assertTrue(sysOutStream.toString().contains(
-        "Hello container_0_0001_01_000001!"));
+        "Hello container_0_0001_01_000001 in syslog!"));
     assertTrue(sysOutStream.toString().contains("Log Upload Time"));
     assertTrue(!sysOutStream.toString().contains(
       "Logs for container " + containerId1.toString()
@@ -258,22 +286,51 @@ public class TestLogsCLI {
     assertTrue(sysOutStream.toString().contains(
       "Logs for container " + containerId0.toString()
           + " are not present in this log-file."));
+    sysOutStream.reset();
+
+    // uploaded two logs for container3. The first log is named as syslog.
+    // The second one is named as stdout.
+    exitCode =
+        cli.run(new String[] { "-applicationId", appId.toString(),
+            "-nodeAddress", nodeId.toString(), "-containerId",
+            containerId3.toString() });
+    assertTrue(exitCode == 0);
+    assertTrue(sysOutStream.toString().contains(
+        "Hello container_0_0001_01_000003 in syslog!"));
+    assertTrue(sysOutStream.toString().contains(
+        "Hello container_0_0001_01_000003 in stdout!"));
+    sysOutStream.reset();
+
+    // set -logFiles option as stdout
+    // should only print log with the name as stdout
+    exitCode =
+        cli.run(new String[] { "-applicationId", appId.toString(),
+            "-nodeAddress", nodeId.toString(), "-containerId",
+            containerId3.toString() , "-logFiles", "stdout"});
+    assertTrue(exitCode == 0);
+    assertTrue(sysOutStream.toString().contains(
+        "Hello container_0_0001_01_000003 in stdout!"));
+    assertTrue(!sysOutStream.toString().contains(
+        "Hello container_0_0001_01_000003 in syslog!"));
+    sysOutStream.reset();
 
     fs.delete(new Path(remoteLogRootDir), true);
     fs.delete(new Path(rootLogDir), true);
   }
 
   private static void createContainerLogInLocalDir(Path appLogsDir,
-      ContainerId containerId, FileSystem fs) throws Exception {
+      ContainerId containerId, FileSystem fs, List<String> logTypes) throws Exception {
     Path containerLogsDir = new Path(appLogsDir, containerId.toString());
     if (fs.exists(containerLogsDir)) {
       fs.delete(containerLogsDir, true);
     }
     assertTrue(fs.mkdirs(containerLogsDir));
-    Writer writer =
-        new FileWriter(new File(containerLogsDir.toString(), "sysout"));
-    writer.write("Hello " + containerId + "!");
-    writer.close();
+    for (String logType : logTypes) {
+      Writer writer =
+          new FileWriter(new File(containerLogsDir.toString(), logType));
+      writer.write("Hello " + containerId + " in " + logType + "!");
+      writer.close();
+    }
   }
 
   private static void uploadContainerLogIntoRemoteDir(UserGroupInformation ugi,

http://git-wip-us.apache.org/repos/asf/hadoop/blob/a64b8915/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/logaggregation/AggregatedLogFormat.java
----------------------------------------------------------------------
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/logaggregation/AggregatedLogFormat.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/logaggregation/AggregatedLogFormat.java
index 57f655b..debe770 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/logaggregation/AggregatedLogFormat.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/logaggregation/AggregatedLogFormat.java
@@ -745,6 +745,60 @@ public class AggregatedLogFormat {
       readAContainerLogsForALogType(valueStream, out, -1);
     }
 
+    /**
+     * Keep calling this till you get a {@link EOFException} for getting logs of
+     * the specific types for a single container.
+     * @param valueStream
+     * @param out
+     * @param logUploadedTime
+     * @param logType
+     * @throws IOException
+     */
+    public static int readContainerLogsForALogType(
+        DataInputStream valueStream, PrintStream out, long logUploadedTime,
+        List<String> logType) throws IOException {
+      byte[] buf = new byte[65535];
+
+      String fileType = valueStream.readUTF();
+      String fileLengthStr = valueStream.readUTF();
+      long fileLength = Long.parseLong(fileLengthStr);
+      if (logType.contains(fileType)) {
+        out.print("LogType:");
+        out.println(fileType);
+        if (logUploadedTime != -1) {
+          out.print("Log Upload Time:");
+          out.println(Times.format(logUploadedTime));
+        }
+        out.print("LogLength:");
+        out.println(fileLengthStr);
+        out.println("Log Contents:");
+
+        long curRead = 0;
+        long pendingRead = fileLength - curRead;
+        int toRead = pendingRead > buf.length ? buf.length : (int) pendingRead;
+        int len = valueStream.read(buf, 0, toRead);
+        while (len != -1 && curRead < fileLength) {
+          out.write(buf, 0, len);
+          curRead += len;
+
+          pendingRead = fileLength - curRead;
+          toRead = pendingRead > buf.length ? buf.length : (int) pendingRead;
+          len = valueStream.read(buf, 0, toRead);
+        }
+        out.println("End of LogType:" + fileType);
+        out.println("");
+        return 0;
+      } else {
+        long totalSkipped = 0;
+        long currSkipped = 0;
+        while (currSkipped != -1 && totalSkipped < fileLength) {
+          currSkipped = valueStream.skip(fileLength - totalSkipped);
+          totalSkipped += currSkipped;
+        }
+        return -1;
+      }
+    }
+
     public void close() {
       IOUtils.cleanup(LOG, scanner, reader, fsDataIStream);
     }

http://git-wip-us.apache.org/repos/asf/hadoop/blob/a64b8915/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/logaggregation/LogCLIHelpers.java
----------------------------------------------------------------------
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/logaggregation/LogCLIHelpers.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/logaggregation/LogCLIHelpers.java
index df9bd32..39fd95e 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/logaggregation/LogCLIHelpers.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/logaggregation/LogCLIHelpers.java
@@ -23,6 +23,7 @@ import java.io.EOFException;
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.PrintStream;
+import java.util.List;
 
 import org.apache.commons.lang.StringUtils;
 import org.apache.hadoop.classification.InterfaceAudience.Private;
@@ -48,6 +49,14 @@ public class LogCLIHelpers implements Configurable {
   @VisibleForTesting
   public int dumpAContainersLogs(String appId, String containerId,
       String nodeId, String jobOwner) throws IOException {
+    return dumpAContainersLogsForALogType(appId, containerId, nodeId, jobOwner,
+      null);
+  }
+
+  @Private
+  @VisibleForTesting
+  public int dumpAContainersLogsForALogType(String appId, String containerId,
+      String nodeId, String jobOwner, List<String> logType) throws IOException {
     Path remoteRootLogDir = new Path(getConf().get(
         YarnConfiguration.NM_REMOTE_APP_LOG_DIR,
         YarnConfiguration.DEFAULT_NM_REMOTE_APP_LOG_DIR));
@@ -78,9 +87,16 @@ public class LogCLIHelpers implements Configurable {
           reader =
               new AggregatedLogFormat.LogReader(getConf(),
                 thisNodeFile.getPath());
-          if (dumpAContainerLogs(containerId, reader, System.out,
+          if (logType == null) {
+            if (dumpAContainerLogs(containerId, reader, System.out,
               thisNodeFile.getModificationTime()) > -1) {
-            foundContainerLogs = true;
+              foundContainerLogs = true;
+            }
+          } else {
+            if (dumpAContainerLogsForALogType(containerId, reader, System.out,
+              thisNodeFile.getModificationTime(), logType) > -1) {
+              foundContainerLogs = true;
+            }
           }
         } finally {
           if (reader != null) {
@@ -131,6 +147,43 @@ public class LogCLIHelpers implements Configurable {
   }
 
   @Private
+  public int dumpAContainerLogsForALogType(String containerIdStr,
+      AggregatedLogFormat.LogReader reader, PrintStream out,
+      long logUploadedTime, List<String> logType) throws IOException {
+    DataInputStream valueStream;
+    LogKey key = new LogKey();
+    valueStream = reader.next(key);
+
+    while (valueStream != null && !key.toString().equals(containerIdStr)) {
+      // Next container
+      key = new LogKey();
+      valueStream = reader.next(key);
+    }
+
+    if (valueStream == null) {
+      return -1;
+    }
+
+    boolean foundContainerLogs = false;
+    while (true) {
+      try {
+        int result = LogReader.readContainerLogsForALogType(
+            valueStream, out, logUploadedTime, logType);
+        if (result == 0) {
+          foundContainerLogs = true;
+        }
+      } catch (EOFException eof) {
+        break;
+      }
+    }
+
+    if (foundContainerLogs) {
+      return 0;
+    }
+    return -1;
+  }
+
+  @Private
   public int dumpAllContainersLogs(ApplicationId appId, String appOwner,
       PrintStream out) throws IOException {
     Path remoteRootLogDir = new Path(getConf().get(