diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/main/java/org/apache/hadoop/yarn/client/cli/LogsCLI.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/main/java/org/apache/hadoop/yarn/client/cli/LogsCLI.java index 1ac4d61..cbb98a1 100644 --- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/main/java/org/apache/hadoop/yarn/client/cli/LogsCLI.java +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/main/java/org/apache/hadoop/yarn/client/cli/LogsCLI.java @@ -207,8 +207,17 @@ public int run(String[] args) throws Exception { logFiles, appOwner, nodeAddress, containerId, logCliHelper); } else { if (nodeAddress == null) { + List logs; + if (logFiles == null) { + logs = null; + } else if (fetchAllLogFiles(logFiles)) { + logs = null; + } else { + logs = Arrays.asList(logFiles); + } resultCode = - logCliHelper.dumpAllContainersLogs(appId, appOwner, System.out); + logCliHelper.dumpAllContainersLogs(appId, appOwner, System.out, + logs); if (resultCode == -1) { System.err.println("Can not find the logs for the application: " + appId + " with the appOwner: " + appOwner); @@ -604,7 +613,7 @@ private Options createCommandOpts() { Option logFileOpt = new Option(CONTAINER_LOG_FILES, true, "Work with -am/-containerId and specify comma-separated value " + "to get specified container log files. Use \"ALL\" to fetch all the " - + "log files for the container."); + + "log files for the container. It also supports Java Regex."); logFileOpt.setValueSeparator(','); logFileOpt.setArgs(Option.UNLIMITED_VALUES); logFileOpt.setArgName("Log File Name"); diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/test/java/org/apache/hadoop/yarn/client/cli/TestLogsCLI.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/test/java/org/apache/hadoop/yarn/client/cli/TestLogsCLI.java index 4f1c629..6a53609 100644 --- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/test/java/org/apache/hadoop/yarn/client/cli/TestLogsCLI.java +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/test/java/org/apache/hadoop/yarn/client/cli/TestLogsCLI.java @@ -178,7 +178,8 @@ public void testHelpMessage() throws Exception { pw.println(" -logFiles Work with -am/-containerId and specify"); pw.println(" comma-separated value to get specified"); pw.println(" container log files. Use \"ALL\" to fetch"); - pw.println(" all the log files for the container."); + pw.println(" all the log files for the container. It"); + pw.println(" also supports Java Regex."); pw.println(" -nodeAddress NodeAddress in the format nodename:port"); pw.println(" -show_meta_info Show the log metadata, including log-file"); pw.println(" names, the size of the log files. You can"); @@ -281,6 +282,19 @@ public void testFetchApplictionLogs() throws Exception { "Hello container_0_0001_01_000003 in stdout!")); sysOutStream.reset(); + exitCode = cli.run(new String[] { "-applicationId", appId.toString(), + "-logFiles", ".*" }); + assertTrue(exitCode == 0); + assertTrue(sysOutStream.toString().contains( + "Hello container_0_0001_01_000001 in syslog!")); + assertTrue(sysOutStream.toString().contains( + "Hello container_0_0001_01_000002 in syslog!")); + assertTrue(sysOutStream.toString().contains( + "Hello container_0_0001_01_000003 in syslog!")); + assertTrue(sysOutStream.toString().contains( + "Hello container_0_0001_01_000003 in stdout!")); + sysOutStream.reset(); + // uploaded two logs for container1. The first log is empty. // The second one is not empty. // We can still successfully read logs for container1. diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/logaggregation/AggregatedLogFormat.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/logaggregation/AggregatedLogFormat.java index 61b92dd..15f5f87 100644 --- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/logaggregation/AggregatedLogFormat.java +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/logaggregation/AggregatedLogFormat.java @@ -771,7 +771,7 @@ public static int readContainerLogsForALogType( String fileType = valueStream.readUTF(); String fileLengthStr = valueStream.readUTF(); long fileLength = Long.parseLong(fileLengthStr); - if (logType.contains(fileType)) { + if (isFileMatching(fileType, logType)) { out.print("LogType:"); out.println(fileType); if (logUploadedTime != -1) { @@ -808,6 +808,18 @@ public static int readContainerLogsForALogType( } } + private static boolean isFileMatching(String fileType, + List logTypes) { + for (String logType : logTypes) { + Pattern filterPattern = Pattern.compile(logType); + boolean match = filterPattern.matcher(fileType).find(); + if (match) { + return true; + } + } + return false; + } + @Private public static void readContainerMetaDataAndSkipData( DataInputStream valueStream, PrintStream out) throws IOException { diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/logaggregation/LogCLIHelpers.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/logaggregation/LogCLIHelpers.java index ba0dd89..eace699 100644 --- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/logaggregation/LogCLIHelpers.java +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/logaggregation/LogCLIHelpers.java @@ -283,7 +283,7 @@ public int dumpAContainerLogsForALogType(String containerIdStr, @Private public int dumpAllContainersLogs(ApplicationId appId, String appOwner, - PrintStream out) throws IOException { + PrintStream out, List logTypes) throws IOException { RemoteIterator nodeFiles = getRemoteNodeFileDir( appId, appOwner); if (nodeFiles == null) { @@ -318,9 +318,17 @@ public int dumpAllContainersLogs(ApplicationId appId, String appOwner, out.println(StringUtils.repeat("=", containerString.length())); while (true) { try { - LogReader.readAContainerLogsForALogType(valueStream, out, - thisNodeFile.getModificationTime()); - foundAnyLogs = true; + if (logTypes == null) { + LogReader.readAContainerLogsForALogType(valueStream, out, + thisNodeFile.getModificationTime()); + foundAnyLogs = true; + } else { + int result = LogReader.readContainerLogsForALogType(valueStream, + out, thisNodeFile.getModificationTime(), logTypes); + if (result == 0) { + foundAnyLogs = true; + } + } } catch (EOFException eof) { break; } diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-applicationhistoryservice/src/main/java/org/apache/hadoop/yarn/server/applicationhistoryservice/webapp/AHSWebServices.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-applicationhistoryservice/src/main/java/org/apache/hadoop/yarn/server/applicationhistoryservice/webapp/AHSWebServices.java index deae894..502cebb 100644 --- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-applicationhistoryservice/src/main/java/org/apache/hadoop/yarn/server/applicationhistoryservice/webapp/AHSWebServices.java +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-applicationhistoryservice/src/main/java/org/apache/hadoop/yarn/server/applicationhistoryservice/webapp/AHSWebServices.java @@ -23,9 +23,11 @@ import java.io.IOException; import java.io.OutputStream; import java.nio.charset.Charset; +import java.util.ArrayList; import java.util.Collections; +import java.util.List; import java.util.Set; - +import java.util.regex.Pattern; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.ws.rs.GET; @@ -313,7 +315,7 @@ private Response sendStreamOutputResponse(ApplicationId appId, if (downloadFile) { response.header("Content-Type", "application/octet-stream"); response.header("Content-Disposition", "attachment; filename=" - + fileName); + + containerIdStr); } return response.build(); } @@ -381,7 +383,7 @@ public void write(OutputStream os) throws IOException, String fileType = valueStream.readUTF(); String fileLengthStr = valueStream.readUTF(); long fileLength = Long.parseLong(fileLengthStr); - if (fileType.equalsIgnoreCase(logFile)) { + if (isFileMatched(logFile, fileType)) { StringBuilder sb = new StringBuilder(); sb.append("LogType:"); sb.append(fileType + "\n"); @@ -435,4 +437,9 @@ public void write(OutputStream os) throws IOException, }; return stream; } + + private boolean isFileMatched(String filePattern, String fileName) { + Pattern filterPattern = Pattern.compile(filePattern); + return filterPattern.matcher(fileName).find(); + } } \ No newline at end of file diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-applicationhistoryservice/src/test/java/org/apache/hadoop/yarn/server/applicationhistoryservice/webapp/TestAHSWebServices.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-applicationhistoryservice/src/test/java/org/apache/hadoop/yarn/server/applicationhistoryservice/webapp/TestAHSWebServices.java index f985fe4..e87b733 100644 --- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-applicationhistoryservice/src/test/java/org/apache/hadoop/yarn/server/applicationhistoryservice/webapp/TestAHSWebServices.java +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-applicationhistoryservice/src/test/java/org/apache/hadoop/yarn/server/applicationhistoryservice/webapp/TestAHSWebServices.java @@ -501,6 +501,7 @@ public void testSingleContainer() throws Exception { @Test(timeout = 10000) public void testContainerLogsForFinishedApps() throws Exception { String fileName = "syslog"; + String fileName2 = "stderr"; String user = "user1"; UserGroupInformation ugi = UserGroupInformation.createRemoteUser("user1"); NodeId nodeId = NodeId.newInstance("test host", 100); @@ -522,6 +523,7 @@ public void testContainerLogsForFinishedApps() throws Exception { } assertTrue(fs.mkdirs(appLogsDir)); + List fileContexts = new ArrayList(); // create container logs in local log file dir // create two container log files. We can get containerInfo // for container1 from AHS, but can not get such info for @@ -529,11 +531,22 @@ public void testContainerLogsForFinishedApps() throws Exception { ApplicationAttemptId appAttemptId = ApplicationAttemptId.newInstance(appId, 1); ContainerId containerId1 = ContainerId.newContainerId(appAttemptId, 1); - ContainerId containerId100 = ContainerId.newContainerId(appAttemptId, 100); - createContainerLogInLocalDir(appLogsDir, containerId1, fs, fileName, + LogFileContext fileContext1 = new LogFileContext(fileName, ("Hello." + containerId1)); - createContainerLogInLocalDir(appLogsDir, containerId100, fs, fileName, + LogFileContext fileContext2 = new LogFileContext(fileName2, + ("Error." + containerId1)); + fileContexts.add(fileContext1); + fileContexts.add(fileContext2); + createContainerLogInLocalDir(appLogsDir, containerId1, fs, fileContexts); + ContainerId containerId100 = ContainerId.newContainerId(appAttemptId, 100); + fileContexts.clear(); + fileContext1 = new LogFileContext(fileName, ("Hello." + containerId100)); + fileContext2 = new LogFileContext(fileName2, + ("Error." + containerId100)); + fileContexts.add(fileContext1); + fileContexts.add(fileContext2); + createContainerLogInLocalDir(appLogsDir, containerId100, fs, fileContexts); // upload container logs to remote log dir Path path = new Path(conf.get(YarnConfiguration.NM_REMOTE_APP_LOG_DIR) + @@ -559,6 +572,17 @@ public void testContainerLogsForFinishedApps() throws Exception { String responseText = response.getEntity(String.class); assertTrue(responseText.contains("Hello." + containerId1)); + r = resource(); + response = r.path("ws").path("v1") + .path("applicationhistory").path("containerlogs") + .path(containerId1.toString()).path(".*") + .queryParam("user.name", user) + .accept(MediaType.TEXT_PLAIN) + .get(ClientResponse.class); + responseText = response.getEntity(String.class); + assertTrue(responseText.contains("Hello." + containerId1)); + assertTrue(responseText.contains("Error." + containerId1)); + // test whether we can find container log from remote diretory if // the containerInfo for this container could not be fetched from AHS. r = resource(); @@ -571,6 +595,17 @@ public void testContainerLogsForFinishedApps() throws Exception { responseText = response.getEntity(String.class); assertTrue(responseText.contains("Hello." + containerId100)); + r = resource(); + response = r.path("ws").path("v1") + .path("applicationhistory").path("containerlogs") + .path(containerId100.toString()).path(".*") + .queryParam("user.name", user) + .accept(MediaType.TEXT_PLAIN) + .get(ClientResponse.class); + responseText = response.getEntity(String.class); + assertTrue(responseText.contains("Hello." + containerId100)); + assertTrue(responseText.contains("Error." + containerId100)); + // create an application which can not be found from AHS ApplicationId appId100 = ApplicationId.newInstance(0, 100); appLogsDir = new Path(rootLogDirPath, appId100.toString()); @@ -582,8 +617,15 @@ public void testContainerLogsForFinishedApps() throws Exception { ApplicationAttemptId.newInstance(appId100, 1); ContainerId containerId1ForApp100 = ContainerId .newContainerId(appAttemptId100, 1); + fileContexts.clear(); + fileContext1 = new LogFileContext(fileName, + ("Hello." + containerId1ForApp100)); + fileContext2 = new LogFileContext(fileName2, + ("Error." + containerId1ForApp100)); + fileContexts.add(fileContext1); + fileContexts.add(fileContext2); createContainerLogInLocalDir(appLogsDir, containerId1ForApp100, fs, - fileName, ("Hello." + containerId1ForApp100)); + fileContexts); path = new Path(conf.get(YarnConfiguration.NM_REMOTE_APP_LOG_DIR) + user + "/logs/" + appId100.toString()); if (fs.exists(path)) { @@ -601,20 +643,34 @@ public void testContainerLogsForFinishedApps() throws Exception { .get(ClientResponse.class); responseText = response.getEntity(String.class); assertTrue(responseText.contains("Hello." + containerId1ForApp100)); + + r = resource(); + response = r.path("ws").path("v1") + .path("applicationhistory").path("containerlogs") + .path(containerId1ForApp100.toString()).path(".*") + .queryParam("user.name", user) + .accept(MediaType.TEXT_PLAIN) + .get(ClientResponse.class); + responseText = response.getEntity(String.class); + assertTrue(responseText.contains("Hello." + containerId1ForApp100)); + assertTrue(responseText.contains("Error." + containerId1ForApp100)); } private static void createContainerLogInLocalDir(Path appLogsDir, - ContainerId containerId, FileSystem fs, String fileName, String content) + ContainerId containerId, FileSystem fs, List fileContexts) throws Exception { Path containerLogsDir = new Path(appLogsDir, containerId.toString()); if (fs.exists(containerLogsDir)) { fs.delete(containerLogsDir, true); } assertTrue(fs.mkdirs(containerLogsDir)); - Writer writer = - new FileWriter(new File(containerLogsDir.toString(), fileName)); - writer.write(content); - writer.close(); + for (LogFileContext fileContext : fileContexts) { + Writer writer = + new FileWriter(new File(containerLogsDir.toString(), + fileContext.getFileName())); + writer.write(fileContext.getFileContent()); + writer.close(); + } } private static void uploadContainerLogIntoRemoteDir(UserGroupInformation ugi, @@ -670,4 +726,25 @@ private static String getRedirectURL(String url) { } return redirectUrl; } + + private class LogFileContext { + private String fileName; + private String fileContent; + public LogFileContext(String name, String content) { + this.setFileName(name); + this.setFileContent(content); + } + public String getFileName() { + return fileName; + } + public void setFileName(String fileName) { + this.fileName = fileName; + } + public String getFileContent() { + return fileContent; + } + public void setFileContent(String fileContent) { + this.fileContent = fileContent; + } + } } diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/webapp/NMWebServices.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/webapp/NMWebServices.java index 57e729c..0a675f7 100644 --- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/webapp/NMWebServices.java +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/webapp/NMWebServices.java @@ -21,8 +21,10 @@ import java.io.FileInputStream; import java.io.IOException; import java.io.OutputStream; +import java.util.ArrayList; +import java.util.List; import java.util.Map.Entry; - +import java.util.regex.Pattern; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.ws.rs.GET; @@ -37,7 +39,7 @@ import javax.ws.rs.core.Response.Status; import javax.ws.rs.core.StreamingOutput; import javax.ws.rs.core.UriInfo; - +import org.apache.commons.io.IOUtils; import org.apache.hadoop.classification.InterfaceAudience.Public; import org.apache.hadoop.classification.InterfaceStability.Unstable; import org.apache.hadoop.yarn.api.records.ApplicationId; @@ -215,39 +217,48 @@ public ContainerInfo getNodeContainer(@javax.ws.rs.core.Context @Produces({ MediaType.TEXT_PLAIN }) @Public @Unstable - public Response getLogs(@PathParam("containerid") String containerIdStr, - @PathParam("filename") String filename, - @QueryParam("download") String download) { - ContainerId containerId; + public Response getLogs(@javax.ws.rs.core.Context + HttpServletRequest hsr, @PathParam("containerid") + final String containerIdStr, @PathParam("filename") String filename, + @QueryParam("download") String download) throws IOException { + ContainerId tempId; try { - containerId = ConverterUtils.toContainerId(containerIdStr); + tempId = ConverterUtils.toContainerId(containerIdStr); } catch (IllegalArgumentException ex) { return Response.status(Status.BAD_REQUEST).build(); } - - File logFile = null; - try { - logFile = ContainerLogsUtils.getContainerLogFile( - containerId, filename, request.getRemoteUser(), nmContext); - } catch (NotFoundException ex) { - return Response.status(Status.NOT_FOUND).entity(ex.getMessage()).build(); - } catch (YarnException ex) { - return Response.serverError().entity(ex.getMessage()).build(); + final ContainerId containerId = tempId; + ContainerInfo containerInfo = getNodeContainer(hsr, containerIdStr); + final List matchedFiles = findMatchingFiles(filename, + containerInfo.getContainerLogFiles()); + if (matchedFiles.isEmpty()) { + String errorMessage = "Can not find any log files matching " + + "the pattern: " + filename + " on the local disk."; + return Response.status(Status.NOT_FOUND).entity(errorMessage).build(); } + boolean downloadFile = parseBooleanParam(download); - try { - final FileInputStream fis = ContainerLogsUtils.openLogFileForRead( - containerIdStr, logFile, nmContext); - - StreamingOutput stream = new StreamingOutput() { + StreamingOutput stream = new StreamingOutput() { @Override public void write(OutputStream os) throws IOException, WebApplicationException { int bufferSize = 65536; byte[] buf = new byte[bufferSize]; - int len; - while ((len = fis.read(buf, 0, bufferSize)) > 0) { - os.write(buf, 0, len); + File logFile = null; + for (String file : matchedFiles) { + try { + logFile = ContainerLogsUtils.getContainerLogFile( + containerId, file, request.getRemoteUser(), nmContext); + } catch (YarnException | NotFoundException ex) { + continue; + } + FileInputStream fis = ContainerLogsUtils.openLogFileForRead( + containerIdStr, logFile, nmContext); + int len; + os.write(("\n" + file + "\n").getBytes()); + while ((len = fis.read(buf, 0, bufferSize)) > 0) { + os.write(buf, 0, len); + } } os.flush(); } @@ -256,12 +267,21 @@ public void write(OutputStream os) throws IOException, if (downloadFile) { resp.header("Content-Type", "application/octet-stream"); resp.header("Content-Disposition", "attachment; filename=" - + logFile.getName()); + + containerIdStr); } return resp.build(); - } catch (IOException ex) { - return Response.serverError().entity(ex.getMessage()).build(); + } + + private List findMatchingFiles(String filename, + List containerLogFiles) { + Pattern filterPattern = Pattern.compile(filename); + List files = new ArrayList(); + for (String logFile : containerLogFiles) { + if (filterPattern.matcher(logFile).find()) { + files.add(logFile); + } } + return files; } private boolean parseBooleanParam(String param) { diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/webapp/TestNMWebServices.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/webapp/TestNMWebServices.java index c10d4c8..1cbabfc 100644 --- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/webapp/TestNMWebServices.java +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/webapp/TestNMWebServices.java @@ -23,10 +23,11 @@ import static org.junit.Assert.fail; import java.io.File; +import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; import java.io.StringReader; - +import java.io.Writer; import javax.ws.rs.core.MediaType; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; @@ -313,18 +314,19 @@ public void testSingleNodesXML() throws JSONException, Exception { assertEquals("incorrect number of elements", 1, nodes.getLength()); verifyNodesXML(nodes); } - + @Test public void testContainerLogs() throws IOException { WebResource r = resource(); - final ContainerId containerId = BuilderUtils.newContainerId(0, 0, 0, 0); - final String containerIdStr = BuilderUtils.newContainerId(0, 0, 0, 0) - .toString(); + final ContainerId containerId = BuilderUtils.newContainerId(0, 0, 0, 1); + final String containerIdStr = containerId.toString(); final ApplicationAttemptId appAttemptId = containerId.getApplicationAttemptId(); final ApplicationId appId = appAttemptId.getApplicationId(); final String appIdStr = appId.toString(); final String filename = "logfile1"; final String logMessage = "log message\n"; + final String filename2 = "stderr"; + final String errorMessage = "error message\n"; nmContext.getApplications().put(appId, new ApplicationImpl(null, "user", appId, null, nmContext)); @@ -333,7 +335,7 @@ public void testContainerLogs() throws IOException { container.setState(ContainerState.RUNNING); nmContext.getContainers().put(containerId, container); - // write out log file + // write out log file for "logfile1" Path path = dirsHandler.getLogPathForWrite( ContainerLaunch.getRelativeContainerLogDir( appIdStr, containerIdStr) + "/" + filename, false); @@ -345,19 +347,37 @@ public void testContainerLogs() throws IOException { pw.print(logMessage); pw.close(); + // write out log file for "stderr" + path = dirsHandler.getLogPathForWrite( + ContainerLaunch.getRelativeContainerLogDir( + appIdStr, containerIdStr) + "/" + filename2, false); + + logFile = new File(path.toUri().getPath()); + logFile.deleteOnExit(); + pw = new PrintWriter(logFile); + pw.print(errorMessage); + pw.close(); + // ask for it ClientResponse response = r.path("ws").path("v1").path("node") .path("containerlogs").path(containerIdStr).path(filename) .accept(MediaType.TEXT_PLAIN).get(ClientResponse.class); String responseText = response.getEntity(String.class); - assertEquals(logMessage, responseText); + Assert.assertTrue(responseText.contains(logMessage)); + + response = r.path("ws").path("v1").path("node") + .path("containerlogs").path(containerIdStr).path(".*") + .accept(MediaType.TEXT_PLAIN).get(ClientResponse.class); + responseText = response.getEntity(String.class); + Assert.assertTrue(responseText.contains(logMessage)); + Assert.assertTrue(responseText.contains(errorMessage)); // ask and download it response = r.path("ws").path("v1").path("node").path("containerlogs") .path(containerIdStr).path(filename).queryParam("download", "true") .accept(MediaType.TEXT_PLAIN).get(ClientResponse.class); responseText = response.getEntity(String.class); - assertEquals(logMessage, responseText); + Assert.assertTrue(responseText.contains(logMessage)); assertEquals(200, response.getStatus()); assertEquals("application/octet-stream", response.getType().toString()); @@ -367,7 +387,8 @@ public void testContainerLogs() throws IOException { .accept(MediaType.TEXT_PLAIN).get(ClientResponse.class); Assert.assertEquals(Status.NOT_FOUND.getStatusCode(), response.getStatus()); responseText = response.getEntity(String.class); - assertTrue(responseText.contains("Cannot find this log on the local disk.")); + assertTrue(responseText.contains("Can not find any log files " + + "matching the pattern")); // After container is completed, it is removed from nmContext nmContext.getContainers().remove(containerId); @@ -376,8 +397,8 @@ public void testContainerLogs() throws IOException { r.path("ws").path("v1").path("node").path("containerlogs") .path(containerIdStr).path(filename).accept(MediaType.TEXT_PLAIN) .get(ClientResponse.class); - responseText = response.getEntity(String.class); - assertEquals(logMessage, responseText); + assertEquals(Status.INTERNAL_SERVER_ERROR.getStatusCode(), + response.getStatus()); } public void verifyNodesXML(NodeList nodes) throws JSONException, Exception {