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.ContainerReport;
049import org.apache.hadoop.yarn.api.records.YarnApplicationState;
050import org.apache.hadoop.yarn.client.api.YarnClient;
051import org.apache.hadoop.yarn.conf.YarnConfiguration;
052import org.apache.hadoop.yarn.exceptions.YarnException;
053import org.apache.hadoop.yarn.logaggregation.LogCLIHelpers;
054import org.apache.hadoop.yarn.util.ConverterUtils;
055import org.apache.hadoop.yarn.util.Times;
056import org.apache.hadoop.yarn.webapp.util.WebAppUtils;
057import org.codehaus.jettison.json.JSONArray;
058import org.codehaus.jettison.json.JSONException;
059import org.codehaus.jettison.json.JSONObject;
060
061import com.google.common.annotations.VisibleForTesting;
062import com.sun.jersey.api.client.Client;
063import com.sun.jersey.api.client.ClientHandlerException;
064import com.sun.jersey.api.client.ClientResponse;
065import com.sun.jersey.api.client.UniformInterfaceException;
066import com.sun.jersey.api.client.WebResource;
067import org.w3c.dom.Document;
068import org.w3c.dom.NodeList;
069import org.xml.sax.InputSource;
070
071@Public
072@Evolving
073public class LogsCLI extends Configured implements Tool {
074
075  private static final String CONTAINER_ID_OPTION = "containerId";
076  private static final String APPLICATION_ID_OPTION = "applicationId";
077  private static final String NODE_ADDRESS_OPTION = "nodeAddress";
078  private static final String APP_OWNER_OPTION = "appOwner";
079  private static final String AM_CONTAINER_OPTION = "am";
080  private static final String CONTAINER_LOG_FILES = "logFiles";
081  public static final String HELP_CMD = "help";
082
083  @Override
084  public int run(String[] args) throws Exception {
085
086    Options opts = new Options();
087    opts.addOption(HELP_CMD, false, "Displays help for all commands.");
088    Option appIdOpt =
089        new Option(APPLICATION_ID_OPTION, true, "ApplicationId (required)");
090    appIdOpt.setRequired(true);
091    opts.addOption(appIdOpt);
092    opts.addOption(CONTAINER_ID_OPTION, true, "ContainerId. "
093        + "By default, it will only print syslog if the application is runing."
094        + " Work with -logFiles to get other logs.");
095    opts.addOption(NODE_ADDRESS_OPTION, true, "NodeAddress in the format "
096      + "nodename:port");
097    opts.addOption(APP_OWNER_OPTION, true,
098      "AppOwner (assumed to be current user if not specified)");
099    Option amOption = new Option(AM_CONTAINER_OPTION, true, 
100      "Prints the AM Container logs for this application. "
101      + "Specify comma-separated value to get logs for related AM Container. "
102      + "For example, If we specify -am 1,2, we will get the logs for "
103      + "the first AM Container as well as the second AM Container. "
104      + "To get logs for all AM Containers, use -am ALL. "
105      + "To get logs for the latest AM Container, use -am -1. "
106      + "By default, it will only print out syslog. Work with -logFiles "
107      + "to get other logs");
108    amOption.setValueSeparator(',');
109    amOption.setArgs(Option.UNLIMITED_VALUES);
110    amOption.setArgName("AM Containers");
111    opts.addOption(amOption);
112    Option logFileOpt = new Option(CONTAINER_LOG_FILES, true,
113      "Work with -am/-containerId and specify comma-separated value "
114        + "to get specified container log files. Use \"ALL\" to fetch all the "
115        + "log files for the container.");
116    logFileOpt.setValueSeparator(',');
117    logFileOpt.setArgs(Option.UNLIMITED_VALUES);
118    logFileOpt.setArgName("Log File Name");
119    opts.addOption(logFileOpt);
120
121    opts.getOption(APPLICATION_ID_OPTION).setArgName("Application ID");
122    opts.getOption(CONTAINER_ID_OPTION).setArgName("Container ID");
123    opts.getOption(NODE_ADDRESS_OPTION).setArgName("Node Address");
124    opts.getOption(APP_OWNER_OPTION).setArgName("Application Owner");
125    opts.getOption(AM_CONTAINER_OPTION).setArgName("AM Containers");
126
127    Options printOpts = new Options();
128    printOpts.addOption(opts.getOption(HELP_CMD));
129    printOpts.addOption(opts.getOption(CONTAINER_ID_OPTION));
130    printOpts.addOption(opts.getOption(NODE_ADDRESS_OPTION));
131    printOpts.addOption(opts.getOption(APP_OWNER_OPTION));
132    printOpts.addOption(opts.getOption(AM_CONTAINER_OPTION));
133    printOpts.addOption(opts.getOption(CONTAINER_LOG_FILES));
134
135    if (args.length < 1) {
136      printHelpMessage(printOpts);
137      return -1;
138    }
139    if (args[0].equals("-help")) {
140      printHelpMessage(printOpts);
141      return 0;
142    }
143    CommandLineParser parser = new GnuParser();
144    String appIdStr = null;
145    String containerIdStr = null;
146    String nodeAddress = null;
147    String appOwner = null;
148    boolean getAMContainerLogs = false;
149    String[] logFiles = null;
150    List<String> amContainersList = new ArrayList<String>();
151    try {
152      CommandLine commandLine = parser.parse(opts, args, true);
153      appIdStr = commandLine.getOptionValue(APPLICATION_ID_OPTION);
154      containerIdStr = commandLine.getOptionValue(CONTAINER_ID_OPTION);
155      nodeAddress = commandLine.getOptionValue(NODE_ADDRESS_OPTION);
156      appOwner = commandLine.getOptionValue(APP_OWNER_OPTION);
157      getAMContainerLogs = commandLine.hasOption(AM_CONTAINER_OPTION);
158      if (getAMContainerLogs) {
159        String[] amContainers = commandLine.getOptionValues(AM_CONTAINER_OPTION);
160        for (String am : amContainers) {
161          boolean errorInput = false;
162          if (!am.trim().equalsIgnoreCase("ALL")) {
163            try {
164              int id = Integer.parseInt(am.trim());
165              if (id != -1 && id <= 0) {
166                errorInput = true;
167              }
168            } catch (NumberFormatException ex) {
169              errorInput = true;
170            }
171            if (errorInput) {
172              System.err.println(
173                "Invalid input for option -am. Valid inputs are 'ALL', -1 "
174                + "and any other integer which is larger than 0.");
175              printHelpMessage(printOpts);
176              return -1;
177            }
178            amContainersList.add(am.trim());
179          } else {
180            amContainersList.add("ALL");
181            break;
182          }
183        }
184      }
185      if (commandLine.hasOption(CONTAINER_LOG_FILES)) {
186        logFiles = commandLine.getOptionValues(CONTAINER_LOG_FILES);
187      }
188    } catch (ParseException e) {
189      System.err.println("options parsing failed: " + e.getMessage());
190      printHelpMessage(printOpts);
191      return -1;
192    }
193
194    if (appIdStr == null) {
195      System.err.println("ApplicationId cannot be null!");
196      printHelpMessage(printOpts);
197      return -1;
198    }
199
200    ApplicationId appId = null;
201    try {
202      appId = ConverterUtils.toApplicationId(appIdStr);
203    } catch (Exception e) {
204      System.err.println("Invalid ApplicationId specified");
205      return -1;
206    }
207
208    LogCLIHelpers logCliHelper = new LogCLIHelpers();
209    logCliHelper.setConf(getConf());
210
211    if (appOwner == null || appOwner.isEmpty()) {
212      appOwner = UserGroupInformation.getCurrentUser().getShortUserName();
213    }
214
215    YarnApplicationState appState = YarnApplicationState.NEW;
216    try {
217      appState = getApplicationState(appId);
218      if (appState == YarnApplicationState.NEW
219          || appState == YarnApplicationState.NEW_SAVING
220          || appState == YarnApplicationState.SUBMITTED) {
221        System.out.println("Logs are not avaiable right now.");
222        return -1;
223      }
224    } catch (IOException | YarnException e) {
225      System.err.println("Unable to get ApplicationState."
226          + " Attempting to fetch logs directly from the filesystem.");
227    }
228
229    // To get am logs
230    if (getAMContainerLogs) {
231      // if we do not specify the value for CONTAINER_LOG_FILES option,
232      // we will only output syslog
233      if (logFiles == null || logFiles.length == 0) {
234        logFiles = new String[] { "syslog" };
235      }
236      // If the application is running, we will call the RM WebService
237      // to get the AppAttempts which includes the nodeHttpAddress
238      // and containerId for all the AM Containers.
239      // After that, we will call NodeManager webService to get the
240      // related logs
241      if (appState == YarnApplicationState.ACCEPTED
242          || appState == YarnApplicationState.RUNNING) {
243        return printAMContainerLogs(getConf(), appIdStr, amContainersList,
244          logFiles, logCliHelper, appOwner, false);
245      } else {
246        // If the application is in the final state, we will call RM webservice
247        // to get all AppAttempts information first. If we get nothing,
248        // we will try to call AHS webservice to get related AppAttempts
249        // which includes nodeAddress for the AM Containers.
250        // After that, we will use nodeAddress and containerId
251        // to get logs from HDFS directly.
252        if (getConf().getBoolean(YarnConfiguration.APPLICATION_HISTORY_ENABLED,
253          YarnConfiguration.DEFAULT_APPLICATION_HISTORY_ENABLED)) {
254          return printAMContainerLogs(getConf(), appIdStr, amContainersList,
255            logFiles, logCliHelper, appOwner, true);
256        } else {
257          System.out
258            .println(
259                "Can not get AMContainers logs for the application:" + appId);
260          System.out.println("This application:" + appId + " is finished."
261              + " Please enable the application history service. Or Using "
262              + "yarn logs -applicationId <appId> -containerId <containerId> "
263              + "--nodeAddress <nodeHttpAddress> to get the container logs");
264          return -1;
265        }
266      }
267    }
268
269    int resultCode = 0;
270    if (containerIdStr != null) {
271      // if we provide the node address and the application is in the final
272      // state, we could directly get logs from HDFS.
273      if (nodeAddress != null && isApplicationFinished(appState)) {
274        // if user specified "ALL" as the logFiles param, pass null
275        // to logCliHelper so that it fetches all the logs
276        List<String> logs;
277        if (logFiles == null) {
278          logs = null;
279        } else if (fetchAllLogFiles(logFiles)) {
280          logs = null;
281        } else {
282          logs = Arrays.asList(logFiles);
283        }
284        return logCliHelper.dumpAContainersLogsForALogType(appIdStr,
285            containerIdStr, nodeAddress, appOwner, logs);
286      }
287      try {
288        // If the nodeAddress is not provided, we will try to get
289        // the ContainerReport. In the containerReport, we could get
290        // nodeAddress and nodeHttpAddress
291        ContainerReport report = getContainerReport(containerIdStr);
292        String nodeHttpAddress =
293            report.getNodeHttpAddress().replaceFirst(
294              WebAppUtils.getHttpSchemePrefix(getConf()), "");
295        String nodeId = report.getAssignedNode().toString();
296        // If the application is not in the final state,
297        // we will provide the NodeHttpAddress and get the container logs
298        // by calling NodeManager webservice.
299        if (!isApplicationFinished(appState)) {
300          if (logFiles == null || logFiles.length == 0) {
301            logFiles = new String[] { "syslog" };
302          }
303          printContainerLogsFromRunningApplication(getConf(), appIdStr,
304            containerIdStr, nodeHttpAddress, nodeId, logFiles, logCliHelper,
305            appOwner);
306        } else {
307          String [] requestedLogFiles = logFiles;
308          if(fetchAllLogFiles(logFiles)) {
309            requestedLogFiles = null;
310          }
311          // If the application is in the final state, we will directly
312          // get the container logs from HDFS.
313          printContainerLogsForFinishedApplication(appIdStr, containerIdStr,
314            nodeId, requestedLogFiles, logCliHelper, appOwner);
315        }
316        return resultCode;
317      } catch (IOException | YarnException ex) {
318        System.err.println("Unable to get logs for this container:"
319            + containerIdStr + "for the application:" + appId);
320        if (!getConf().getBoolean(YarnConfiguration.APPLICATION_HISTORY_ENABLED,
321          YarnConfiguration.DEFAULT_APPLICATION_HISTORY_ENABLED)) {
322          System.out.println("Please enable the application history service. Or ");
323        }
324        System.out.println("Using "
325            + "yarn logs -applicationId <appId> -containerId <containerId> "
326            + "--nodeAddress <nodeHttpAddress> to get the container logs");
327        return -1;
328      }
329    } else {
330      if (nodeAddress == null) {
331        resultCode =
332            logCliHelper.dumpAllContainersLogs(appId, appOwner, System.out);
333      } else {
334        System.out.println("Should at least provide ContainerId!");
335        printHelpMessage(printOpts);
336        resultCode = -1;
337      }
338    }
339    return resultCode;
340  }
341
342  private YarnApplicationState getApplicationState(ApplicationId appId)
343      throws IOException, YarnException {
344    YarnClient yarnClient = createYarnClient();
345
346    try {
347      ApplicationReport appReport = yarnClient.getApplicationReport(appId);
348      return appReport.getYarnApplicationState();
349    } finally {
350      yarnClient.close();
351    }
352  }
353  
354  @VisibleForTesting
355  protected YarnClient createYarnClient() {
356    YarnClient yarnClient = YarnClient.createYarnClient();
357    yarnClient.init(getConf());
358    yarnClient.start();
359    return yarnClient;
360  }
361
362  public static void main(String[] args) throws Exception {
363    Configuration conf = new YarnConfiguration();
364    LogsCLI logDumper = new LogsCLI();
365    logDumper.setConf(conf);
366    int exitCode = logDumper.run(args);
367    System.exit(exitCode);
368  }
369
370  private void printHelpMessage(Options options) {
371    System.out.println("Retrieve logs for completed YARN applications.");
372    HelpFormatter formatter = new HelpFormatter();
373    formatter.printHelp("yarn logs -applicationId <application ID> [OPTIONS]", new Options());
374    formatter.setSyntaxPrefix("");
375    formatter.printHelp("general options are:", options);
376  }
377
378  private List<JSONObject> getAMContainerInfoForRMWebService(
379      Configuration conf, String appId) throws ClientHandlerException,
380      UniformInterfaceException, JSONException {
381    Client webServiceClient = Client.create();
382    String webAppAddress =
383        WebAppUtils.getWebAppBindURL(conf, YarnConfiguration.RM_BIND_HOST,
384          WebAppUtils.getRMWebAppURLWithScheme(conf));
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(ConverterUtils
527        .toContainerId(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}