001/**
002* Licensed to the Apache Software Foundation (ASF) under one
003* or more contributor license agreements.  See the NOTICE file
004* distributed with this work for additional information
005* regarding copyright ownership.  The ASF licenses this file
006* to you under the Apache License, Version 2.0 (the
007* "License"); you may not use this file except in compliance
008* with the License.  You may obtain a copy of the License at
009*
010*     http://www.apache.org/licenses/LICENSE-2.0
011*
012* Unless required by applicable law or agreed to in writing, software
013* distributed under the License is distributed on an "AS IS" BASIS,
014* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015* See the License for the specific language governing permissions and
016* limitations under the License.
017*/
018
019package org.apache.hadoop.yarn.client.cli;
020
021import java.io.IOException;
022import java.io.StringReader;
023import java.util.ArrayList;
024import java.util.Arrays;
025import java.util.Collections;
026import java.util.List;
027
028import javax.ws.rs.core.MediaType;
029import javax.xml.parsers.DocumentBuilder;
030import javax.xml.parsers.DocumentBuilderFactory;
031
032import org.apache.commons.cli.CommandLine;
033import org.apache.commons.cli.CommandLineParser;
034import org.apache.commons.cli.GnuParser;
035import org.apache.commons.cli.HelpFormatter;
036import org.apache.commons.cli.Option;
037import org.apache.commons.cli.Options;
038import org.apache.commons.cli.ParseException;
039import org.apache.commons.lang.StringUtils;
040import org.apache.hadoop.classification.InterfaceAudience.Public;
041import org.apache.hadoop.classification.InterfaceStability.Evolving;
042import org.apache.hadoop.conf.Configuration;
043import org.apache.hadoop.conf.Configured;
044import org.apache.hadoop.security.UserGroupInformation;
045import org.apache.hadoop.util.Tool;
046import org.apache.hadoop.yarn.api.records.ApplicationId;
047import org.apache.hadoop.yarn.api.records.ApplicationReport;
048import org.apache.hadoop.yarn.api.records.ContainerId;
049import org.apache.hadoop.yarn.api.records.ContainerReport;
050import org.apache.hadoop.yarn.api.records.YarnApplicationState;
051import org.apache.hadoop.yarn.client.api.YarnClient;
052import org.apache.hadoop.yarn.conf.YarnConfiguration;
053import org.apache.hadoop.yarn.exceptions.YarnException;
054import org.apache.hadoop.yarn.logaggregation.LogCLIHelpers;
055import org.apache.hadoop.yarn.util.ConverterUtils;
056import org.apache.hadoop.yarn.util.Times;
057import org.apache.hadoop.yarn.webapp.util.WebAppUtils;
058import org.codehaus.jettison.json.JSONArray;
059import org.codehaus.jettison.json.JSONException;
060import org.codehaus.jettison.json.JSONObject;
061
062import com.google.common.annotations.VisibleForTesting;
063import com.sun.jersey.api.client.Client;
064import com.sun.jersey.api.client.ClientHandlerException;
065import com.sun.jersey.api.client.ClientResponse;
066import com.sun.jersey.api.client.UniformInterfaceException;
067import com.sun.jersey.api.client.WebResource;
068import org.w3c.dom.Document;
069import org.w3c.dom.NodeList;
070import org.xml.sax.InputSource;
071
072@Public
073@Evolving
074public class LogsCLI extends Configured implements Tool {
075
076  private static final String CONTAINER_ID_OPTION = "containerId";
077  private static final String APPLICATION_ID_OPTION = "applicationId";
078  private static final String NODE_ADDRESS_OPTION = "nodeAddress";
079  private static final String APP_OWNER_OPTION = "appOwner";
080  private static final String AM_CONTAINER_OPTION = "am";
081  private static final String CONTAINER_LOG_FILES = "logFiles";
082  public static final String HELP_CMD = "help";
083
084  @Override
085  public int run(String[] args) throws Exception {
086
087    Options opts = new Options();
088    opts.addOption(HELP_CMD, false, "Displays help for all commands.");
089    Option appIdOpt =
090        new Option(APPLICATION_ID_OPTION, true, "ApplicationId (required)");
091    appIdOpt.setRequired(true);
092    opts.addOption(appIdOpt);
093    opts.addOption(CONTAINER_ID_OPTION, true, "ContainerId. "
094        + "By default, it will only print syslog if the application is runing."
095        + " Work with -logFiles to get other logs.");
096    opts.addOption(NODE_ADDRESS_OPTION, true, "NodeAddress in the format "
097      + "nodename:port");
098    opts.addOption(APP_OWNER_OPTION, true,
099      "AppOwner (assumed to be current user if not specified)");
100    Option amOption = new Option(AM_CONTAINER_OPTION, true, 
101      "Prints the AM Container logs for this application. "
102      + "Specify comma-separated value to get logs for related AM Container. "
103      + "For example, If we specify -am 1,2, we will get the logs for "
104      + "the first AM Container as well as the second AM Container. "
105      + "To get logs for all AM Containers, use -am ALL. "
106      + "To get logs for the latest AM Container, use -am -1. "
107      + "By default, it will only print out syslog. Work with -logFiles "
108      + "to get other logs");
109    amOption.setValueSeparator(',');
110    amOption.setArgs(Option.UNLIMITED_VALUES);
111    amOption.setArgName("AM Containers");
112    opts.addOption(amOption);
113    Option logFileOpt = new Option(CONTAINER_LOG_FILES, true,
114      "Work with -am/-containerId and specify comma-separated value "
115        + "to get specified container log files. Use \"ALL\" to fetch all the "
116        + "log files for the container.");
117    logFileOpt.setValueSeparator(',');
118    logFileOpt.setArgs(Option.UNLIMITED_VALUES);
119    logFileOpt.setArgName("Log File Name");
120    opts.addOption(logFileOpt);
121
122    opts.getOption(APPLICATION_ID_OPTION).setArgName("Application ID");
123    opts.getOption(CONTAINER_ID_OPTION).setArgName("Container ID");
124    opts.getOption(NODE_ADDRESS_OPTION).setArgName("Node Address");
125    opts.getOption(APP_OWNER_OPTION).setArgName("Application Owner");
126    opts.getOption(AM_CONTAINER_OPTION).setArgName("AM Containers");
127
128    Options printOpts = new Options();
129    printOpts.addOption(opts.getOption(HELP_CMD));
130    printOpts.addOption(opts.getOption(CONTAINER_ID_OPTION));
131    printOpts.addOption(opts.getOption(NODE_ADDRESS_OPTION));
132    printOpts.addOption(opts.getOption(APP_OWNER_OPTION));
133    printOpts.addOption(opts.getOption(AM_CONTAINER_OPTION));
134    printOpts.addOption(opts.getOption(CONTAINER_LOG_FILES));
135
136    if (args.length < 1) {
137      printHelpMessage(printOpts);
138      return -1;
139    }
140    if (args[0].equals("-help")) {
141      printHelpMessage(printOpts);
142      return 0;
143    }
144    CommandLineParser parser = new GnuParser();
145    String appIdStr = null;
146    String containerIdStr = null;
147    String nodeAddress = null;
148    String appOwner = null;
149    boolean getAMContainerLogs = false;
150    String[] logFiles = null;
151    List<String> amContainersList = new ArrayList<String>();
152    try {
153      CommandLine commandLine = parser.parse(opts, args, true);
154      appIdStr = commandLine.getOptionValue(APPLICATION_ID_OPTION);
155      containerIdStr = commandLine.getOptionValue(CONTAINER_ID_OPTION);
156      nodeAddress = commandLine.getOptionValue(NODE_ADDRESS_OPTION);
157      appOwner = commandLine.getOptionValue(APP_OWNER_OPTION);
158      getAMContainerLogs = commandLine.hasOption(AM_CONTAINER_OPTION);
159      if (getAMContainerLogs) {
160        String[] amContainers = commandLine.getOptionValues(AM_CONTAINER_OPTION);
161        for (String am : amContainers) {
162          boolean errorInput = false;
163          if (!am.trim().equalsIgnoreCase("ALL")) {
164            try {
165              int id = Integer.parseInt(am.trim());
166              if (id != -1 && id <= 0) {
167                errorInput = true;
168              }
169            } catch (NumberFormatException ex) {
170              errorInput = true;
171            }
172            if (errorInput) {
173              System.err.println(
174                "Invalid input for option -am. Valid inputs are 'ALL', -1 "
175                + "and any other integer which is larger than 0.");
176              printHelpMessage(printOpts);
177              return -1;
178            }
179            amContainersList.add(am.trim());
180          } else {
181            amContainersList.add("ALL");
182            break;
183          }
184        }
185      }
186      if (commandLine.hasOption(CONTAINER_LOG_FILES)) {
187        logFiles = commandLine.getOptionValues(CONTAINER_LOG_FILES);
188      }
189    } catch (ParseException e) {
190      System.err.println("options parsing failed: " + e.getMessage());
191      printHelpMessage(printOpts);
192      return -1;
193    }
194
195    if (appIdStr == null) {
196      System.err.println("ApplicationId cannot be null!");
197      printHelpMessage(printOpts);
198      return -1;
199    }
200
201    ApplicationId appId = null;
202    try {
203      appId = ApplicationId.fromString(appIdStr);
204    } catch (Exception e) {
205      System.err.println("Invalid ApplicationId specified");
206      return -1;
207    }
208
209    LogCLIHelpers logCliHelper = new LogCLIHelpers();
210    logCliHelper.setConf(getConf());
211
212    if (appOwner == null || appOwner.isEmpty()) {
213      appOwner = UserGroupInformation.getCurrentUser().getShortUserName();
214    }
215
216    YarnApplicationState appState = YarnApplicationState.NEW;
217    try {
218      appState = getApplicationState(appId);
219      if (appState == YarnApplicationState.NEW
220          || appState == YarnApplicationState.NEW_SAVING
221          || appState == YarnApplicationState.SUBMITTED) {
222        System.out.println("Logs are not avaiable right now.");
223        return -1;
224      }
225    } catch (IOException | YarnException e) {
226      System.err.println("Unable to get ApplicationState."
227          + " Attempting to fetch logs directly from the filesystem.");
228    }
229
230    // To get am logs
231    if (getAMContainerLogs) {
232      // if we do not specify the value for CONTAINER_LOG_FILES option,
233      // we will only output syslog
234      if (logFiles == null || logFiles.length == 0) {
235        logFiles = new String[] { "syslog" };
236      }
237      // If the application is running, we will call the RM WebService
238      // to get the AppAttempts which includes the nodeHttpAddress
239      // and containerId for all the AM Containers.
240      // After that, we will call NodeManager webService to get the
241      // related logs
242      if (appState == YarnApplicationState.ACCEPTED
243          || appState == YarnApplicationState.RUNNING) {
244        return printAMContainerLogs(getConf(), appIdStr, amContainersList,
245          logFiles, logCliHelper, appOwner, false);
246      } else {
247        // If the application is in the final state, we will call RM webservice
248        // to get all AppAttempts information first. If we get nothing,
249        // we will try to call AHS webservice to get related AppAttempts
250        // which includes nodeAddress for the AM Containers.
251        // After that, we will use nodeAddress and containerId
252        // to get logs from HDFS directly.
253        if (getConf().getBoolean(YarnConfiguration.APPLICATION_HISTORY_ENABLED,
254          YarnConfiguration.DEFAULT_APPLICATION_HISTORY_ENABLED)) {
255          return printAMContainerLogs(getConf(), appIdStr, amContainersList,
256            logFiles, logCliHelper, appOwner, true);
257        } else {
258          System.out
259            .println(
260                "Can not get AMContainers logs for the application:" + appId);
261          System.out.println("This application:" + appId + " is finished."
262              + " Please enable the application history service. Or Using "
263              + "yarn logs -applicationId <appId> -containerId <containerId> "
264              + "--nodeAddress <nodeHttpAddress> to get the container logs");
265          return -1;
266        }
267      }
268    }
269
270    int resultCode = 0;
271    if (containerIdStr != null) {
272      // if we provide the node address and the application is in the final
273      // state, we could directly get logs from HDFS.
274      if (nodeAddress != null && isApplicationFinished(appState)) {
275        // if user specified "ALL" as the logFiles param, pass null
276        // to logCliHelper so that it fetches all the logs
277        List<String> logs;
278        if (logFiles == null) {
279          logs = null;
280        } else if (fetchAllLogFiles(logFiles)) {
281          logs = null;
282        } else {
283          logs = Arrays.asList(logFiles);
284        }
285        return logCliHelper.dumpAContainersLogsForALogType(appIdStr,
286            containerIdStr, nodeAddress, appOwner, logs);
287      }
288      try {
289        // If the nodeAddress is not provided, we will try to get
290        // the ContainerReport. In the containerReport, we could get
291        // nodeAddress and nodeHttpAddress
292        ContainerReport report = getContainerReport(containerIdStr);
293        String nodeHttpAddress =
294            report.getNodeHttpAddress().replaceFirst(
295              WebAppUtils.getHttpSchemePrefix(getConf()), "");
296        String nodeId = report.getAssignedNode().toString();
297        // If the application is not in the final state,
298        // we will provide the NodeHttpAddress and get the container logs
299        // by calling NodeManager webservice.
300        if (!isApplicationFinished(appState)) {
301          if (logFiles == null || logFiles.length == 0) {
302            logFiles = new String[] { "syslog" };
303          }
304          printContainerLogsFromRunningApplication(getConf(), appIdStr,
305            containerIdStr, nodeHttpAddress, nodeId, logFiles, logCliHelper,
306            appOwner);
307        } else {
308          String [] requestedLogFiles = logFiles;
309          if(fetchAllLogFiles(logFiles)) {
310            requestedLogFiles = null;
311          }
312          // If the application is in the final state, we will directly
313          // get the container logs from HDFS.
314          printContainerLogsForFinishedApplication(appIdStr, containerIdStr,
315            nodeId, requestedLogFiles, logCliHelper, appOwner);
316        }
317        return resultCode;
318      } catch (IOException | YarnException ex) {
319        System.err.println("Unable to get logs for this container:"
320            + containerIdStr + "for the application:" + appId);
321        if (!getConf().getBoolean(YarnConfiguration.APPLICATION_HISTORY_ENABLED,
322          YarnConfiguration.DEFAULT_APPLICATION_HISTORY_ENABLED)) {
323          System.out.println("Please enable the application history service. Or ");
324        }
325        System.out.println("Using "
326            + "yarn logs -applicationId <appId> -containerId <containerId> "
327            + "--nodeAddress <nodeHttpAddress> to get the container logs");
328        return -1;
329      }
330    } else {
331      if (nodeAddress == null) {
332        resultCode =
333            logCliHelper.dumpAllContainersLogs(appId, appOwner, System.out);
334      } else {
335        System.out.println("Should at least provide ContainerId!");
336        printHelpMessage(printOpts);
337        resultCode = -1;
338      }
339    }
340    return resultCode;
341  }
342
343  private YarnApplicationState getApplicationState(ApplicationId appId)
344      throws IOException, YarnException {
345    YarnClient yarnClient = createYarnClient();
346
347    try {
348      ApplicationReport appReport = yarnClient.getApplicationReport(appId);
349      return appReport.getYarnApplicationState();
350    } finally {
351      yarnClient.close();
352    }
353  }
354  
355  @VisibleForTesting
356  protected YarnClient createYarnClient() {
357    YarnClient yarnClient = YarnClient.createYarnClient();
358    yarnClient.init(getConf());
359    yarnClient.start();
360    return yarnClient;
361  }
362
363  public static void main(String[] args) throws Exception {
364    Configuration conf = new YarnConfiguration();
365    LogsCLI logDumper = new LogsCLI();
366    logDumper.setConf(conf);
367    int exitCode = logDumper.run(args);
368    System.exit(exitCode);
369  }
370
371  private void printHelpMessage(Options options) {
372    System.out.println("Retrieve logs for completed YARN applications.");
373    HelpFormatter formatter = new HelpFormatter();
374    formatter.printHelp("yarn logs -applicationId <application ID> [OPTIONS]", new Options());
375    formatter.setSyntaxPrefix("");
376    formatter.printHelp("general options are:", options);
377  }
378
379  private List<JSONObject> getAMContainerInfoForRMWebService(
380      Configuration conf, String appId) throws ClientHandlerException,
381      UniformInterfaceException, JSONException {
382    Client webServiceClient = Client.create();
383    String webAppAddress = WebAppUtils.getRMWebAppURLWithScheme(conf);
384
385    WebResource webResource = webServiceClient.resource(webAppAddress);
386
387    ClientResponse response =
388        webResource.path("ws").path("v1").path("cluster").path("apps")
389          .path(appId).path("appattempts").accept(MediaType.APPLICATION_JSON)
390          .get(ClientResponse.class);
391    JSONObject json =
392        response.getEntity(JSONObject.class).getJSONObject("appAttempts");
393    JSONArray requests = json.getJSONArray("appAttempt");
394    List<JSONObject> amContainersList = new ArrayList<JSONObject>();
395    for (int i = 0; i < requests.length(); i++) {
396      amContainersList.add(requests.getJSONObject(i));
397    }
398    return amContainersList;
399  }
400
401  private List<JSONObject> getAMContainerInfoForAHSWebService(Configuration conf,
402      String appId) throws ClientHandlerException, UniformInterfaceException,
403      JSONException {
404    Client webServiceClient = Client.create();
405    String webAppAddress =
406        WebAppUtils.getHttpSchemePrefix(conf)
407            + WebAppUtils.getAHSWebAppURLWithoutScheme(conf);
408    WebResource webResource = webServiceClient.resource(webAppAddress);
409
410    ClientResponse response =
411        webResource.path("ws").path("v1").path("applicationhistory").path("apps")
412          .path(appId).path("appattempts").accept(MediaType.APPLICATION_JSON)
413          .get(ClientResponse.class);
414    JSONObject json = response.getEntity(JSONObject.class);
415    JSONArray requests = json.getJSONArray("appAttempt");
416    List<JSONObject> amContainersList = new ArrayList<JSONObject>();
417    for (int i = 0; i < requests.length(); i++) {
418      amContainersList.add(requests.getJSONObject(i));
419    }
420    Collections.reverse(amContainersList);
421    return amContainersList;
422  }
423
424  private boolean fetchAllLogFiles(String[] logFiles) {
425    if(logFiles != null) {
426      List<String> logs = Arrays.asList(logFiles);
427      if(logs.contains("ALL")) {
428        return true;
429      }
430    }
431    return false;
432  }
433
434  private String[] getContainerLogFiles(Configuration conf,
435      String containerIdStr, String nodeHttpAddress) throws IOException {
436    List<String> logFiles = new ArrayList<>();
437    Client webServiceClient = Client.create();
438    try {
439      WebResource webResource = webServiceClient
440          .resource(WebAppUtils.getHttpSchemePrefix(conf) + nodeHttpAddress);
441      ClientResponse response =
442          webResource.path("ws").path("v1").path("node").path("containers")
443              .path(containerIdStr).accept(MediaType.APPLICATION_XML)
444              .get(ClientResponse.class);
445      if (response.getClientResponseStatus().equals(ClientResponse.Status.OK)) {
446        try {
447          String xml = response.getEntity(String.class);
448          DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
449          DocumentBuilder db = dbf.newDocumentBuilder();
450          InputSource is = new InputSource();
451          is.setCharacterStream(new StringReader(xml));
452          Document dom = db.parse(is);
453          NodeList elements = dom.getElementsByTagName("containerLogFiles");
454          for (int i = 0; i < elements.getLength(); i++) {
455            logFiles.add(elements.item(i).getTextContent());
456          }
457        } catch (Exception e) {
458          System.out.println("Unable to parse xml from webservice. Error:");
459          System.out.println(e.getMessage());
460          throw new IOException(e);
461        }
462      }
463
464    } catch (ClientHandlerException | UniformInterfaceException ex) {
465      System.out.println("Unable to fetch log files list");
466      throw new IOException(ex);
467    }
468    return logFiles.toArray(new String[0]);
469  }
470
471  private void printContainerLogsFromRunningApplication(Configuration conf,
472      String appId, String containerIdStr, String nodeHttpAddress,
473      String nodeId, String[] logFiles, LogCLIHelpers logCliHelper,
474      String appOwner) throws IOException {
475    String [] requestedLogFiles = logFiles;
476    // fetch all the log files for the container
477    if (fetchAllLogFiles(logFiles)) {
478      requestedLogFiles =
479          getContainerLogFiles(getConf(), containerIdStr, nodeHttpAddress);
480    }
481    Client webServiceClient = Client.create();
482    String containerString = "\n\nContainer: " + containerIdStr;
483    System.out.println(containerString);
484    System.out.println(StringUtils.repeat("=", containerString.length()));
485
486    for (String logFile : requestedLogFiles) {
487      System.out.println("LogType:" + logFile);
488      System.out.println("Log Upload Time:"
489          + Times.format(System.currentTimeMillis()));
490      System.out.println("Log Contents:");
491      try {
492        WebResource webResource =
493            webServiceClient.resource(WebAppUtils.getHttpSchemePrefix(conf)
494                + nodeHttpAddress);
495        ClientResponse response =
496            webResource.path("ws").path("v1").path("node")
497              .path("containerlogs").path(containerIdStr).path(logFile)
498              .accept(MediaType.TEXT_PLAIN).get(ClientResponse.class);
499        System.out.println(response.getEntity(String.class));
500        System.out.println("End of LogType:" + logFile);
501      } catch (ClientHandlerException | UniformInterfaceException ex) {
502        System.out.println("Can not find the log file:" + logFile
503            + " for the container:" + containerIdStr + " in NodeManager:"
504            + nodeId);
505      }
506    }
507    // for the case, we have already uploaded partial logs in HDFS
508    logCliHelper.dumpAContainersLogsForALogType(appId, containerIdStr, nodeId,
509      appOwner, Arrays.asList(requestedLogFiles));
510  }
511
512  private void printContainerLogsForFinishedApplication(String appId,
513      String containerId, String nodeAddress, String[] logFiles,
514      LogCLIHelpers logCliHelper, String appOwner) throws IOException {
515    String containerString = "\n\nContainer: " + containerId;
516    System.out.println(containerString);
517    System.out.println(StringUtils.repeat("=", containerString.length()));
518    logCliHelper.dumpAContainersLogsForALogType(appId, containerId,
519      nodeAddress, appOwner, logFiles != null ? Arrays.asList(logFiles) : null);
520  }
521
522  private ContainerReport getContainerReport(String containerIdStr)
523      throws YarnException, IOException {
524    YarnClient yarnClient = createYarnClient();
525    try {
526      return yarnClient.getContainerReport(
527          ContainerId.fromString(containerIdStr));
528    } finally {
529      yarnClient.close();
530    }
531  }
532
533  private boolean isApplicationFinished(YarnApplicationState appState) {
534    return appState == YarnApplicationState.FINISHED
535        || appState == YarnApplicationState.FAILED
536        || appState == YarnApplicationState.KILLED; 
537  }
538
539  private int printAMContainerLogs(Configuration conf, String appId,
540      List<String> amContainers, String[] logFiles, LogCLIHelpers logCliHelper,
541      String appOwner, boolean applicationFinished) throws Exception {
542    List<JSONObject> amContainersList = null;
543    List<AMLogsRequest> requests = new ArrayList<AMLogsRequest>();
544    boolean getAMContainerLists = false;
545    String errorMessage = "";
546    try {
547      amContainersList = getAMContainerInfoForRMWebService(conf, appId);
548      if (amContainersList != null && !amContainersList.isEmpty()) {
549        getAMContainerLists = true;
550        for (JSONObject amContainer : amContainersList) {
551          AMLogsRequest request = new AMLogsRequest(applicationFinished);
552          request.setAmContainerId(amContainer.getString("containerId"));
553          request.setNodeHttpAddress(amContainer.getString("nodeHttpAddress"));
554          request.setNodeId(amContainer.getString("nodeId"));
555          requests.add(request);
556        }
557      }
558    } catch (Exception ex) {
559      errorMessage = ex.getMessage();
560      if (applicationFinished) {
561        try {
562          amContainersList = getAMContainerInfoForAHSWebService(conf, appId);
563          if (amContainersList != null && !amContainersList.isEmpty()) {
564            getAMContainerLists = true;
565            for (JSONObject amContainer : amContainersList) {
566              AMLogsRequest request = new AMLogsRequest(applicationFinished);
567              request.setAmContainerId(amContainer.getString("amContainerId"));
568              requests.add(request);
569            }
570          }
571        } catch (Exception e) {
572          errorMessage = e.getMessage();
573        }
574      }
575    }
576
577    if (!getAMContainerLists) {
578      System.err.println("Unable to get AM container informations "
579          + "for the application:" + appId);
580      System.err.println(errorMessage);
581      return -1;
582    }
583
584    if (amContainers.contains("ALL")) {
585      for (AMLogsRequest request : requests) {
586        outputAMContainerLogs(request, conf, appId, logFiles, logCliHelper,
587          appOwner);
588      }
589      System.out.println();      
590      System.out.println("Specified ALL for -am option. "
591          + "Printed logs for all am containers.");
592    } else {
593      for (String amContainer : amContainers) {
594        int amContainerId = Integer.parseInt(amContainer.trim());
595        if (amContainerId == -1) {
596          outputAMContainerLogs(requests.get(requests.size() - 1), conf, appId,
597            logFiles, logCliHelper, appOwner);
598        } else {
599          if (amContainerId <= requests.size()) {
600            outputAMContainerLogs(requests.get(amContainerId - 1), conf, appId,
601              logFiles, logCliHelper, appOwner);
602          }
603        }
604      }
605    }
606    return 0;
607  }
608
609  private void outputAMContainerLogs(AMLogsRequest request, Configuration conf,
610      String appId, String[] logFiles, LogCLIHelpers logCliHelper,
611      String appOwner) throws Exception {
612    String nodeHttpAddress = request.getNodeHttpAddress();
613    String containerId = request.getAmContainerId();
614    String nodeId = request.getNodeId();
615
616    if (request.isAppFinished()) {
617      if (containerId != null && !containerId.isEmpty()) {
618        if (nodeId == null || nodeId.isEmpty()) {
619          try {
620            nodeId =
621                getContainerReport(containerId).getAssignedNode().toString();
622          } catch (Exception ex) {
623            System.err.println(ex);
624            nodeId = null;
625          }
626        }
627        if (nodeId != null && !nodeId.isEmpty()) {
628          String [] requestedLogFilesList = null;
629          if(!fetchAllLogFiles(logFiles)) {
630            requestedLogFilesList = logFiles;
631          }
632          printContainerLogsForFinishedApplication(appId, containerId, nodeId,
633            requestedLogFilesList, logCliHelper, appOwner);
634        }
635      }
636    } else {
637      if (nodeHttpAddress != null && containerId != null
638          && !nodeHttpAddress.isEmpty() && !containerId.isEmpty()) {
639        String [] requestedLogFiles = logFiles;
640        // fetch all the log files for the AM
641        if (fetchAllLogFiles(logFiles)) {
642          requestedLogFiles =
643              getContainerLogFiles(getConf(), containerId, nodeHttpAddress);
644        }
645        printContainerLogsFromRunningApplication(conf, appId, containerId,
646          nodeHttpAddress, nodeId, requestedLogFiles, logCliHelper, appOwner);
647      }
648    }
649  }
650
651  private static class AMLogsRequest {
652    private String amContainerId;
653    private String nodeId;
654    private String nodeHttpAddress;
655    private final boolean isAppFinished;
656
657    AMLogsRequest(boolean isAppFinished) {
658      this.isAppFinished = isAppFinished;
659      this.setAmContainerId("");
660      this.setNodeId("");
661      this.setNodeHttpAddress("");
662    }
663
664    public String getAmContainerId() {
665      return amContainerId;
666    }
667
668    public void setAmContainerId(String amContainerId) {
669      this.amContainerId = amContainerId;
670    }
671
672    public String getNodeId() {
673      return nodeId;
674    }
675
676    public void setNodeId(String nodeId) {
677      this.nodeId = nodeId;
678    }
679
680    public String getNodeHttpAddress() {
681      return nodeHttpAddress;
682    }
683
684    public void setNodeHttpAddress(String nodeHttpAddress) {
685      this.nodeHttpAddress = nodeHttpAddress;
686    }
687
688    public boolean isAppFinished() {
689      return isAppFinished;
690    }
691  }
692}