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.File;
022import java.io.IOException;
023import java.io.InputStream;
024import java.io.PrintStream;
025import java.util.ArrayList;
026import java.util.Arrays;
027import java.util.Collection;
028import java.util.Collections;
029import java.util.HashSet;
030import java.util.List;
031import java.util.Map;
032import java.util.Set;
033import java.util.TreeMap;
034import java.util.regex.Pattern;
035
036import javax.ws.rs.core.MediaType;
037
038import org.apache.commons.cli.CommandLine;
039import org.apache.commons.cli.CommandLineParser;
040import org.apache.commons.cli.GnuParser;
041import org.apache.commons.cli.HelpFormatter;
042import org.apache.commons.cli.Option;
043import org.apache.commons.cli.Options;
044import org.apache.commons.cli.ParseException;
045import org.apache.commons.io.IOUtils;
046import org.apache.commons.lang.StringUtils;
047import org.apache.hadoop.classification.InterfaceAudience.Private;
048import org.apache.hadoop.classification.InterfaceAudience.Public;
049import org.apache.hadoop.classification.InterfaceStability.Evolving;
050import org.apache.hadoop.conf.Configuration;
051import org.apache.hadoop.conf.Configured;
052import org.apache.hadoop.security.UserGroupInformation;
053import org.apache.hadoop.util.Tool;
054import org.apache.hadoop.yarn.api.records.ApplicationAttemptReport;
055import org.apache.hadoop.yarn.api.records.ApplicationId;
056import org.apache.hadoop.yarn.api.records.ApplicationReport;
057import org.apache.hadoop.yarn.api.records.ContainerId;
058import org.apache.hadoop.yarn.api.records.ContainerReport;
059import org.apache.hadoop.yarn.api.records.ContainerState;
060import org.apache.hadoop.yarn.api.records.YarnApplicationState;
061import org.apache.hadoop.yarn.client.api.YarnClient;
062import org.apache.hadoop.yarn.conf.YarnConfiguration;
063import org.apache.hadoop.yarn.exceptions.YarnException;
064import org.apache.hadoop.yarn.logaggregation.ContainerLogsRequest;
065import org.apache.hadoop.yarn.logaggregation.LogCLIHelpers;
066import org.apache.hadoop.yarn.util.Times;
067import org.apache.hadoop.yarn.webapp.util.WebAppUtils;
068import org.codehaus.jettison.json.JSONArray;
069import org.codehaus.jettison.json.JSONException;
070import org.codehaus.jettison.json.JSONObject;
071
072import com.google.common.annotations.VisibleForTesting;
073import com.sun.jersey.api.client.Client;
074import com.sun.jersey.api.client.ClientHandlerException;
075import com.sun.jersey.api.client.ClientResponse;
076import com.sun.jersey.api.client.UniformInterfaceException;
077import com.sun.jersey.api.client.WebResource;
078
079@Public
080@Evolving
081public class LogsCLI extends Configured implements Tool {
082
083  private static final String CONTAINER_ID_OPTION = "containerId";
084  private static final String APPLICATION_ID_OPTION = "applicationId";
085  private static final String NODE_ADDRESS_OPTION = "nodeAddress";
086  private static final String APP_OWNER_OPTION = "appOwner";
087  private static final String AM_CONTAINER_OPTION = "am";
088  private static final String PER_CONTAINER_LOG_FILES_OPTION = "log_files";
089  private static final String PER_CONTAINER_LOG_FILES_REGEX_OPTION
090      = "log_files_pattern";
091  private static final String LIST_NODES_OPTION = "list_nodes";
092  private static final String SHOW_APPLICATION_LOG_INFO
093      = "show_application_log_info";
094  private static final String SHOW_CONTAINER_LOG_INFO
095      = "show_container_log_info";
096  private static final String OUT_OPTION = "out";
097  private static final String SIZE_OPTION = "size";
098  public static final String HELP_CMD = "help";
099  private PrintStream outStream = System.out;
100  private YarnClient yarnClient = null;
101
102  @Override
103  public int run(String[] args) throws Exception {
104    try {
105      yarnClient = createYarnClient();
106      return runCommand(args);
107    } finally {
108      if (yarnClient != null) {
109        yarnClient.close();
110      }
111    }
112  }
113
114  private int runCommand(String[] args) throws Exception {
115    Options opts = createCommandOpts();
116    Options printOpts = createPrintOpts(opts);
117    if (args.length < 1) {
118      printHelpMessage(printOpts);
119      return -1;
120    }
121    if (args[0].equals("-help")) {
122      printHelpMessage(printOpts);
123      return 0;
124    }
125    CommandLineParser parser = new GnuParser();
126    String appIdStr = null;
127    String containerIdStr = null;
128    String nodeAddress = null;
129    String appOwner = null;
130    boolean getAMContainerLogs = false;
131    boolean nodesList = false;
132    boolean showApplicationLogInfo = false;
133    boolean showContainerLogInfo = false;
134    boolean useRegex = false;
135    String[] logFiles = null;
136    String[] logFilesRegex = null;
137    List<String> amContainersList = new ArrayList<String>();
138    String localDir = null;
139    long bytes = Long.MAX_VALUE;
140    try {
141      CommandLine commandLine = parser.parse(opts, args, true);
142      appIdStr = commandLine.getOptionValue(APPLICATION_ID_OPTION);
143      containerIdStr = commandLine.getOptionValue(CONTAINER_ID_OPTION);
144      nodeAddress = commandLine.getOptionValue(NODE_ADDRESS_OPTION);
145      appOwner = commandLine.getOptionValue(APP_OWNER_OPTION);
146      getAMContainerLogs = commandLine.hasOption(AM_CONTAINER_OPTION);
147      nodesList = commandLine.hasOption(LIST_NODES_OPTION);
148      localDir = commandLine.getOptionValue(OUT_OPTION);
149      showApplicationLogInfo = commandLine.hasOption(
150          SHOW_APPLICATION_LOG_INFO);
151      showContainerLogInfo = commandLine.hasOption(SHOW_CONTAINER_LOG_INFO);
152      if (getAMContainerLogs) {
153        try {
154          amContainersList = parseAMContainer(commandLine, printOpts);
155        } catch (NumberFormatException ex) {
156          System.err.println(ex.getMessage());
157          return -1;
158        }
159      }
160      if (commandLine.hasOption(PER_CONTAINER_LOG_FILES_OPTION)) {
161        logFiles = commandLine.getOptionValues(PER_CONTAINER_LOG_FILES_OPTION);
162      }
163      if (commandLine.hasOption(PER_CONTAINER_LOG_FILES_REGEX_OPTION)) {
164        logFilesRegex = commandLine.getOptionValues(
165            PER_CONTAINER_LOG_FILES_REGEX_OPTION);
166        useRegex = true;
167      }
168      if (commandLine.hasOption(SIZE_OPTION)) {
169        bytes = Long.parseLong(commandLine.getOptionValue(SIZE_OPTION));
170      }
171    } catch (ParseException e) {
172      System.err.println("options parsing failed: " + e.getMessage());
173      printHelpMessage(printOpts);
174      return -1;
175    }
176
177    if (appIdStr == null && containerIdStr == null) {
178      System.err.println("Both applicationId and containerId are missing, "
179          + " one of them must be specified.");
180      printHelpMessage(printOpts);
181      return -1;
182    }
183
184    ApplicationId appId = null;
185    if (appIdStr != null) {
186      try {
187        appId = ApplicationId.fromString(appIdStr);
188      } catch (Exception e) {
189        System.err.println("Invalid ApplicationId specified");
190        return -1;
191      }
192    }
193
194    if (containerIdStr != null) {
195      try {
196        ContainerId containerId = ContainerId.fromString(containerIdStr);
197        if (appId == null) {
198          appId = containerId.getApplicationAttemptId().getApplicationId();
199        } else if (!containerId.getApplicationAttemptId().getApplicationId()
200            .equals(appId)) {
201          System.err.println("The Application:" + appId
202              + " does not have the container:" + containerId);
203          return -1;
204        }
205      } catch (Exception e) {
206        System.err.println("Invalid ContainerId specified");
207        return -1;
208      }
209    }
210
211    if (showApplicationLogInfo && showContainerLogInfo) {
212      System.err.println("Invalid options. Can only accept one of "
213          + "show_application_log_info/show_container_log_info.");
214      return -1;
215    }
216
217    if (logFiles != null && logFiles.length > 0 && logFilesRegex != null
218        && logFilesRegex.length > 0) {
219      System.err.println("Invalid options. Can only accept one of "
220          + "log_files/log_files_pattern.");
221      return -1;
222    }
223    if (localDir != null) {
224      File file = new File(localDir);
225      if (file.exists() && file.isFile()) {
226        System.err.println("Invalid value for -out option. "
227            + "Please provide a directory.");
228        return -1;
229      }
230    }
231
232    LogCLIHelpers logCliHelper = new LogCLIHelpers();
233    logCliHelper.setConf(getConf());
234
235    YarnApplicationState appState = YarnApplicationState.NEW;
236    ApplicationReport appReport = null;
237    try {
238      appReport = getApplicationReport(appId);
239      appState = appReport.getYarnApplicationState();
240      if (appState == YarnApplicationState.NEW
241          || appState == YarnApplicationState.NEW_SAVING
242          || appState == YarnApplicationState.SUBMITTED) {
243        System.err.println("Logs are not avaiable right now.");
244        return -1;
245      }
246    } catch (IOException | YarnException e) {
247      // If we can not get appReport from either RM or ATS
248      // We will assume that this app has already finished.
249      appState = YarnApplicationState.FINISHED;
250      System.err.println("Unable to get ApplicationState."
251          + " Attempting to fetch logs directly from the filesystem.");
252    }
253
254    if (appOwner == null || appOwner.isEmpty()) {
255      appOwner = guessAppOwner(appReport, appId);
256      if (appOwner == null) {
257        System.err.println("Can not find the appOwner. "
258            + "Please specify the correct appOwner");
259        System.err.println("Could not locate application logs for " + appId);
260        return -1;
261      }
262    }
263
264    Set<String> logs = new HashSet<String>();
265    if (fetchAllLogFiles(logFiles, logFilesRegex)) {
266      logs.add("ALL");
267    } else if (logFiles != null && logFiles.length > 0) {
268      logs.addAll(Arrays.asList(logFiles));
269    } else if (logFilesRegex != null && logFilesRegex.length > 0) {
270      logs.addAll(Arrays.asList(logFilesRegex));
271    }
272
273    ContainerLogsRequest request = new ContainerLogsRequest(appId,
274        isApplicationFinished(appState), appOwner, nodeAddress, null,
275        containerIdStr, localDir, logs, bytes, null);
276
277    if (showContainerLogInfo) {
278      return showContainerLogInfo(request, logCliHelper);
279    }
280
281    if (nodesList) {
282      return showNodeLists(request, logCliHelper);
283    }
284
285    if (showApplicationLogInfo) {
286      return showApplicationLogInfo(request, logCliHelper);
287    }
288    // To get am logs
289    if (getAMContainerLogs) {
290      return fetchAMContainerLogs(request, amContainersList,
291          logCliHelper, useRegex);
292    }
293
294    int resultCode = 0;
295    if (containerIdStr != null) {
296      return fetchContainerLogs(request, logCliHelper, useRegex);
297    } else {
298      if (nodeAddress == null) {
299        resultCode = fetchApplicationLogs(request, logCliHelper, useRegex);
300      } else {
301        System.err.println("Should at least provide ContainerId!");
302        printHelpMessage(printOpts);
303        resultCode = -1;
304      }
305    }
306    return resultCode;
307  }
308
309  private ApplicationReport getApplicationReport(ApplicationId appId)
310      throws IOException, YarnException {
311    return yarnClient.getApplicationReport(appId);
312  }
313  
314  @VisibleForTesting
315  protected YarnClient createYarnClient() {
316    YarnClient client = YarnClient.createYarnClient();
317    client.init(getConf());
318    client.start();
319    return client;
320  }
321
322  public static void main(String[] args) throws Exception {
323    Configuration conf = new YarnConfiguration();
324    LogsCLI logDumper = new LogsCLI();
325    logDumper.setConf(conf);
326    int exitCode = logDumper.run(args);
327    System.exit(exitCode);
328  }
329
330  private void printHelpMessage(Options options) {
331    outStream.println("Retrieve logs for YARN applications.");
332    HelpFormatter formatter = new HelpFormatter();
333    formatter.printHelp("yarn logs -applicationId <application ID> [OPTIONS]",
334        new Options());
335    formatter.setSyntaxPrefix("");
336    formatter.printHelp("general options are:", options);
337  }
338
339  protected List<JSONObject> getAMContainerInfoForRMWebService(
340      Configuration conf, String appId) throws ClientHandlerException,
341      UniformInterfaceException, JSONException {
342    Client webServiceClient = Client.create();
343    String webAppAddress = WebAppUtils.getRMWebAppURLWithScheme(conf);
344
345    WebResource webResource = webServiceClient.resource(webAppAddress);
346
347    ClientResponse response =
348        webResource.path("ws").path("v1").path("cluster").path("apps")
349          .path(appId).path("appattempts").accept(MediaType.APPLICATION_JSON)
350          .get(ClientResponse.class);
351    JSONObject json =
352        response.getEntity(JSONObject.class).getJSONObject("appAttempts");
353    JSONArray requests = json.getJSONArray("appAttempt");
354    List<JSONObject> amContainersList = new ArrayList<JSONObject>();
355    for (int i = 0; i < requests.length(); i++) {
356      amContainersList.add(requests.getJSONObject(i));
357    }
358    return amContainersList;
359  }
360
361  private List<JSONObject> getAMContainerInfoForAHSWebService(
362      Configuration conf, String appId) throws ClientHandlerException,
363      UniformInterfaceException, JSONException {
364    Client webServiceClient = Client.create();
365    String webAppAddress =
366        WebAppUtils.getHttpSchemePrefix(conf)
367            + WebAppUtils.getAHSWebAppURLWithoutScheme(conf);
368    WebResource webResource = webServiceClient.resource(webAppAddress);
369
370    ClientResponse response =
371        webResource.path("ws").path("v1").path("applicationhistory")
372          .path("apps").path(appId).path("appattempts")
373          .accept(MediaType.APPLICATION_JSON)
374          .get(ClientResponse.class);
375    JSONObject json = response.getEntity(JSONObject.class);
376    JSONArray requests = json.getJSONArray("appAttempt");
377    List<JSONObject> amContainersList = new ArrayList<JSONObject>();
378    for (int i = 0; i < requests.length(); i++) {
379      amContainersList.add(requests.getJSONObject(i));
380    }
381    Collections.reverse(amContainersList);
382    return amContainersList;
383  }
384
385  private boolean fetchAllLogFiles(String[] logFiles, String[] logFilesRegex) {
386
387    // If no value is specified for the PER_CONTAINER_LOG_FILES_OPTION option
388    // and PER_CONTAINER_LOG_FILES_REGEX_OPTION
389    // we will assume all logs.
390    if ((logFiles == null || logFiles.length == 0) && (
391        logFilesRegex == null || logFilesRegex.length == 0)) {
392      return true;
393    }
394
395    if (logFiles != null && logFiles.length > 0) {
396      List<String> logs = Arrays.asList(logFiles);
397      if (logs.contains("ALL") || logs.contains("*")) {
398        return true;
399      }
400    }
401
402    if (logFilesRegex != null && logFilesRegex.length > 0) {
403      List<String> logsRegex = Arrays.asList(logFilesRegex);
404      if (logsRegex.contains(".*")) {
405        return true;
406      }
407    }
408
409    return false;
410  }
411
412  private List<PerLogFileInfo> getContainerLogFiles(Configuration conf,
413      String containerIdStr, String nodeHttpAddress) throws IOException {
414    List<PerLogFileInfo> logFileInfos = new ArrayList<>();
415    Client webServiceClient = Client.create();
416    try {
417      WebResource webResource = webServiceClient
418          .resource(WebAppUtils.getHttpSchemePrefix(conf) + nodeHttpAddress);
419      ClientResponse response =
420          webResource.path("ws").path("v1").path("node").path("containers")
421              .path(containerIdStr).path("logs")
422              .accept(MediaType.APPLICATION_JSON)
423              .get(ClientResponse.class);
424      if (response.getStatusInfo().getStatusCode() ==
425          ClientResponse.Status.OK.getStatusCode()) {
426        try {
427          JSONObject json = response.getEntity(JSONObject.class);
428          JSONArray array = json.getJSONArray("containerLogInfo");
429          for (int i = 0; i < array.length(); i++) {
430            String fileName = array.getJSONObject(i).getString("fileName");
431            String fileSize = array.getJSONObject(i).getString("fileSize");
432            logFileInfos.add(new PerLogFileInfo(fileName, fileSize));
433          }
434        } catch (Exception e) {
435          System.err.println("Unable to parse json from webservice. Error:");
436          System.err.println(e.getMessage());
437          throw new IOException(e);
438        }
439      }
440
441    } catch (ClientHandlerException | UniformInterfaceException ex) {
442      System.err.println("Unable to fetch log files list");
443      throw new IOException(ex);
444    }
445    return logFileInfos;
446  }
447
448  @Private
449  @VisibleForTesting
450  public int printContainerLogsFromRunningApplication(Configuration conf,
451      ContainerLogsRequest request, LogCLIHelpers logCliHelper,
452      boolean useRegex) throws IOException {
453    String containerIdStr = request.getContainerId().toString();
454    String localDir = request.getOutputLocalDir();
455    String nodeHttpAddress = request.getNodeHttpAddress();
456    if (nodeHttpAddress == null || nodeHttpAddress.isEmpty()) {
457      System.err.println("Can not get the logs for the container: "
458          + containerIdStr);
459      System.err.println("The node http address is required to get container "
460          + "logs for the Running application.");
461      return -1;
462    }
463    String nodeId = request.getNodeId();
464    PrintStream out = logCliHelper.createPrintStream(localDir, nodeId,
465        containerIdStr);
466    try {
467      Set<String> matchedFiles = getMatchedContainerLogFiles(request,
468          useRegex);
469      if (matchedFiles.isEmpty()) {
470        System.err.println("Can not find any log file matching the pattern: "
471            + request.getLogTypes() + " for the container: " + containerIdStr
472            + " within the application: " + request.getAppId());
473        return -1;
474      }
475      ContainerLogsRequest newOptions = new ContainerLogsRequest(request);
476      newOptions.setLogTypes(matchedFiles);
477
478      Client webServiceClient = Client.create();
479      String containerString = String.format(
480          LogCLIHelpers.CONTAINER_ON_NODE_PATTERN, containerIdStr, nodeId);
481      out.println(containerString);
482      out.println(StringUtils.repeat("=", containerString.length()));
483      boolean foundAnyLogs = false;
484      byte[] buffer = new byte[65536];
485      for (String logFile : newOptions.getLogTypes()) {
486        out.println("LogType:" + logFile);
487        out.println("Log Upload Time:"
488            + Times.format(System.currentTimeMillis()));
489        out.println("Log Contents:");
490        InputStream is = null;
491        try {
492          ClientResponse response = getResponeFromNMWebService(conf,
493              webServiceClient, request, logFile);
494          if (response != null && response.getStatusInfo().getStatusCode() ==
495              ClientResponse.Status.OK.getStatusCode()) {
496            is = response.getEntityInputStream();
497            int len = 0;
498            while((len = is.read(buffer)) != -1) {
499              out.write(buffer, 0, len);
500            }
501            out.println();
502          } else {
503            out.println("Can not get any logs for the log file: " + logFile);
504            String msg = "Response from the NodeManager:" + nodeId +
505                " WebService is " + ((response == null) ? "null":
506                "not successful," + " HTTP error code: " +
507                response.getStatus() + ", Server response:\n" +
508                response.getEntity(String.class));
509            out.println(msg);
510          }
511          StringBuilder sb = new StringBuilder();
512          sb.append("End of LogType:" + logFile + ".");
513          if (request.getContainerState() == ContainerState.RUNNING) {
514            sb.append(" This log file belongs"
515                + " to a running container (" + containerIdStr + ") and so may"
516                + " not be complete.");
517          }
518          out.println(sb.toString());
519          out.flush();
520          foundAnyLogs = true;
521        } catch (ClientHandlerException | UniformInterfaceException ex) {
522          System.err.println("Can not find the log file:" + logFile
523              + " for the container:" + containerIdStr + " in NodeManager:"
524              + nodeId);
525        } finally {
526          IOUtils.closeQuietly(is);
527        }
528      }
529      // for the case, we have already uploaded partial logs in HDFS
530      int result = logCliHelper.dumpAContainerLogsForLogType(
531          newOptions, false);
532      if (result == 0 || foundAnyLogs) {
533        return 0;
534      } else {
535        return -1;
536      }
537    } finally {
538      logCliHelper.closePrintStream(out);
539    }
540  }
541
542  private int printContainerLogsForFinishedApplication(
543      ContainerLogsRequest request, LogCLIHelpers logCliHelper,
544      boolean useRegex) throws IOException {
545    ContainerLogsRequest newOptions = getMatchedLogOptions(
546        request, logCliHelper, useRegex);
547    if (newOptions == null) {
548      System.err.println("Can not find any log file matching the pattern: "
549          + request.getLogTypes() + " for the container: "
550          + request.getContainerId() + " within the application: "
551          + request.getAppId());
552      return -1;
553    }
554    return logCliHelper.dumpAContainerLogsForLogType(newOptions);
555  }
556
557  private int printContainerLogsForFinishedApplicationWithoutNodeId(
558      ContainerLogsRequest request, LogCLIHelpers logCliHelper,
559      boolean useRegex) throws IOException {
560    ContainerLogsRequest newOptions = getMatchedLogOptions(
561        request, logCliHelper, useRegex);
562    if (newOptions == null) {
563      System.err.println("Can not find any log file matching the pattern: "
564          + request.getLogTypes() + " for the container: "
565          + request.getContainerId() + " within the application: "
566          + request.getAppId());
567      return -1;
568    }
569    return logCliHelper.dumpAContainerLogsForLogTypeWithoutNodeId(
570        newOptions);
571  }
572
573  @Private
574  @VisibleForTesting
575  public ContainerReport getContainerReport(String containerIdStr)
576      throws YarnException, IOException {
577    return yarnClient.getContainerReport(
578        ContainerId.fromString(containerIdStr));
579  }
580
581  private boolean isApplicationFinished(YarnApplicationState appState) {
582    return appState == YarnApplicationState.FINISHED
583        || appState == YarnApplicationState.FAILED
584        || appState == YarnApplicationState.KILLED; 
585  }
586
587  private int printAMContainerLogs(Configuration conf,
588      ContainerLogsRequest request, List<String> amContainers,
589      LogCLIHelpers logCliHelper, boolean useRegex) throws Exception {
590    List<JSONObject> amContainersList = null;
591    List<ContainerLogsRequest> requests =
592        new ArrayList<ContainerLogsRequest>();
593    boolean getAMContainerLists = false;
594    String appId = request.getAppId().toString();
595    StringBuilder errorMessage = new StringBuilder();
596    // We will call RM webservice to get all AppAttempts information.
597    // If we get nothing, we will try to call AHS webservice to get AppAttempts
598    // which includes nodeAddress for the AM Containers.
599    try {
600      amContainersList = getAMContainerInfoForRMWebService(conf, appId);
601      if (amContainersList != null && !amContainersList.isEmpty()) {
602        getAMContainerLists = true;
603        for (JSONObject amContainer : amContainersList) {
604          ContainerLogsRequest amRequest = new ContainerLogsRequest(request);
605          amRequest.setContainerId(amContainer.getString("containerId"));
606          String httpAddress = amContainer.getString("nodeHttpAddress");
607          if (httpAddress != null && !httpAddress.isEmpty()) {
608            amRequest.setNodeHttpAddress(httpAddress);
609          }
610          amRequest.setNodeId(amContainer.getString("nodeId"));
611          requests.add(amRequest);
612        }
613      }
614    } catch (Exception ex) {
615      errorMessage.append(ex.getMessage() + "\n");
616      if (request.isAppFinished()) {
617        if (!conf.getBoolean(YarnConfiguration.TIMELINE_SERVICE_ENABLED,
618            YarnConfiguration.DEFAULT_TIMELINE_SERVICE_ENABLED)) {
619          errorMessage.append("Please enable the timeline service "
620              + "and make sure the timeline server is running.");
621        } else {
622          try {
623            amContainersList = getAMContainerInfoForAHSWebService(conf, appId);
624            if (amContainersList != null && !amContainersList.isEmpty()) {
625              getAMContainerLists = true;
626              for (JSONObject amContainer : amContainersList) {
627                ContainerLogsRequest amRequest = new ContainerLogsRequest(
628                    request);
629                amRequest.setContainerId(
630                    amContainer.getString("amContainerId"));
631                requests.add(amRequest);
632              }
633            }
634          } catch (Exception e) {
635            errorMessage.append(e.getMessage());
636          }
637        }
638      }
639    }
640
641    if (!getAMContainerLists) {
642      System.err.println("Unable to get AM container informations "
643          + "for the application:" + appId);
644      System.err.println(errorMessage);
645      System.err.println("Can not get AMContainers logs for "
646          + "the application:" + appId + " with the appOwner:"
647          + request.getAppOwner());
648      return -1;
649    }
650
651    if (amContainers.contains("ALL")) {
652      for (ContainerLogsRequest amRequest : requests) {
653        outputAMContainerLogs(amRequest, conf, logCliHelper, useRegex);
654      }
655      outStream.println();
656      outStream.println("Specified ALL for -am option. "
657          + "Printed logs for all am containers.");
658    } else {
659      for (String amContainer : amContainers) {
660        int amContainerId = Integer.parseInt(amContainer.trim());
661        if (amContainerId == -1) {
662          outputAMContainerLogs(requests.get(requests.size() - 1), conf,
663              logCliHelper, useRegex);
664        } else {
665          if (amContainerId <= requests.size()) {
666            outputAMContainerLogs(requests.get(amContainerId - 1), conf,
667                logCliHelper, useRegex);
668          } else {
669            System.err.println(String.format("ERROR: Specified AM containerId"
670                + " (%s) exceeds the number of AM containers (%s).",
671                amContainerId, requests.size()));
672            return -1;
673          }
674        }
675      }
676    }
677    return 0;
678  }
679
680  private void outputAMContainerLogs(ContainerLogsRequest request,
681      Configuration conf, LogCLIHelpers logCliHelper, boolean useRegex)
682      throws Exception {
683    String nodeHttpAddress = request.getNodeHttpAddress();
684    String containerId = request.getContainerId();
685    String nodeId = request.getNodeId();
686
687    if (request.isAppFinished()) {
688      if (containerId != null && !containerId.isEmpty()) {
689        if (nodeId != null && !nodeId.isEmpty()) {
690          printContainerLogsForFinishedApplication(request,
691              logCliHelper, useRegex);
692        } else {
693          printContainerLogsForFinishedApplicationWithoutNodeId(
694              request, logCliHelper, useRegex);
695        }
696      }
697    } else {
698      if (nodeHttpAddress != null && containerId != null
699          && !nodeHttpAddress.isEmpty() && !containerId.isEmpty()) {
700        ContainerState containerState = getContainerReport(containerId)
701            .getContainerState();
702        request.setContainerState(containerState);
703        printContainerLogsFromRunningApplication(conf,
704            request, logCliHelper, useRegex);
705      }
706    }
707  }
708
709  private int showContainerLogInfo(ContainerLogsRequest request,
710      LogCLIHelpers logCliHelper) throws IOException, YarnException {
711    if (!request.isAppFinished()) {
712      return printContainerInfoFromRunningApplication(request);
713    } else {
714      return logCliHelper.printAContainerLogMetadata(
715          request, System.out, System.err);
716    }
717  }
718
719  private int showNodeLists(ContainerLogsRequest request,
720      LogCLIHelpers logCliHelper) throws IOException {
721    if (!request.isAppFinished()) {
722      System.err.println("The -list_nodes command can be only used with "
723          + "finished applications");
724      return -1;
725    } else {
726      logCliHelper.printNodesList(request, System.out, System.err);
727      return 0;
728    }
729  }
730
731  private int showApplicationLogInfo(ContainerLogsRequest request,
732      LogCLIHelpers logCliHelper) throws IOException, YarnException {
733    String appState = "Application State: "
734        + (request.isAppFinished() ? "Completed." : "Running.");
735    if (!request.isAppFinished()) {
736      List<ContainerReport> reports =
737          getContainerReportsFromRunningApplication(request);
738      List<ContainerReport> filterReports = filterContainersInfo(
739          request, reports);
740      if (filterReports.isEmpty()) {
741        System.err.println("Can not find any containers for the application:"
742            + request.getAppId() + ".");
743        return -1;
744      }
745      outStream.println(appState);
746      for (ContainerReport report : filterReports) {
747        outStream.println(String.format(LogCLIHelpers.CONTAINER_ON_NODE_PATTERN,
748            report.getContainerId(), report.getAssignedNode()));
749      }
750      return 0;
751    } else {
752      outStream.println(appState);
753      logCliHelper.printContainersList(request, System.out, System.err);
754      return 0;
755    }
756  }
757
758  private Options createCommandOpts() {
759    Options opts = new Options();
760    opts.addOption(HELP_CMD, false, "Displays help for all commands.");
761    Option appIdOpt =
762        new Option(APPLICATION_ID_OPTION, true, "ApplicationId (required)");
763    opts.addOption(appIdOpt);
764    opts.addOption(CONTAINER_ID_OPTION, true, "ContainerId. "
765        + "By default, it will print all available logs."
766        + " Work with -log_files to get only specific logs. If specified, the"
767        + " applicationId can be omitted");
768    opts.addOption(NODE_ADDRESS_OPTION, true, "NodeAddress in the format "
769        + "nodename:port");
770    opts.addOption(APP_OWNER_OPTION, true,
771        "AppOwner (assumed to be current user if not specified)");
772    Option amOption = new Option(AM_CONTAINER_OPTION, true,
773        "Prints the AM Container logs for this application. "
774        + "Specify comma-separated value to get logs for related AM "
775        + "Container. For example, If we specify -am 1,2, we will get "
776        + "the logs for the first AM Container as well as the second "
777        + "AM Container. To get logs for all AM Containers, use -am ALL. "
778        + "To get logs for the latest AM Container, use -am -1. "
779        + "By default, it will print all available logs. Work with -log_files "
780        + "to get only specific logs.");
781    amOption.setValueSeparator(',');
782    amOption.setArgs(Option.UNLIMITED_VALUES);
783    amOption.setArgName("AM Containers");
784    opts.addOption(amOption);
785    Option logFileOpt = new Option(PER_CONTAINER_LOG_FILES_OPTION, true,
786        "Specify comma-separated value "
787        + "to get exact matched log files. Use \"ALL\" or \"*\" to "
788        + "fetch all the log files for the container.");
789    logFileOpt.setValueSeparator(',');
790    logFileOpt.setArgs(Option.UNLIMITED_VALUES);
791    logFileOpt.setArgName("Log File Name");
792    opts.addOption(logFileOpt);
793    Option logFileRegexOpt = new Option(PER_CONTAINER_LOG_FILES_REGEX_OPTION,
794        true, "Specify comma-separated value "
795        + "to get matched log files by using java regex. Use \".*\" to "
796        + "fetch all the log files for the container.");
797    logFileRegexOpt.setValueSeparator(',');
798    logFileRegexOpt.setArgs(Option.UNLIMITED_VALUES);
799    logFileRegexOpt.setArgName("Log File Pattern");
800    opts.addOption(logFileRegexOpt);
801    opts.addOption(SHOW_CONTAINER_LOG_INFO, false,
802        "Show the container log metadata, "
803        + "including log-file names, the size of the log files. "
804        + "You can combine this with --containerId to get log metadata for "
805        + "the specific container, or with --nodeAddress to get log metadata "
806        + "for all the containers on the specific NodeManager.");
807    opts.addOption(SHOW_APPLICATION_LOG_INFO, false, "Show the "
808        + "containerIds which belong to the specific Application. "
809        + "You can combine this with --nodeAddress to get containerIds "
810        + "for all the containers on the specific NodeManager.");
811    opts.addOption(LIST_NODES_OPTION, false,
812        "Show the list of nodes that successfully aggregated logs. "
813        + "This option can only be used with finished applications.");
814    opts.addOption(OUT_OPTION, true, "Local directory for storing individual "
815        + "container logs. The container logs will be stored based on the "
816        + "node the container ran on.");
817    opts.addOption(SIZE_OPTION, true, "Prints the log file's first 'n' bytes "
818        + "or the last 'n' bytes. Use negative values as bytes to read from "
819        + "the end and positive values as bytes to read from the beginning.");
820    opts.getOption(APPLICATION_ID_OPTION).setArgName("Application ID");
821    opts.getOption(CONTAINER_ID_OPTION).setArgName("Container ID");
822    opts.getOption(NODE_ADDRESS_OPTION).setArgName("Node Address");
823    opts.getOption(APP_OWNER_OPTION).setArgName("Application Owner");
824    opts.getOption(AM_CONTAINER_OPTION).setArgName("AM Containers");
825    opts.getOption(OUT_OPTION).setArgName("Local Directory");
826    opts.getOption(SIZE_OPTION).setArgName("size");
827    return opts;
828  }
829
830  private Options createPrintOpts(Options commandOpts) {
831    Options printOpts = new Options();
832    printOpts.addOption(commandOpts.getOption(HELP_CMD));
833    printOpts.addOption(commandOpts.getOption(CONTAINER_ID_OPTION));
834    printOpts.addOption(commandOpts.getOption(NODE_ADDRESS_OPTION));
835    printOpts.addOption(commandOpts.getOption(APP_OWNER_OPTION));
836    printOpts.addOption(commandOpts.getOption(AM_CONTAINER_OPTION));
837    printOpts.addOption(commandOpts.getOption(PER_CONTAINER_LOG_FILES_OPTION));
838    printOpts.addOption(commandOpts.getOption(LIST_NODES_OPTION));
839    printOpts.addOption(commandOpts.getOption(SHOW_APPLICATION_LOG_INFO));
840    printOpts.addOption(commandOpts.getOption(SHOW_CONTAINER_LOG_INFO));
841    printOpts.addOption(commandOpts.getOption(OUT_OPTION));
842    printOpts.addOption(commandOpts.getOption(SIZE_OPTION));
843    printOpts.addOption(commandOpts.getOption(
844        PER_CONTAINER_LOG_FILES_REGEX_OPTION));
845    return printOpts;
846  }
847
848  private List<String> parseAMContainer(CommandLine commandLine,
849      Options printOpts) throws NumberFormatException {
850    List<String> amContainersList = new ArrayList<String>();
851    String[] amContainers = commandLine.getOptionValues(AM_CONTAINER_OPTION);
852    for (String am : amContainers) {
853      boolean errorInput = false;
854      if (!am.trim().equalsIgnoreCase("ALL")) {
855        try {
856          int id = Integer.parseInt(am.trim());
857          if (id != -1 && id <= 0) {
858            errorInput = true;
859          }
860        } catch (NumberFormatException ex) {
861          errorInput = true;
862        }
863        if (errorInput) {
864          String errMessage =
865              "Invalid input for option -am. Valid inputs are 'ALL', -1 "
866              + "and any other integer which is larger than 0.";
867          printHelpMessage(printOpts);
868          throw new NumberFormatException(errMessage);
869        }
870        amContainersList.add(am.trim());
871      } else {
872        amContainersList.add("ALL");
873        break;
874      }
875    }
876    return amContainersList;
877  }
878
879  private int fetchAMContainerLogs(ContainerLogsRequest request,
880      List<String> amContainersList, LogCLIHelpers logCliHelper,
881      boolean useRegex) throws Exception {
882    return printAMContainerLogs(getConf(), request, amContainersList,
883        logCliHelper, useRegex);
884  }
885
886  private int fetchContainerLogs(ContainerLogsRequest request,
887      LogCLIHelpers logCliHelper, boolean useRegex) throws IOException {
888    int resultCode = 0;
889    String appIdStr = request.getAppId().toString();
890    String containerIdStr = request.getContainerId();
891    String nodeAddress = request.getNodeId();
892    String appOwner = request.getAppOwner();
893    boolean isAppFinished = request.isAppFinished();
894    // if the application is in the final state,
895    // we could directly get logs from HDFS.
896    if (isAppFinished) {
897      // if user specified "ALL" as the logFiles param, pass empty list
898      // to logCliHelper so that it fetches all the logs
899      if (nodeAddress != null && !nodeAddress.isEmpty()) {
900        return printContainerLogsForFinishedApplication(
901            request, logCliHelper, useRegex);
902      } else {
903        return printContainerLogsForFinishedApplicationWithoutNodeId(
904            request, logCliHelper, useRegex);
905      }
906    }
907    String nodeHttpAddress = null;
908    String nodeId = null;
909    try {
910      // If the nodeAddress is not provided, we will try to get
911      // the ContainerReport. In the containerReport, we could get
912      // nodeAddress and nodeHttpAddress
913      ContainerReport report = getContainerReport(containerIdStr);
914      nodeHttpAddress = report.getNodeHttpAddress();
915      if (nodeHttpAddress != null && !nodeHttpAddress.isEmpty()) {
916        nodeHttpAddress = nodeHttpAddress.replaceFirst(
917                WebAppUtils.getHttpSchemePrefix(getConf()), "");
918        request.setNodeHttpAddress(nodeHttpAddress);
919      }
920      nodeId = report.getAssignedNode().toString();
921      request.setNodeId(nodeId);
922      request.setContainerState(report.getContainerState());
923    } catch (IOException | YarnException ex) {
924      if (isAppFinished) {
925        return printContainerLogsForFinishedApplicationWithoutNodeId(
926            request, logCliHelper, useRegex);
927      } else {
928        System.err.println("Unable to get logs for this container:"
929            + containerIdStr + "for the application:" + appIdStr
930            + " with the appOwner: " + appOwner);
931        System.err.println("The application: " + appIdStr
932            + " is still running, and we can not get Container report "
933            + "for the container: " + containerIdStr +". Please try later "
934            + "or after the application finishes.");
935        return -1;
936      }
937    }
938    // If the application is not in the final state,
939    // we will provide the NodeHttpAddress and get the container logs
940    // by calling NodeManager webservice.
941    if (!isAppFinished) {
942      resultCode = printContainerLogsFromRunningApplication(getConf(), request,
943          logCliHelper, useRegex);
944    } else {
945      // If the application is in the final state, we will directly
946      // get the container logs from HDFS.
947      resultCode = printContainerLogsForFinishedApplication(
948          request, logCliHelper, useRegex);
949    }
950    return resultCode;
951  }
952
953  private int fetchApplicationLogs(ContainerLogsRequest options,
954      LogCLIHelpers logCliHelper, boolean useRegex) throws IOException,
955      YarnException {
956    // If the application has finished, we would fetch the logs
957    // from HDFS.
958    // If the application is still running, we would get the full
959    // list of the containers first, then fetch the logs for each
960    // container from NM.
961    int resultCode = -1;
962    if (options.isAppFinished()) {
963      ContainerLogsRequest newOptions = getMatchedLogOptions(
964          options, logCliHelper, useRegex);
965      if (newOptions == null) {
966        System.err.println("Can not find any log file matching the pattern: "
967            + options.getLogTypes() + " for the application: "
968            + options.getAppId());
969      } else {
970        resultCode =
971            logCliHelper.dumpAllContainersLogs(newOptions);
972      }
973    } else {
974      List<ContainerLogsRequest> containerLogRequests =
975          getContainersLogRequestForRunningApplication(options);
976      for (ContainerLogsRequest container : containerLogRequests) {
977        int result = printContainerLogsFromRunningApplication(getConf(),
978            container, logCliHelper, useRegex);
979        if (result == 0) {
980          resultCode = 0;
981        }
982      }
983    }
984    if (resultCode == -1) {
985      System.err.println("Can not find the logs for the application: "
986          + options.getAppId() + " with the appOwner: "
987          + options.getAppOwner());
988    }
989    return resultCode;
990  }
991
992  private String guessAppOwner(ApplicationReport appReport,
993      ApplicationId appId) throws IOException {
994    String appOwner = null;
995    if (appReport != null) {
996      //always use the app owner from the app report if possible
997      appOwner = appReport.getUser();
998    } else {
999      appOwner = UserGroupInformation.getCurrentUser().getShortUserName();
1000      appOwner = LogCLIHelpers.getOwnerForAppIdOrNull(
1001          appId, appOwner, getConf());
1002    }
1003    return appOwner;
1004  }
1005
1006  private ContainerLogsRequest getMatchedLogOptions(
1007      ContainerLogsRequest request, LogCLIHelpers logCliHelper,
1008      boolean useRegex) throws IOException {
1009    ContainerLogsRequest newOptions = new ContainerLogsRequest(request);
1010    if (request.getLogTypes() != null && !request.getLogTypes().isEmpty()) {
1011      Set<String> matchedFiles = new HashSet<String>();
1012      if (!request.getLogTypes().contains("ALL")) {
1013        Set<String> files = logCliHelper.listContainerLogs(request);
1014        matchedFiles = getMatchedLogFiles(request, files, useRegex);
1015        if (matchedFiles.isEmpty()) {
1016          return null;
1017        }
1018      }
1019      newOptions.setLogTypes(matchedFiles);
1020    }
1021    return newOptions;
1022  }
1023
1024  private Set<String> getMatchedLogFiles(ContainerLogsRequest options,
1025      Collection<String> candidate, boolean useRegex) throws IOException {
1026    Set<String> matchedFiles = new HashSet<String>();
1027    Set<String> filePattern = options.getLogTypes();
1028    if (options.getLogTypes().contains("ALL")) {
1029      return new HashSet<String>(candidate);
1030    }
1031    for (String file : candidate) {
1032      if (useRegex) {
1033        if (isFileMatching(file, filePattern)) {
1034          matchedFiles.add(file);
1035        }
1036      } else {
1037        if (filePattern.contains(file)) {
1038          matchedFiles.add(file);
1039        }
1040      }
1041    }
1042    return matchedFiles;
1043  }
1044
1045  private boolean isFileMatching(String fileType,
1046      Set<String> logTypes) {
1047    for (String logType : logTypes) {
1048      Pattern filterPattern = Pattern.compile(logType);
1049      boolean match = filterPattern.matcher(fileType).find();
1050      if (match) {
1051        return true;
1052      }
1053    }
1054    return false;
1055  }
1056
1057  private List<ContainerLogsRequest>
1058      getContainersLogRequestForRunningApplication(
1059          ContainerLogsRequest options) throws YarnException, IOException {
1060    List<ContainerLogsRequest> newOptionsList =
1061        new ArrayList<ContainerLogsRequest>();
1062    List<ContainerReport> reports =
1063        getContainerReportsFromRunningApplication(options);
1064    for (ContainerReport container : reports) {
1065      ContainerLogsRequest newOptions = new ContainerLogsRequest(options);
1066      newOptions.setContainerId(container.getContainerId().toString());
1067      newOptions.setNodeId(container.getAssignedNode().toString());
1068      String httpAddress = container.getNodeHttpAddress();
1069      if (httpAddress != null && !httpAddress.isEmpty()) {
1070        newOptions.setNodeHttpAddress(httpAddress
1071            .replaceFirst(WebAppUtils.getHttpSchemePrefix(getConf()), ""));
1072      }
1073      newOptions.setContainerState(container.getContainerState());
1074      newOptionsList.add(newOptions);
1075    }
1076    return newOptionsList;
1077  }
1078
1079  private List<ContainerReport> getContainerReportsFromRunningApplication(
1080      ContainerLogsRequest options) throws YarnException, IOException {
1081    List<ContainerReport> reports = new ArrayList<ContainerReport>();
1082    List<ApplicationAttemptReport> attempts =
1083        yarnClient.getApplicationAttempts(options.getAppId());
1084    Map<ContainerId, ContainerReport> containerMap = new TreeMap<
1085        ContainerId, ContainerReport>();
1086    for (ApplicationAttemptReport attempt : attempts) {
1087      List<ContainerReport> containers = yarnClient.getContainers(
1088          attempt.getApplicationAttemptId());
1089      for (ContainerReport container : containers) {
1090        if (!containerMap.containsKey(container.getContainerId())) {
1091          containerMap.put(container.getContainerId(), container);
1092        }
1093      }
1094    }
1095    reports.addAll(containerMap.values());
1096    return reports;
1097  }
1098
1099  // filter the containerReports based on the nodeId and ContainerId
1100  private List<ContainerReport> filterContainersInfo(
1101      ContainerLogsRequest options, List<ContainerReport> containers) {
1102    List<ContainerReport> filterReports = new ArrayList<ContainerReport>(
1103        containers);
1104    String nodeId = options.getNodeId();
1105    boolean filterBasedOnNodeId = (nodeId != null && !nodeId.isEmpty());
1106    String containerId = options.getContainerId();
1107    boolean filterBasedOnContainerId = (containerId != null
1108        && !containerId.isEmpty());
1109
1110    if (filterBasedOnNodeId || filterBasedOnContainerId) {
1111    // filter the reports based on the containerId and.or nodeId
1112      for(ContainerReport report : containers) {
1113        if (filterBasedOnContainerId) {
1114          if (!report.getContainerId().toString()
1115              .equalsIgnoreCase(containerId)) {
1116            filterReports.remove(report);
1117          }
1118        }
1119
1120        if (filterBasedOnNodeId) {
1121          if (!report.getAssignedNode().toString().equalsIgnoreCase(nodeId)) {
1122            filterReports.remove(report);
1123          }
1124        }
1125      }
1126    }
1127    return filterReports;
1128  }
1129
1130  private int printContainerInfoFromRunningApplication(
1131      ContainerLogsRequest options) throws YarnException, IOException {
1132    String containerIdStr = options.getContainerId();
1133    String nodeIdStr = options.getNodeId();
1134    List<ContainerReport> reports =
1135        getContainerReportsFromRunningApplication(options);
1136    List<ContainerReport> filteredReports = filterContainersInfo(
1137        options, reports);
1138    if (filteredReports.isEmpty()) {
1139      StringBuilder sb = new StringBuilder();
1140      if (containerIdStr != null && !containerIdStr.isEmpty()) {
1141        sb.append("Trying to get container with ContainerId: "
1142            + containerIdStr + "\n");
1143      }
1144      if (nodeIdStr != null && !nodeIdStr.isEmpty()) {
1145        sb.append("Trying to get container from NodeManager: "
1146            + nodeIdStr + "\n");
1147      }
1148      sb.append("Can not find any matched containers for the application: "
1149          + options.getAppId());
1150      System.err.println(sb.toString());
1151      return -1;
1152    }
1153    for (ContainerReport report : filteredReports) {
1154      String nodeId = report.getAssignedNode().toString();
1155      String nodeHttpAddress = report.getNodeHttpAddress().replaceFirst(
1156          WebAppUtils.getHttpSchemePrefix(getConf()), "");
1157      String containerId = report.getContainerId().toString();
1158      String containerString = String.format(
1159          LogCLIHelpers.CONTAINER_ON_NODE_PATTERN, containerId, nodeId);
1160      outStream.println(containerString);
1161      outStream.println(StringUtils.repeat("=", containerString.length()));
1162      outStream.printf(LogCLIHelpers.PER_LOG_FILE_INFO_PATTERN,
1163          "LogType", "LogLength");
1164      outStream.println(StringUtils.repeat("=", containerString.length()));
1165      List<PerLogFileInfo> infos = getContainerLogFiles(
1166          getConf(), containerId, nodeHttpAddress);
1167      for (PerLogFileInfo info : infos) {
1168        outStream.printf(LogCLIHelpers.PER_LOG_FILE_INFO_PATTERN,
1169            info.getFileName(), info.getFileLength());
1170      }
1171    }
1172    return 0;
1173  }
1174
1175  private static class PerLogFileInfo {
1176    private String fileName;
1177    private String fileLength;
1178    public PerLogFileInfo(String fileName, String fileLength) {
1179      setFileName(fileName);
1180      setFileLength(fileLength);
1181    }
1182
1183    public String getFileName() {
1184      return fileName;
1185    }
1186
1187    public void setFileName(String fileName) {
1188      this.fileName = fileName;
1189    }
1190
1191    public String getFileLength() {
1192      return fileLength;
1193    }
1194
1195    public void setFileLength(String fileLength) {
1196      this.fileLength = fileLength;
1197    }
1198  }
1199
1200  @VisibleForTesting
1201  public Set<String> getMatchedContainerLogFiles(ContainerLogsRequest request,
1202      boolean useRegex) throws IOException {
1203    // fetch all the log files for the container
1204    // filter the log files based on the given -log_files pattern
1205    List<PerLogFileInfo> allLogFileInfos=
1206        getContainerLogFiles(getConf(), request.getContainerId(),
1207            request.getNodeHttpAddress());
1208    List<String> fileNames = new ArrayList<String>();
1209    for (PerLogFileInfo fileInfo : allLogFileInfos) {
1210      fileNames.add(fileInfo.getFileName());
1211    }
1212    return getMatchedLogFiles(request, fileNames,
1213        useRegex);
1214  }
1215
1216  @VisibleForTesting
1217  public ClientResponse getResponeFromNMWebService(Configuration conf,
1218      Client webServiceClient, ContainerLogsRequest request, String logFile) {
1219    WebResource webResource =
1220        webServiceClient.resource(WebAppUtils.getHttpSchemePrefix(conf)
1221        + request.getNodeHttpAddress());
1222    return webResource.path("ws").path("v1").path("node")
1223        .path("containers").path(request.getContainerId()).path("logs")
1224        .path(logFile)
1225        .queryParam("size", Long.toString(request.getBytes()))
1226        .accept(MediaType.TEXT_PLAIN).get(ClientResponse.class);
1227  }
1228}