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.PrintStream; 023import java.io.StringReader; 024import java.util.ArrayList; 025import java.util.Arrays; 026import java.util.Collection; 027import java.util.Collections; 028import java.util.List; 029import java.util.Set; 030import java.util.regex.Pattern; 031import javax.ws.rs.core.MediaType; 032import javax.xml.parsers.DocumentBuilder; 033import javax.xml.parsers.DocumentBuilderFactory; 034 035import org.apache.commons.cli.CommandLine; 036import org.apache.commons.cli.CommandLineParser; 037import org.apache.commons.cli.GnuParser; 038import org.apache.commons.cli.HelpFormatter; 039import org.apache.commons.cli.Option; 040import org.apache.commons.cli.Options; 041import org.apache.commons.cli.ParseException; 042import org.apache.commons.lang.StringUtils; 043import org.apache.hadoop.classification.InterfaceAudience.Private; 044import org.apache.hadoop.classification.InterfaceAudience.Public; 045import org.apache.hadoop.classification.InterfaceStability.Evolving; 046import org.apache.hadoop.conf.Configuration; 047import org.apache.hadoop.conf.Configured; 048import org.apache.hadoop.security.UserGroupInformation; 049import org.apache.hadoop.util.Tool; 050import org.apache.hadoop.yarn.api.records.ApplicationAttemptReport; 051import org.apache.hadoop.yarn.api.records.ApplicationId; 052import org.apache.hadoop.yarn.api.records.ApplicationReport; 053import org.apache.hadoop.yarn.api.records.ContainerId; 054import org.apache.hadoop.yarn.api.records.ContainerReport; 055import org.apache.hadoop.yarn.api.records.YarnApplicationState; 056import org.apache.hadoop.yarn.client.api.YarnClient; 057import org.apache.hadoop.yarn.conf.YarnConfiguration; 058import org.apache.hadoop.yarn.exceptions.YarnException; 059import org.apache.hadoop.yarn.logaggregation.LogCLIHelpers; 060import org.apache.hadoop.yarn.logaggregation.ContainerLogsRequest; 061import org.apache.hadoop.yarn.util.ConverterUtils; 062import org.apache.hadoop.yarn.util.Times; 063import org.apache.hadoop.yarn.webapp.util.WebAppUtils; 064import org.codehaus.jettison.json.JSONArray; 065import org.codehaus.jettison.json.JSONException; 066import org.codehaus.jettison.json.JSONObject; 067 068import com.google.common.annotations.VisibleForTesting; 069import com.sun.jersey.api.client.Client; 070import com.sun.jersey.api.client.ClientHandlerException; 071import com.sun.jersey.api.client.ClientResponse; 072import com.sun.jersey.api.client.UniformInterfaceException; 073import com.sun.jersey.api.client.WebResource; 074import org.w3c.dom.Document; 075import org.w3c.dom.NodeList; 076import org.xml.sax.InputSource; 077 078@Public 079@Evolving 080public class LogsCLI extends Configured implements Tool { 081 082 private static final String CONTAINER_ID_OPTION = "containerId"; 083 private static final String APPLICATION_ID_OPTION = "applicationId"; 084 private static final String NODE_ADDRESS_OPTION = "nodeAddress"; 085 private static final String APP_OWNER_OPTION = "appOwner"; 086 private static final String AM_CONTAINER_OPTION = "am"; 087 private static final String CONTAINER_LOG_FILES = "logFiles"; 088 private static final String SHOW_META_INFO = "show_meta_info"; 089 private static final String LIST_NODES_OPTION = "list_nodes"; 090 private static final String OUT_OPTION = "out"; 091 private static final String SIZE_OPTION = "size"; 092 public static final String HELP_CMD = "help"; 093 094 @Override 095 public int run(String[] args) throws Exception { 096 Options opts = createCommandOpts(); 097 Options printOpts = createPrintOpts(opts); 098 if (args.length < 1) { 099 printHelpMessage(printOpts); 100 return -1; 101 } 102 if (args[0].equals("-help")) { 103 printHelpMessage(printOpts); 104 return 0; 105 } 106 CommandLineParser parser = new GnuParser(); 107 String appIdStr = null; 108 String containerIdStr = null; 109 String nodeAddress = null; 110 String appOwner = null; 111 boolean getAMContainerLogs = false; 112 boolean showMetaInfo = false; 113 boolean nodesList = false; 114 String[] logFiles = null; 115 List<String> amContainersList = new ArrayList<String>(); 116 String localDir = null; 117 long bytes = Long.MAX_VALUE; 118 try { 119 CommandLine commandLine = parser.parse(opts, args, true); 120 appIdStr = commandLine.getOptionValue(APPLICATION_ID_OPTION); 121 containerIdStr = commandLine.getOptionValue(CONTAINER_ID_OPTION); 122 nodeAddress = commandLine.getOptionValue(NODE_ADDRESS_OPTION); 123 appOwner = commandLine.getOptionValue(APP_OWNER_OPTION); 124 getAMContainerLogs = commandLine.hasOption(AM_CONTAINER_OPTION); 125 showMetaInfo = commandLine.hasOption(SHOW_META_INFO); 126 nodesList = commandLine.hasOption(LIST_NODES_OPTION); 127 localDir = commandLine.getOptionValue(OUT_OPTION); 128 if (getAMContainerLogs) { 129 try { 130 amContainersList = parseAMContainer(commandLine, printOpts); 131 } catch (NumberFormatException ex) { 132 System.err.println(ex.getMessage()); 133 return -1; 134 } 135 } 136 if (commandLine.hasOption(CONTAINER_LOG_FILES)) { 137 logFiles = commandLine.getOptionValues(CONTAINER_LOG_FILES); 138 } 139 if (commandLine.hasOption(SIZE_OPTION)) { 140 bytes = Long.parseLong(commandLine.getOptionValue(SIZE_OPTION)); 141 } 142 } catch (ParseException e) { 143 System.err.println("options parsing failed: " + e.getMessage()); 144 printHelpMessage(printOpts); 145 return -1; 146 } 147 148 if (appIdStr == null) { 149 System.err.println("ApplicationId cannot be null!"); 150 printHelpMessage(printOpts); 151 return -1; 152 } 153 154 ApplicationId appId = null; 155 try { 156 appId = ApplicationId.fromString(appIdStr); 157 } catch (Exception e) { 158 System.err.println("Invalid ApplicationId specified"); 159 return -1; 160 } 161 162 LogCLIHelpers logCliHelper = new LogCLIHelpers(); 163 logCliHelper.setConf(getConf()); 164 165 YarnApplicationState appState = YarnApplicationState.NEW; 166 ApplicationReport appReport = null; 167 try { 168 appReport = getApplicationReport(appId); 169 appState = appReport.getYarnApplicationState(); 170 if (appState == YarnApplicationState.NEW 171 || appState == YarnApplicationState.NEW_SAVING 172 || appState == YarnApplicationState.SUBMITTED) { 173 System.err.println("Logs are not avaiable right now."); 174 return -1; 175 } 176 } catch (IOException | YarnException e) { 177 // If we can not get appReport from either RM or ATS 178 // We will assume that this app has already finished. 179 appState = YarnApplicationState.FINISHED; 180 System.err.println("Unable to get ApplicationState." 181 + " Attempting to fetch logs directly from the filesystem."); 182 } 183 184 if (appOwner == null || appOwner.isEmpty()) { 185 appOwner = guessAppOwner(appReport, appId); 186 if (appOwner == null) { 187 System.err.println("Can not find the appOwner. " 188 + "Please specify the correct appOwner"); 189 System.err.println("Could not locate application logs for " + appId); 190 return -1; 191 } 192 } 193 194 List<String> logs = new ArrayList<String>(); 195 if (fetchAllLogFiles(logFiles)) { 196 logs.add(".*"); 197 } else if (logFiles != null && logFiles.length > 0) { 198 logs = Arrays.asList(logFiles); 199 } 200 201 ContainerLogsRequest request = new ContainerLogsRequest(appId, 202 isApplicationFinished(appState), appOwner, nodeAddress, null, 203 containerIdStr, localDir, logs, bytes); 204 205 if (showMetaInfo) { 206 return showMetaInfo(request, logCliHelper); 207 } 208 209 if (nodesList) { 210 return showNodeLists(request, logCliHelper); 211 } 212 213 // To get am logs 214 if (getAMContainerLogs) { 215 return fetchAMContainerLogs(request, amContainersList, 216 logCliHelper); 217 } 218 219 int resultCode = 0; 220 if (containerIdStr != null) { 221 ContainerId containerId = ContainerId.fromString(containerIdStr); 222 if (!containerId.getApplicationAttemptId().getApplicationId() 223 .equals(appId)) { 224 System.err.println("The Application:" + appId 225 + " does not have the container:" + containerId); 226 return -1; 227 } 228 return fetchContainerLogs(request, logCliHelper); 229 } else { 230 if (nodeAddress == null) { 231 resultCode = fetchApplicationLogs(request, logCliHelper); 232 } else { 233 System.err.println("Should at least provide ContainerId!"); 234 printHelpMessage(printOpts); 235 resultCode = -1; 236 } 237 } 238 return resultCode; 239 } 240 241 private ApplicationReport getApplicationReport(ApplicationId appId) 242 throws IOException, YarnException { 243 YarnClient yarnClient = createYarnClient(); 244 245 try { 246 return yarnClient.getApplicationReport(appId); 247 } finally { 248 yarnClient.close(); 249 } 250 } 251 252 @VisibleForTesting 253 protected YarnClient createYarnClient() { 254 YarnClient yarnClient = YarnClient.createYarnClient(); 255 yarnClient.init(getConf()); 256 yarnClient.start(); 257 return yarnClient; 258 } 259 260 public static void main(String[] args) throws Exception { 261 Configuration conf = new YarnConfiguration(); 262 LogsCLI logDumper = new LogsCLI(); 263 logDumper.setConf(conf); 264 int exitCode = logDumper.run(args); 265 System.exit(exitCode); 266 } 267 268 private void printHelpMessage(Options options) { 269 System.out.println("Retrieve logs for completed YARN applications."); 270 HelpFormatter formatter = new HelpFormatter(); 271 formatter.printHelp("yarn logs -applicationId <application ID> [OPTIONS]", 272 new Options()); 273 formatter.setSyntaxPrefix(""); 274 formatter.printHelp("general options are:", options); 275 } 276 277 protected List<JSONObject> getAMContainerInfoForRMWebService( 278 Configuration conf, String appId) throws ClientHandlerException, 279 UniformInterfaceException, JSONException { 280 Client webServiceClient = Client.create(); 281 String webAppAddress = WebAppUtils.getHttpSchemePrefix(conf) + 282 WebAppUtils.getWebAppBindURL(conf, YarnConfiguration.RM_BIND_HOST, 283 WebAppUtils.getRMWebAppURLWithoutScheme(conf)); 284 WebResource webResource = webServiceClient.resource(webAppAddress); 285 286 ClientResponse response = 287 webResource.path("ws").path("v1").path("cluster").path("apps") 288 .path(appId).path("appattempts").accept(MediaType.APPLICATION_JSON) 289 .get(ClientResponse.class); 290 JSONObject json = 291 response.getEntity(JSONObject.class).getJSONObject("appAttempts"); 292 JSONArray requests = json.getJSONArray("appAttempt"); 293 List<JSONObject> amContainersList = new ArrayList<JSONObject>(); 294 for (int i = 0; i < requests.length(); i++) { 295 amContainersList.add(requests.getJSONObject(i)); 296 } 297 return amContainersList; 298 } 299 300 private List<JSONObject> getAMContainerInfoForAHSWebService( 301 Configuration conf, String appId) throws ClientHandlerException, 302 UniformInterfaceException, JSONException { 303 Client webServiceClient = Client.create(); 304 String webAppAddress = 305 WebAppUtils.getHttpSchemePrefix(conf) 306 + WebAppUtils.getAHSWebAppURLWithoutScheme(conf); 307 WebResource webResource = webServiceClient.resource(webAppAddress); 308 309 ClientResponse response = 310 webResource.path("ws").path("v1").path("applicationhistory") 311 .path("apps").path(appId).path("appattempts") 312 .accept(MediaType.APPLICATION_JSON) 313 .get(ClientResponse.class); 314 JSONObject json = response.getEntity(JSONObject.class); 315 JSONArray requests = json.getJSONArray("appAttempt"); 316 List<JSONObject> amContainersList = new ArrayList<JSONObject>(); 317 for (int i = 0; i < requests.length(); i++) { 318 amContainersList.add(requests.getJSONObject(i)); 319 } 320 Collections.reverse(amContainersList); 321 return amContainersList; 322 } 323 324 private boolean fetchAllLogFiles(String[] logFiles) { 325 if(logFiles != null) { 326 List<String> logs = Arrays.asList(logFiles); 327 if(logs.contains("ALL") || logs.contains(".*")) { 328 return true; 329 } 330 } 331 return false; 332 } 333 334 private List<String> getContainerLogFiles(Configuration conf, 335 String containerIdStr, String nodeHttpAddress) throws IOException { 336 List<String> logFiles = new ArrayList<>(); 337 Client webServiceClient = Client.create(); 338 try { 339 WebResource webResource = webServiceClient 340 .resource(WebAppUtils.getHttpSchemePrefix(conf) + nodeHttpAddress); 341 ClientResponse response = 342 webResource.path("ws").path("v1").path("node").path("containers") 343 .path(containerIdStr).accept(MediaType.APPLICATION_XML) 344 .get(ClientResponse.class); 345 if (response.getStatusInfo().getStatusCode() == 346 ClientResponse.Status.OK.getStatusCode()) { 347 try { 348 String xml = response.getEntity(String.class); 349 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); 350 DocumentBuilder db = dbf.newDocumentBuilder(); 351 InputSource is = new InputSource(); 352 is.setCharacterStream(new StringReader(xml)); 353 Document dom = db.parse(is); 354 NodeList elements = dom.getElementsByTagName("containerLogFiles"); 355 for (int i = 0; i < elements.getLength(); i++) { 356 logFiles.add(elements.item(i).getTextContent()); 357 } 358 } catch (Exception e) { 359 System.err.println("Unable to parse xml from webservice. Error:"); 360 System.err.println(e.getMessage()); 361 throw new IOException(e); 362 } 363 } 364 365 } catch (ClientHandlerException | UniformInterfaceException ex) { 366 System.err.println("Unable to fetch log files list"); 367 throw new IOException(ex); 368 } 369 return logFiles; 370 } 371 372 @Private 373 @VisibleForTesting 374 public void printContainerLogsFromRunningApplication(Configuration conf, 375 ContainerLogsRequest request, LogCLIHelpers logCliHelper) 376 throws IOException { 377 String containerIdStr = request.getContainerId().toString(); 378 String localDir = request.getOutputLocalDir(); 379 String nodeHttpAddress = request.getNodeHttpAddress(); 380 String nodeId = request.getNodeId(); 381 PrintStream out = logCliHelper.createPrintStream(localDir, nodeId, 382 containerIdStr); 383 try { 384 // fetch all the log files for the container 385 // filter the log files based on the given --logFiles pattern 386 List<String> allLogs= 387 getContainerLogFiles(getConf(), containerIdStr, nodeHttpAddress); 388 List<String> matchedFiles = getMatchedLogFiles( 389 request, allLogs, true); 390 if (matchedFiles.isEmpty()) { 391 return; 392 } 393 ContainerLogsRequest newOptions = new ContainerLogsRequest(request); 394 newOptions.setLogTypes(matchedFiles); 395 396 Client webServiceClient = Client.create(); 397 String containerString = "\n\nContainer: " + containerIdStr + " on " 398 + nodeId; 399 out.println(containerString); 400 out.println(StringUtils.repeat("=", containerString.length())); 401 402 for (String logFile : newOptions.getLogTypes()) { 403 out.println("LogType:" + logFile); 404 out.println("Log Upload Time:" 405 + Times.format(System.currentTimeMillis())); 406 out.println("Log Contents:"); 407 try { 408 WebResource webResource = 409 webServiceClient.resource(WebAppUtils.getHttpSchemePrefix(conf) 410 + nodeHttpAddress); 411 ClientResponse response = 412 webResource.path("ws").path("v1").path("node") 413 .path("containerlogs").path(containerIdStr).path(logFile) 414 .queryParam("size", Long.toString(request.getBytes())) 415 .accept(MediaType.TEXT_PLAIN).get(ClientResponse.class); 416 out.println(response.getEntity(String.class)); 417 out.println("End of LogType:" + logFile + ". This log file belongs" 418 + " to a running container (" + containerIdStr + ") and so may" 419 + " not be complete."); 420 out.flush(); 421 } catch (ClientHandlerException | UniformInterfaceException ex) { 422 System.err.println("Can not find the log file:" + logFile 423 + " for the container:" + containerIdStr + " in NodeManager:" 424 + nodeId); 425 } 426 } 427 // for the case, we have already uploaded partial logs in HDFS 428 logCliHelper.dumpAContainersLogsForALogType(newOptions, false); 429 } finally { 430 logCliHelper.closePrintStream(out); 431 } 432 } 433 434 private int printContainerLogsForFinishedApplication( 435 ContainerLogsRequest request, LogCLIHelpers logCliHelper) 436 throws IOException { 437 ContainerLogsRequest newOptions = getMatchedLogOptions( 438 request, logCliHelper); 439 if (newOptions == null) { 440 return -1; 441 } 442 return logCliHelper.dumpAContainersLogsForALogType(newOptions); 443 } 444 445 private int printContainerLogsForFinishedApplicationWithoutNodeId( 446 ContainerLogsRequest request, LogCLIHelpers logCliHelper) 447 throws IOException { 448 ContainerLogsRequest newOptions = getMatchedLogOptions( 449 request, logCliHelper); 450 if (newOptions == null) { 451 return -1; 452 } 453 return logCliHelper.dumpAContainersLogsForALogTypeWithoutNodeId( 454 newOptions); 455 } 456 457 @Private 458 @VisibleForTesting 459 public ContainerReport getContainerReport(String containerIdStr) 460 throws YarnException, IOException { 461 YarnClient yarnClient = createYarnClient(); 462 try { 463 return yarnClient.getContainerReport( 464 ContainerId.fromString(containerIdStr)); 465 } finally { 466 yarnClient.close(); 467 } 468 } 469 470 private boolean isApplicationFinished(YarnApplicationState appState) { 471 return appState == YarnApplicationState.FINISHED 472 || appState == YarnApplicationState.FAILED 473 || appState == YarnApplicationState.KILLED; 474 } 475 476 private int printAMContainerLogs(Configuration conf, 477 ContainerLogsRequest request, List<String> amContainers, 478 LogCLIHelpers logCliHelper) throws Exception { 479 List<JSONObject> amContainersList = null; 480 List<ContainerLogsRequest> requests = 481 new ArrayList<ContainerLogsRequest>(); 482 boolean getAMContainerLists = false; 483 String appId = request.getAppId().toString(); 484 String errorMessage = ""; 485 try { 486 amContainersList = getAMContainerInfoForRMWebService(conf, appId); 487 if (amContainersList != null && !amContainersList.isEmpty()) { 488 getAMContainerLists = true; 489 for (JSONObject amContainer : amContainersList) { 490 ContainerLogsRequest amRequest = new ContainerLogsRequest(request); 491 amRequest.setContainerId(amContainer.getString("containerId")); 492 amRequest.setNodeHttpAddress( 493 amContainer.getString("nodeHttpAddress")); 494 amRequest.setNodeId(amContainer.getString("nodeId")); 495 requests.add(amRequest); 496 } 497 } 498 } catch (Exception ex) { 499 errorMessage = ex.getMessage(); 500 if (request.isAppFinished()) { 501 try { 502 amContainersList = getAMContainerInfoForAHSWebService(conf, appId); 503 if (amContainersList != null && !amContainersList.isEmpty()) { 504 getAMContainerLists = true; 505 for (JSONObject amContainer : amContainersList) { 506 ContainerLogsRequest amRequest = new ContainerLogsRequest( 507 request); 508 amRequest.setContainerId(amContainer.getString("amContainerId")); 509 requests.add(amRequest); 510 } 511 } 512 } catch (Exception e) { 513 errorMessage = e.getMessage(); 514 } 515 } 516 } 517 518 if (!getAMContainerLists) { 519 System.err.println("Unable to get AM container informations " 520 + "for the application:" + appId); 521 System.err.println(errorMessage); 522 return -1; 523 } 524 525 if (amContainers.contains("ALL")) { 526 for (ContainerLogsRequest amRequest : requests) { 527 outputAMContainerLogs(amRequest, conf, logCliHelper); 528 } 529 System.out.println(); 530 System.out.println("Specified ALL for -am option. " 531 + "Printed logs for all am containers."); 532 } else { 533 for (String amContainer : amContainers) { 534 int amContainerId = Integer.parseInt(amContainer.trim()); 535 if (amContainerId == -1) { 536 outputAMContainerLogs(requests.get(requests.size() - 1), conf, 537 logCliHelper); 538 } else { 539 if (amContainerId <= requests.size()) { 540 outputAMContainerLogs(requests.get(amContainerId - 1), conf, 541 logCliHelper); 542 } else { 543 System.err.println(String.format("ERROR: Specified AM containerId" 544 + " (%s) exceeds the number of AM containers (%s).", 545 amContainerId, requests.size())); 546 return -1; 547 } 548 } 549 } 550 } 551 return 0; 552 } 553 554 private void outputAMContainerLogs(ContainerLogsRequest request, 555 Configuration conf, LogCLIHelpers logCliHelper) throws Exception { 556 String nodeHttpAddress = request.getNodeHttpAddress(); 557 String containerId = request.getContainerId(); 558 String nodeId = request.getNodeId(); 559 560 if (request.isAppFinished()) { 561 if (containerId != null && !containerId.isEmpty()) { 562 if (nodeId == null || nodeId.isEmpty()) { 563 try { 564 nodeId = 565 getContainerReport(containerId).getAssignedNode().toString(); 566 request.setNodeId(nodeId); 567 } catch (Exception ex) { 568 System.err.println(ex); 569 nodeId = null; 570 } 571 } 572 if (nodeId != null && !nodeId.isEmpty()) { 573 printContainerLogsForFinishedApplication(request, 574 logCliHelper); 575 } 576 } 577 } else { 578 if (nodeHttpAddress != null && containerId != null 579 && !nodeHttpAddress.isEmpty() && !containerId.isEmpty()) { 580 printContainerLogsFromRunningApplication(conf, 581 request, logCliHelper); 582 } 583 } 584 } 585 586 private int showMetaInfo(ContainerLogsRequest request, 587 LogCLIHelpers logCliHelper) throws IOException { 588 if (!request.isAppFinished()) { 589 System.err.println("The -show_meta_info command can be only used " 590 + "with finished applications"); 591 return -1; 592 } else { 593 logCliHelper.printLogMetadata(request, System.out, System.err); 594 return 0; 595 } 596 } 597 598 private int showNodeLists(ContainerLogsRequest request, 599 LogCLIHelpers logCliHelper) throws IOException { 600 if (!request.isAppFinished()) { 601 System.err.println("The -list_nodes command can be only used with " 602 + "finished applications"); 603 return -1; 604 } else { 605 logCliHelper.printNodesList(request, System.out, System.err); 606 return 0; 607 } 608 } 609 610 private Options createCommandOpts() { 611 Options opts = new Options(); 612 opts.addOption(HELP_CMD, false, "Displays help for all commands."); 613 Option appIdOpt = 614 new Option(APPLICATION_ID_OPTION, true, "ApplicationId (required)"); 615 appIdOpt.setRequired(true); 616 opts.addOption(appIdOpt); 617 opts.addOption(CONTAINER_ID_OPTION, true, "ContainerId. " 618 + "By default, it will only print syslog if the application is runing." 619 + " Work with -logFiles to get other logs."); 620 opts.addOption(NODE_ADDRESS_OPTION, true, "NodeAddress in the format " 621 + "nodename:port"); 622 opts.addOption(APP_OWNER_OPTION, true, 623 "AppOwner (assumed to be current user if not specified)"); 624 Option amOption = new Option(AM_CONTAINER_OPTION, true, 625 "Prints the AM Container logs for this application. " 626 + "Specify comma-separated value to get logs for related AM " 627 + "Container. For example, If we specify -am 1,2, we will get " 628 + "the logs for the first AM Container as well as the second " 629 + "AM Container. To get logs for all AM Containers, use -am ALL. " 630 + "To get logs for the latest AM Container, use -am -1. " 631 + "By default, it will only print out syslog. Work with -logFiles " 632 + "to get other logs"); 633 amOption.setValueSeparator(','); 634 amOption.setArgs(Option.UNLIMITED_VALUES); 635 amOption.setArgName("AM Containers"); 636 opts.addOption(amOption); 637 Option logFileOpt = new Option(CONTAINER_LOG_FILES, true, 638 "Work with -am/-containerId and specify comma-separated value " 639 + "to get specified container log files. Use \"ALL\" to fetch all the " 640 + "log files for the container. It also supports Java Regex."); 641 logFileOpt.setValueSeparator(','); 642 logFileOpt.setArgs(Option.UNLIMITED_VALUES); 643 logFileOpt.setArgName("Log File Name"); 644 opts.addOption(logFileOpt); 645 opts.addOption(SHOW_META_INFO, false, "Show the log metadata, " 646 + "including log-file names, the size of the log files. " 647 + "You can combine this with --containerId to get log metadata for " 648 + "the specific container, or with --nodeAddress to get log metadata " 649 + "for all the containers on the specific NodeManager. " 650 + "Currently, this option can only be used for finished " 651 + "applications."); 652 opts.addOption(LIST_NODES_OPTION, false, 653 "Show the list of nodes that successfully aggregated logs. " 654 + "This option can only be used with finished applications."); 655 opts.addOption(OUT_OPTION, true, "Local directory for storing individual " 656 + "container logs. The container logs will be stored based on the " 657 + "node the container ran on."); 658 opts.addOption(SIZE_OPTION, true, "Prints the log file's first 'n' bytes " 659 + "or the last 'n' bytes. Use negative values as bytes to read from " 660 + "the end and positive values as bytes to read from the beginning."); 661 opts.getOption(APPLICATION_ID_OPTION).setArgName("Application ID"); 662 opts.getOption(CONTAINER_ID_OPTION).setArgName("Container ID"); 663 opts.getOption(NODE_ADDRESS_OPTION).setArgName("Node Address"); 664 opts.getOption(APP_OWNER_OPTION).setArgName("Application Owner"); 665 opts.getOption(AM_CONTAINER_OPTION).setArgName("AM Containers"); 666 opts.getOption(OUT_OPTION).setArgName("Local Directory"); 667 opts.getOption(SIZE_OPTION).setArgName("size"); 668 return opts; 669 } 670 671 private Options createPrintOpts(Options commandOpts) { 672 Options printOpts = new Options(); 673 printOpts.addOption(commandOpts.getOption(HELP_CMD)); 674 printOpts.addOption(commandOpts.getOption(CONTAINER_ID_OPTION)); 675 printOpts.addOption(commandOpts.getOption(NODE_ADDRESS_OPTION)); 676 printOpts.addOption(commandOpts.getOption(APP_OWNER_OPTION)); 677 printOpts.addOption(commandOpts.getOption(AM_CONTAINER_OPTION)); 678 printOpts.addOption(commandOpts.getOption(CONTAINER_LOG_FILES)); 679 printOpts.addOption(commandOpts.getOption(SHOW_META_INFO)); 680 printOpts.addOption(commandOpts.getOption(LIST_NODES_OPTION)); 681 printOpts.addOption(commandOpts.getOption(OUT_OPTION)); 682 printOpts.addOption(commandOpts.getOption(SIZE_OPTION)); 683 return printOpts; 684 } 685 686 private List<String> parseAMContainer(CommandLine commandLine, 687 Options printOpts) throws NumberFormatException { 688 List<String> amContainersList = new ArrayList<String>(); 689 String[] amContainers = commandLine.getOptionValues(AM_CONTAINER_OPTION); 690 for (String am : amContainers) { 691 boolean errorInput = false; 692 if (!am.trim().equalsIgnoreCase("ALL")) { 693 try { 694 int id = Integer.parseInt(am.trim()); 695 if (id != -1 && id <= 0) { 696 errorInput = true; 697 } 698 } catch (NumberFormatException ex) { 699 errorInput = true; 700 } 701 if (errorInput) { 702 String errMessage = 703 "Invalid input for option -am. Valid inputs are 'ALL', -1 " 704 + "and any other integer which is larger than 0."; 705 printHelpMessage(printOpts); 706 throw new NumberFormatException(errMessage); 707 } 708 amContainersList.add(am.trim()); 709 } else { 710 amContainersList.add("ALL"); 711 break; 712 } 713 } 714 return amContainersList; 715 } 716 717 private int fetchAMContainerLogs(ContainerLogsRequest request, 718 List<String> amContainersList, LogCLIHelpers logCliHelper) 719 throws Exception { 720 List<String> logFiles = request.getLogTypes(); 721 // if we do not specify the value for CONTAINER_LOG_FILES option, 722 // we will only output syslog 723 if (logFiles == null || logFiles.isEmpty()) { 724 logFiles = Arrays.asList("syslog"); 725 } 726 request.setLogTypes(logFiles); 727 // If the application is running, we will call the RM WebService 728 // to get the AppAttempts which includes the nodeHttpAddress 729 // and containerId for all the AM Containers. 730 // After that, we will call NodeManager webService to get the 731 // related logs 732 if (!request.isAppFinished()) { 733 return printAMContainerLogs(getConf(), request, amContainersList, 734 logCliHelper); 735 } else { 736 // If the application is in the final state, we will call RM webservice 737 // to get all AppAttempts information first. If we get nothing, 738 // we will try to call AHS webservice to get related AppAttempts 739 // which includes nodeAddress for the AM Containers. 740 // After that, we will use nodeAddress and containerId 741 // to get logs from HDFS directly. 742 if (getConf().getBoolean(YarnConfiguration.APPLICATION_HISTORY_ENABLED, 743 YarnConfiguration.DEFAULT_APPLICATION_HISTORY_ENABLED)) { 744 return printAMContainerLogs(getConf(), request, amContainersList, 745 logCliHelper); 746 } else { 747 ApplicationId appId = request.getAppId(); 748 String appOwner = request.getAppOwner(); 749 System.err.println("Can not get AMContainers logs for " 750 + "the application:" + appId + " with the appOwner:" + appOwner); 751 System.err.println("This application:" + appId + " has finished." 752 + " Please enable the application-history service or explicitly" 753 + " use 'yarn logs -applicationId <appId> " 754 + "-containerId <containerId> --nodeAddress <nodeHttpAddress>' " 755 + "to get the container logs."); 756 return -1; 757 } 758 } 759 } 760 761 private int fetchContainerLogs(ContainerLogsRequest request, 762 LogCLIHelpers logCliHelper) throws IOException { 763 int resultCode = 0; 764 String appIdStr = request.getAppId().toString(); 765 String containerIdStr = request.getContainerId(); 766 String nodeAddress = request.getNodeId(); 767 String appOwner = request.getAppOwner(); 768 boolean isAppFinished = request.isAppFinished(); 769 List<String> logFiles = request.getLogTypes(); 770 // if we provide the node address and the application is in the final 771 // state, we could directly get logs from HDFS. 772 if (nodeAddress != null && isAppFinished) { 773 // if user specified "ALL" as the logFiles param, pass empty list 774 // to logCliHelper so that it fetches all the logs 775 return printContainerLogsForFinishedApplication( 776 request, logCliHelper); 777 } 778 String nodeHttpAddress = null; 779 String nodeId = null; 780 try { 781 // If the nodeAddress is not provided, we will try to get 782 // the ContainerReport. In the containerReport, we could get 783 // nodeAddress and nodeHttpAddress 784 ContainerReport report = getContainerReport(containerIdStr); 785 nodeHttpAddress = 786 report.getNodeHttpAddress().replaceFirst( 787 WebAppUtils.getHttpSchemePrefix(getConf()), ""); 788 nodeId = report.getAssignedNode().toString(); 789 request.setNodeId(nodeId); 790 request.setNodeHttpAddress(nodeHttpAddress); 791 } catch (IOException | YarnException ex) { 792 if (isAppFinished) { 793 return printContainerLogsForFinishedApplicationWithoutNodeId( 794 request, logCliHelper); 795 } else { 796 System.err.println("Unable to get logs for this container:" 797 + containerIdStr + "for the application:" + appIdStr 798 + " with the appOwner: " + appOwner); 799 System.err.println("The application: " + appIdStr 800 + " is still running, and we can not get Container report " 801 + "for the container: " + containerIdStr +". Please try later " 802 + "or after the application finishes."); 803 return -1; 804 } 805 } 806 // If the application is not in the final state, 807 // we will provide the NodeHttpAddress and get the container logs 808 // by calling NodeManager webservice. 809 if (!isAppFinished) { 810 // if we do not specify the value for CONTAINER_LOG_FILES option, 811 // we will only output syslog 812 if (logFiles == null || logFiles.isEmpty()) { 813 logFiles = Arrays.asList("syslog"); 814 } 815 request.setLogTypes(logFiles); 816 printContainerLogsFromRunningApplication(getConf(), request, 817 logCliHelper); 818 } else { 819 // If the application is in the final state, we will directly 820 // get the container logs from HDFS. 821 resultCode = printContainerLogsForFinishedApplication( 822 request, logCliHelper); 823 } 824 return resultCode; 825 } 826 827 private int fetchApplicationLogs(ContainerLogsRequest options, 828 LogCLIHelpers logCliHelper) throws IOException, YarnException { 829 // If the application has finished, we would fetch the logs 830 // from HDFS. 831 // If the application is still running, we would get the full 832 // list of the containers first, then fetch the logs for each 833 // container from NM. 834 int resultCode = 0; 835 if (options.isAppFinished()) { 836 ContainerLogsRequest newOptions = getMatchedLogOptions( 837 options, logCliHelper); 838 if (newOptions == null) { 839 resultCode = -1; 840 } else { 841 resultCode = 842 logCliHelper.dumpAllContainersLogs(newOptions); 843 } 844 } else { 845 List<ContainerLogsRequest> containerLogRequests = 846 getContainersLogRequestForRunningApplication(options); 847 for (ContainerLogsRequest container : containerLogRequests) { 848 printContainerLogsFromRunningApplication(getConf(), container, 849 logCliHelper); 850 } 851 } 852 if (resultCode == -1) { 853 System.err.println("Can not find the logs for the application: " 854 + options.getAppId() + " with the appOwner: " 855 + options.getAppOwner()); 856 } 857 return resultCode; 858 } 859 860 private String guessAppOwner(ApplicationReport appReport, 861 ApplicationId appId) throws IOException { 862 String appOwner = null; 863 if (appReport != null) { 864 //always use the app owner from the app report if possible 865 appOwner = appReport.getUser(); 866 } else { 867 appOwner = UserGroupInformation.getCurrentUser().getShortUserName(); 868 appOwner = LogCLIHelpers.getOwnerForAppIdOrNull( 869 appId, appOwner, getConf()); 870 } 871 return appOwner; 872 } 873 874 private ContainerLogsRequest getMatchedLogOptions( 875 ContainerLogsRequest request, LogCLIHelpers logCliHelper) 876 throws IOException { 877 ContainerLogsRequest newOptions = new ContainerLogsRequest(request); 878 if (request.getLogTypes() != null && !request.getLogTypes().isEmpty()) { 879 List<String> matchedFiles = new ArrayList<String>(); 880 if (!request.getLogTypes().contains(".*")) { 881 Set<String> files = logCliHelper.listContainerLogs(request); 882 matchedFiles = getMatchedLogFiles( 883 request, files, true); 884 if (matchedFiles.isEmpty()) { 885 return null; 886 } 887 } 888 newOptions.setLogTypes(matchedFiles); 889 } 890 return newOptions; 891 } 892 893 private List<String> getMatchedLogFiles(ContainerLogsRequest options, 894 Collection<String> candidate, boolean printError) throws IOException { 895 List<String> matchedFiles = new ArrayList<String>(); 896 List<String> filePattern = options.getLogTypes(); 897 for (String file : candidate) { 898 if (isFileMatching(file, filePattern)) { 899 matchedFiles.add(file); 900 } 901 } 902 if (matchedFiles.isEmpty()) { 903 if (printError) { 904 System.err.println("Can not find any log file matching the pattern: " 905 + options.getLogTypes() + " for the application: " 906 + options.getAppId()); 907 } 908 } 909 return matchedFiles; 910 } 911 912 private boolean isFileMatching(String fileType, 913 List<String> logTypes) { 914 for (String logType : logTypes) { 915 Pattern filterPattern = Pattern.compile(logType); 916 boolean match = filterPattern.matcher(fileType).find(); 917 if (match) { 918 return true; 919 } 920 } 921 return false; 922 } 923 924 private List<ContainerLogsRequest> 925 getContainersLogRequestForRunningApplication( 926 ContainerLogsRequest options) throws YarnException, IOException { 927 List<ContainerLogsRequest> newOptionsList = 928 new ArrayList<ContainerLogsRequest>(); 929 YarnClient yarnClient = createYarnClient(); 930 try { 931 List<ApplicationAttemptReport> attempts = 932 yarnClient.getApplicationAttempts(options.getAppId()); 933 for (ApplicationAttemptReport attempt : attempts) { 934 List<ContainerReport> containers = yarnClient.getContainers( 935 attempt.getApplicationAttemptId()); 936 for (ContainerReport container : containers) { 937 ContainerLogsRequest newOptions = new ContainerLogsRequest(options); 938 newOptions.setContainerId(container.getContainerId().toString()); 939 newOptions.setNodeId(container.getAssignedNode().toString()); 940 newOptions.setNodeHttpAddress(container.getNodeHttpAddress() 941 .replaceFirst(WebAppUtils.getHttpSchemePrefix(getConf()), "")); 942 // if we do not specify the value for CONTAINER_LOG_FILES option, 943 // we will only output syslog 944 List<String> logFiles = newOptions.getLogTypes(); 945 if (logFiles == null || logFiles.isEmpty()) { 946 logFiles = Arrays.asList("syslog"); 947 newOptions.setLogTypes(logFiles); 948 } 949 newOptionsList.add(newOptions); 950 } 951 } 952 return newOptionsList; 953 } finally { 954 yarnClient.close(); 955 } 956 } 957}