STORM-1280 port backtype.storm.daemon.logviewer to java * address checkstyle violations * also addressed this from DRPC so far, but needs to decide for DRPC vs Drpc
Project: http://git-wip-us.apache.org/repos/asf/storm/repo Commit: http://git-wip-us.apache.org/repos/asf/storm/commit/6a041a98 Tree: http://git-wip-us.apache.org/repos/asf/storm/tree/6a041a98 Diff: http://git-wip-us.apache.org/repos/asf/storm/diff/6a041a98 Branch: refs/heads/master Commit: 6a041a98fc7fe04db3f520fa283192d96ffb0a56 Parents: 7f84691 Author: Jungtaek Lim <[email protected]> Authored: Mon Jul 17 13:35:51 2017 +0900 Committer: Jungtaek Lim <[email protected]> Committed: Mon Jul 17 13:35:51 2017 +0900 ---------------------------------------------------------------------- pom.xml | 6 + storm-buildtools/storm_checkstyle.xml | 6 + storm-webapp/pom.xml | 2 +- .../daemon/common/JsonResponseBuilder.java | 14 +- .../daemon/drpc/webapp/DRPCApplication.java | 2 +- .../daemon/logviewer/LogviewerConstant.java | 8 +- .../storm/daemon/logviewer/LogviewerServer.java | 20 ++- .../handler/LogviewerLogDownloadHandler.java | 30 +++- .../handler/LogviewerLogPageHandler.java | 116 ++++++++++---- .../handler/LogviewerLogSearchHandler.java | 144 +++++++++++------ .../handler/LogviewerProfileHandler.java | 83 ++++++---- .../logviewer/utils/DirectoryCleaner.java | 51 ++++--- .../daemon/logviewer/utils/LogCleaner.java | 64 ++++---- .../logviewer/utils/LogFileDownloader.java | 18 ++- .../utils/LogviewerResponseBuilder.java | 59 +++++-- .../logviewer/utils/ResourceAuthorizer.java | 48 ++++-- .../daemon/logviewer/utils/WorkerLogs.java | 82 +++++++--- .../logviewer/webapp/LogviewerApplication.java | 28 ++-- .../logviewer/webapp/LogviewerResource.java | 77 ++++++++-- .../daemon/utils/ListFunctionalSupport.java | 53 ++++++- .../apache/storm/daemon/utils/StreamUtil.java | 9 ++ .../apache/storm/daemon/utils/URLBuilder.java | 43 ------ .../apache/storm/daemon/utils/UrlBuilder.java | 53 +++++++ .../storm/daemon/drpc/DRPCServerTest.java | 13 +- .../storm/daemon/logviewer/LogviewerTest.java | 51 ------- .../handler/LogviewerLogPageHandlerTest.java | 32 ++-- .../handler/LogviewerLogSearchHandlerTest.java | 153 ++++++++++--------- .../testsupport/ArgumentsVerifier.java | 15 +- .../testsupport/MockDirectoryBuilder.java | 12 +- .../logviewer/testsupport/MockFileBuilder.java | 12 +- .../daemon/logviewer/utils/LogCleanerTest.java | 61 ++++---- .../logviewer/utils/ResourceAuthorizerTest.java | 24 +-- .../daemon/logviewer/utils/WorkerLogsTest.java | 18 +-- 33 files changed, 912 insertions(+), 495 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/storm/blob/6a041a98/pom.xml ---------------------------------------------------------------------- diff --git a/pom.xml b/pom.xml index ffee0cd..9d890d9 100644 --- a/pom.xml +++ b/pom.xml @@ -486,6 +486,12 @@ <exclude>**/derby.log</exclude> <exclude>**/src/dev/**</exclude> + <!-- webapp test resources --> + <exclude>**/src/test/resources/test-3072.log.test</exclude> + <exclude>**/src/test/resources/small-worker.log.test</exclude> + <exclude>**/src/test/resources/test-worker.log.test</exclude> + <exclude>**/src/test/resources/logviewer-search-context-tests.log.test</exclude> + <!-- StormSQL --> <exclude>**/src/codegen/config.fmpp</exclude> <exclude>**/src/codegen/data/Parser.tdd</exclude> http://git-wip-us.apache.org/repos/asf/storm/blob/6a041a98/storm-buildtools/storm_checkstyle.xml ---------------------------------------------------------------------- diff --git a/storm-buildtools/storm_checkstyle.xml b/storm-buildtools/storm_checkstyle.xml index 248e3ce..3b91be9 100644 --- a/storm-buildtools/storm_checkstyle.xml +++ b/storm-buildtools/storm_checkstyle.xml @@ -46,6 +46,9 @@ --> <module name = "Checker"> + <!-- Filter out Checkstyle warnings that have been suppressed with the @SuppressWarnings annotation --> + <module name="SuppressWarningsFilter" /> + <property name="charset" value="UTF-8"/> <property name="severity" value="warning"/> @@ -58,6 +61,9 @@ </module> <module name="TreeWalker"> + <!-- Make the @SuppressWarnings annotations available to Checkstyle --> + <module name="SuppressWarningsHolder" /> + <module name="OuterTypeFilename"/> <module name="IllegalTokenText"> <property name="tokens" value="STRING_LITERAL, CHAR_LITERAL"/> http://git-wip-us.apache.org/repos/asf/storm/blob/6a041a98/storm-webapp/pom.xml ---------------------------------------------------------------------- diff --git a/storm-webapp/pom.xml b/storm-webapp/pom.xml index eeb6ee6..57ccb88 100644 --- a/storm-webapp/pom.xml +++ b/storm-webapp/pom.xml @@ -143,7 +143,7 @@ <artifactId>maven-checkstyle-plugin</artifactId> <!--Note - the version would be inherited--> <configuration> - <maxAllowedViolations>500</maxAllowedViolations> + <maxAllowedViolations>4</maxAllowedViolations> </configuration> </plugin> <plugin> http://git-wip-us.apache.org/repos/asf/storm/blob/6a041a98/storm-webapp/src/main/java/org/apache/storm/daemon/common/JsonResponseBuilder.java ---------------------------------------------------------------------- diff --git a/storm-webapp/src/main/java/org/apache/storm/daemon/common/JsonResponseBuilder.java b/storm-webapp/src/main/java/org/apache/storm/daemon/common/JsonResponseBuilder.java index 2048d81..dc39bc6 100644 --- a/storm-webapp/src/main/java/org/apache/storm/daemon/common/JsonResponseBuilder.java +++ b/storm-webapp/src/main/java/org/apache/storm/daemon/common/JsonResponseBuilder.java @@ -18,12 +18,15 @@ package org.apache.storm.daemon.common; -import org.apache.storm.ui.UIHelpers; - -import javax.ws.rs.core.Response; import java.util.Collections; import java.util.Map; +import javax.ws.rs.core.Response; +import org.apache.storm.ui.UIHelpers; + +/** + * Response builder for JSON. It utilizes {@link UIHelpers} to construct JSON body and headers. + */ public class JsonResponseBuilder { private Object data; private String callback; @@ -56,6 +59,11 @@ public class JsonResponseBuilder { return this; } + /** + * Build a Response based on given parameters. + * + * @return A Response. + */ public Response build() { String body = UIHelpers.getJsonResponseBody(data, callback, needSerialize); Map<String, Object> respHeaders = UIHelpers.getJsonResponseHeaders(callback, headers); http://git-wip-us.apache.org/repos/asf/storm/blob/6a041a98/storm-webapp/src/main/java/org/apache/storm/daemon/drpc/webapp/DRPCApplication.java ---------------------------------------------------------------------- diff --git a/storm-webapp/src/main/java/org/apache/storm/daemon/drpc/webapp/DRPCApplication.java b/storm-webapp/src/main/java/org/apache/storm/daemon/drpc/webapp/DRPCApplication.java index db42310..15cc4d7 100644 --- a/storm-webapp/src/main/java/org/apache/storm/daemon/drpc/webapp/DRPCApplication.java +++ b/storm-webapp/src/main/java/org/apache/storm/daemon/drpc/webapp/DRPCApplication.java @@ -24,8 +24,8 @@ import java.util.Set; import javax.ws.rs.ApplicationPath; import javax.ws.rs.core.Application; -import org.apache.storm.daemon.drpc.DRPC; import org.apache.storm.daemon.common.AuthorizationExceptionMapper; +import org.apache.storm.daemon.drpc.DRPC; @ApplicationPath("") public class DRPCApplication extends Application { http://git-wip-us.apache.org/repos/asf/storm/blob/6a041a98/storm-webapp/src/main/java/org/apache/storm/daemon/logviewer/LogviewerConstant.java ---------------------------------------------------------------------- diff --git a/storm-webapp/src/main/java/org/apache/storm/daemon/logviewer/LogviewerConstant.java b/storm-webapp/src/main/java/org/apache/storm/daemon/logviewer/LogviewerConstant.java index 612e0e2..d1a15be 100644 --- a/storm-webapp/src/main/java/org/apache/storm/daemon/logviewer/LogviewerConstant.java +++ b/storm-webapp/src/main/java/org/apache/storm/daemon/logviewer/LogviewerConstant.java @@ -18,6 +18,12 @@ package org.apache.storm.daemon.logviewer; -public class LogviewerConstant { +/** + * Constants which are used across logviewer related classes. + */ +public final class LogviewerConstant { + private LogviewerConstant() { + } + public static final int DEFAULT_BYTES_PER_PAGE = 51200; } http://git-wip-us.apache.org/repos/asf/storm/blob/6a041a98/storm-webapp/src/main/java/org/apache/storm/daemon/logviewer/LogviewerServer.java ---------------------------------------------------------------------- diff --git a/storm-webapp/src/main/java/org/apache/storm/daemon/logviewer/LogviewerServer.java b/storm-webapp/src/main/java/org/apache/storm/daemon/logviewer/LogviewerServer.java index 1b53809..4f9834d 100644 --- a/storm-webapp/src/main/java/org/apache/storm/daemon/logviewer/LogviewerServer.java +++ b/storm-webapp/src/main/java/org/apache/storm/daemon/logviewer/LogviewerServer.java @@ -18,8 +18,17 @@ package org.apache.storm.daemon.logviewer; +import static org.apache.storm.DaemonConfig.UI_HEADER_BUFFER_BYTES; + import com.codahale.metrics.Meter; import com.google.common.annotations.VisibleForTesting; + +import java.io.File; +import java.io.IOException; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + import org.apache.storm.DaemonConfig; import org.apache.storm.daemon.logviewer.utils.DirectoryCleaner; import org.apache.storm.daemon.logviewer.utils.LogCleaner; @@ -40,14 +49,9 @@ import org.glassfish.jersey.servlet.ServletContainer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.File; -import java.io.IOException; -import java.util.Arrays; -import java.util.List; -import java.util.Map; - -import static org.apache.storm.DaemonConfig.UI_HEADER_BUFFER_BYTES; - +/** + * The main entry of Logviewer. + */ public class LogviewerServer implements AutoCloseable { private static final Logger LOG = LoggerFactory.getLogger(LogviewerServer.class); private static final Meter meterShutdownCalls = StormMetricsRegistry.registerMeter("drpc:num-shutdown-calls"); http://git-wip-us.apache.org/repos/asf/storm/blob/6a041a98/storm-webapp/src/main/java/org/apache/storm/daemon/logviewer/handler/LogviewerLogDownloadHandler.java ---------------------------------------------------------------------- diff --git a/storm-webapp/src/main/java/org/apache/storm/daemon/logviewer/handler/LogviewerLogDownloadHandler.java b/storm-webapp/src/main/java/org/apache/storm/daemon/logviewer/handler/LogviewerLogDownloadHandler.java index 7529297..7b2fbcb 100644 --- a/storm-webapp/src/main/java/org/apache/storm/daemon/logviewer/handler/LogviewerLogDownloadHandler.java +++ b/storm-webapp/src/main/java/org/apache/storm/daemon/logviewer/handler/LogviewerLogDownloadHandler.java @@ -18,28 +18,52 @@ package org.apache.storm.daemon.logviewer.handler; +import java.io.IOException; +import javax.ws.rs.core.Response; + import org.apache.storm.daemon.logviewer.utils.LogFileDownloader; import org.apache.storm.daemon.logviewer.utils.ResourceAuthorizer; import org.apache.storm.daemon.logviewer.utils.WorkerLogs; -import javax.ws.rs.core.Response; -import java.io.IOException; - public class LogviewerLogDownloadHandler { private WorkerLogs workerLogs; private final LogFileDownloader logFileDownloadHelper; + /** + * Constructor. + * + * @param logRoot root worker log directory + * @param daemonLogRoot root daemon log directory + * @param workerLogs {@link WorkerLogs} + * @param resourceAuthorizer {@link ResourceAuthorizer} + */ public LogviewerLogDownloadHandler(String logRoot, String daemonLogRoot, WorkerLogs workerLogs, ResourceAuthorizer resourceAuthorizer) { this.workerLogs = workerLogs; this.logFileDownloadHelper = new LogFileDownloader(logRoot, daemonLogRoot, resourceAuthorizer); } + /** + * Download an worker log. + * + * @param fileName file to download + * @param user username + * @return a Response which lets browsers download that file. + * @see {@link LogFileDownloader#downloadFile(String, String, boolean)} + */ public Response downloadLogFile(String fileName, String user) throws IOException { workerLogs.setLogFilePermission(fileName); return logFileDownloadHelper.downloadFile(fileName, user, false); } + /** + * Download a daemon log. + * + * @param fileName file to download + * @param user username + * @return a Response which lets browsers download that file. + * @see {@link LogFileDownloader#downloadFile(String, String, boolean)} + */ public Response downloadDaemonLogFile(String fileName, String user) throws IOException { return logFileDownloadHelper.downloadFile(fileName, user, true); } http://git-wip-us.apache.org/repos/asf/storm/blob/6a041a98/storm-webapp/src/main/java/org/apache/storm/daemon/logviewer/handler/LogviewerLogPageHandler.java ---------------------------------------------------------------------- diff --git a/storm-webapp/src/main/java/org/apache/storm/daemon/logviewer/handler/LogviewerLogPageHandler.java b/storm-webapp/src/main/java/org/apache/storm/daemon/logviewer/handler/LogviewerLogPageHandler.java index d2488f6..04b1a13 100644 --- a/storm-webapp/src/main/java/org/apache/storm/daemon/logviewer/handler/LogviewerLogPageHandler.java +++ b/storm-webapp/src/main/java/org/apache/storm/daemon/logviewer/handler/LogviewerLogPageHandler.java @@ -18,22 +18,26 @@ package org.apache.storm.daemon.logviewer.handler; +import static j2html.TagCreator.a; +import static j2html.TagCreator.body; +import static j2html.TagCreator.div; +import static j2html.TagCreator.form; +import static j2html.TagCreator.h3; +import static j2html.TagCreator.head; +import static j2html.TagCreator.html; +import static j2html.TagCreator.input; +import static j2html.TagCreator.link; +import static j2html.TagCreator.p; +import static j2html.TagCreator.pre; +import static j2html.TagCreator.select; +import static j2html.TagCreator.text; +import static j2html.TagCreator.title; +import static java.util.stream.Collectors.toCollection; +import static java.util.stream.Collectors.toList; +import static org.apache.commons.lang.StringEscapeUtils.escapeHtml; + import j2html.TagCreator; import j2html.tags.DomContent; -import org.apache.commons.lang.StringUtils; -import org.apache.storm.daemon.logviewer.utils.DirectoryCleaner; -import org.apache.storm.daemon.logviewer.LogviewerConstant; -import org.apache.storm.daemon.logviewer.utils.LogviewerResponseBuilder; -import org.apache.storm.daemon.logviewer.utils.ResourceAuthorizer; -import org.apache.storm.daemon.logviewer.utils.WorkerLogs; -import org.apache.storm.daemon.utils.StreamUtil; -import org.apache.storm.daemon.utils.URLBuilder; -import org.apache.storm.ui.InvalidRequestException; -import org.apache.storm.ui.UIHelpers; -import org.apache.storm.utils.ConfigUtils; -import org.apache.storm.utils.ServerUtils; -import org.apache.storm.utils.Utils; -import org.jooq.lambda.Unchecked; import java.io.ByteArrayOutputStream; import java.io.File; @@ -54,10 +58,20 @@ import java.util.zip.GZIPInputStream; import javax.ws.rs.core.Response; -import static j2html.TagCreator.*; -import static java.util.stream.Collectors.toCollection; -import static java.util.stream.Collectors.toList; -import static org.apache.commons.lang.StringEscapeUtils.escapeHtml; +import org.apache.commons.lang.StringUtils; +import org.apache.storm.daemon.logviewer.LogviewerConstant; +import org.apache.storm.daemon.logviewer.utils.DirectoryCleaner; +import org.apache.storm.daemon.logviewer.utils.LogviewerResponseBuilder; +import org.apache.storm.daemon.logviewer.utils.ResourceAuthorizer; +import org.apache.storm.daemon.logviewer.utils.WorkerLogs; +import org.apache.storm.daemon.utils.StreamUtil; +import org.apache.storm.daemon.utils.UrlBuilder; +import org.apache.storm.ui.InvalidRequestException; +import org.apache.storm.ui.UIHelpers; +import org.apache.storm.utils.ConfigUtils; +import org.apache.storm.utils.ServerUtils; +import org.apache.storm.utils.Utils; +import org.jooq.lambda.Unchecked; public class LogviewerLogPageHandler { private final String logRoot; @@ -65,6 +79,14 @@ public class LogviewerLogPageHandler { private final WorkerLogs workerLogs; private final ResourceAuthorizer resourceAuthorizer; + /** + * Constructor. + * + * @param logRoot root worker log directory + * @param daemonLogRoot root daemon log directory + * @param workerLogs {@link WorkerLogs} + * @param resourceAuthorizer {@link ResourceAuthorizer} + */ public LogviewerLogPageHandler(String logRoot, String daemonLogRoot, WorkerLogs workerLogs, ResourceAuthorizer resourceAuthorizer) { @@ -74,6 +96,16 @@ public class LogviewerLogPageHandler { this.resourceAuthorizer = resourceAuthorizer; } + /** + * Enumerate worker log files for given criteria. + * + * @param user username + * @param port worker's port, null for all workers + * @param topologyId topology ID, null for all topologies + * @param callback callback for JSONP + * @param origin origin + * @return list of worker logs for given criteria + */ public Response listLogFiles(String user, Integer port, String topologyId, String callback, String origin) throws IOException { List<File> fileResults = null; if (topologyId == null) { @@ -130,7 +162,18 @@ public class LogviewerLogPageHandler { return LogviewerResponseBuilder.buildSuccessJsonResponse(files, callback, origin); } - public Response logPage(String fileName, Integer start, Integer length, String grep, String user) throws IOException, InvalidRequestException { + /** + * Provides a worker log file to view. + * + * @param fileName file to view + * @param start start offset, can be null + * @param length length to read in this page, can be null + * @param grep search string if request is a result of the search, can be null + * @param user username + * @return HTML view page of worker log + */ + public Response logPage(String fileName, Integer start, Integer length, String grep, String user) + throws IOException, InvalidRequestException { String rootDir = logRoot; if (resourceAuthorizer.isUserAllowedToAccessFile(fileName, user)) { workerLogs.setLogFilePermission(fileName); @@ -141,8 +184,6 @@ public class LogviewerLogPageHandler { File topoDir = file.getParentFile().getParentFile(); if (file.exists() && new File(rootDir).getCanonicalFile().equals(topoDir.getParentFile())) { - long fileLength = isZipFile ? ServerUtils.zipFileSize(file) : file.length(); - SortedSet<File> logFiles; try { logFiles = Arrays.stream(topoDir.listFiles()) @@ -169,6 +210,7 @@ public class LogviewerLogPageHandler { logString = escapeHtml("This is a binary file and cannot display! You may download the full file."); } + long fileLength = isZipFile ? ServerUtils.zipFileSize(file) : file.length(); start = start != null ? start : Long.valueOf(fileLength - length).intValue(); List<DomContent> bodyContents = new ArrayList<>(); @@ -209,15 +251,24 @@ public class LogviewerLogPageHandler { } } - public Response daemonLogPage(String fileName, Integer start, Integer length, String grep, String user) throws IOException, InvalidRequestException { + /** + * Provides a daemon log file to view. + * + * @param fileName file to view + * @param start start offset, can be null + * @param length length to read in this page, can be null + * @param grep search string if request is a result of the search, can be null + * @param user username + * @return HTML view page of daemon log + */ + public Response daemonLogPage(String fileName, Integer start, Integer length, String grep, String user) + throws IOException, InvalidRequestException { String rootDir = daemonLogRoot; File file = new File(rootDir, fileName).getCanonicalFile(); String path = file.getCanonicalPath(); boolean isZipFile = path.endsWith(".gz"); if (file.exists() && new File(rootDir).getCanonicalFile().equals(file.getParentFile())) { - long fileLength = isZipFile ? ServerUtils.zipFileSize(file) : file.length(); - // all types of files included List<File> logFiles = Arrays.stream(new File(rootDir).listFiles()) .filter(File::isFile) @@ -239,6 +290,7 @@ public class LogviewerLogPageHandler { logString = escapeHtml("This is a binary file and cannot display! You may download the full file."); } + long fileLength = isZipFile ? ServerUtils.zipFileSize(file) : file.length(); start = start != null ? start : Long.valueOf(fileLength - length).intValue(); List<DomContent> bodyContents = new ArrayList<>(); @@ -331,36 +383,36 @@ public class LogviewerLogPageHandler { } private DomContent pagerLinks(String fileName, Integer start, Integer length, Integer fileLength, String type) { - int prevStart = Math.max(0, start - length); - int nextStart = fileLength > 0 ? Math.min(Math.max(0, fileLength - length), start + length) : start + length; - List<DomContent> btnLinks = new ArrayList<>(); - Map<String, Object> urlQueryParams = new HashMap<>(); urlQueryParams.put("file", fileName); urlQueryParams.put("start", Math.max(0, start - length)); urlQueryParams.put("length", length); - btnLinks.add(toButtonLink(URLBuilder.build("/api/v1/" + type, urlQueryParams), "Prev", prevStart < start)); + List<DomContent> btnLinks = new ArrayList<>(); + + int prevStart = Math.max(0, start - length); + btnLinks.add(toButtonLink(UrlBuilder.build("/api/v1/" + type, urlQueryParams), "Prev", prevStart < start)); urlQueryParams.clear(); urlQueryParams.put("file", fileName); urlQueryParams.put("start", 0); urlQueryParams.put("length", length); - btnLinks.add(toButtonLink(URLBuilder.build("/api/v1/" + type, urlQueryParams), "First")); + btnLinks.add(toButtonLink(UrlBuilder.build("/api/v1/" + type, urlQueryParams), "First")); urlQueryParams.clear(); urlQueryParams.put("file", fileName); urlQueryParams.put("length", length); - btnLinks.add(toButtonLink(URLBuilder.build("/api/v1/" + type, urlQueryParams), "Last")); + btnLinks.add(toButtonLink(UrlBuilder.build("/api/v1/" + type, urlQueryParams), "Last")); urlQueryParams.clear(); urlQueryParams.put("file", fileName); urlQueryParams.put("start", Math.min(Math.max(0, fileLength - length), start + length)); urlQueryParams.put("length", length); - btnLinks.add(toButtonLink(URLBuilder.build("/api/v1/" + type, urlQueryParams), "Next", nextStart > start)); + int nextStart = fileLength > 0 ? Math.min(Math.max(0, fileLength - length), start + length) : start + length; + btnLinks.add(toButtonLink(UrlBuilder.build("/api/v1/" + type, urlQueryParams), "Next", nextStart > start)); return div(btnLinks.toArray(new DomContent[]{})); } http://git-wip-us.apache.org/repos/asf/storm/blob/6a041a98/storm-webapp/src/main/java/org/apache/storm/daemon/logviewer/handler/LogviewerLogSearchHandler.java ---------------------------------------------------------------------- diff --git a/storm-webapp/src/main/java/org/apache/storm/daemon/logviewer/handler/LogviewerLogSearchHandler.java b/storm-webapp/src/main/java/org/apache/storm/daemon/logviewer/handler/LogviewerLogSearchHandler.java index 304c2d6..76f1683 100644 --- a/storm-webapp/src/main/java/org/apache/storm/daemon/logviewer/handler/LogviewerLogSearchHandler.java +++ b/storm-webapp/src/main/java/org/apache/storm/daemon/logviewer/handler/LogviewerLogSearchHandler.java @@ -18,29 +18,17 @@ package org.apache.storm.daemon.logviewer.handler; +import static java.util.stream.Collectors.toList; +import static org.apache.storm.daemon.utils.ListFunctionalSupport.drop; +import static org.apache.storm.daemon.utils.ListFunctionalSupport.first; +import static org.apache.storm.daemon.utils.ListFunctionalSupport.last; +import static org.apache.storm.daemon.utils.ListFunctionalSupport.rest; +import static org.apache.storm.daemon.utils.ListFunctionalSupport.takeLast; + import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.annotations.VisibleForTesting; -import org.apache.commons.lang.BooleanUtils; -import org.apache.commons.lang.StringUtils; -import org.apache.storm.DaemonConfig; -import org.apache.storm.daemon.logviewer.utils.DirectoryCleaner; -import org.apache.storm.daemon.common.JsonResponseBuilder; -import org.apache.storm.daemon.logviewer.LogviewerConstant; -import org.apache.storm.daemon.logviewer.utils.WorkerLogs; -import org.apache.storm.daemon.utils.StreamUtil; -import org.apache.storm.daemon.utils.URLBuilder; -import org.apache.storm.daemon.logviewer.utils.LogviewerResponseBuilder; -import org.apache.storm.daemon.logviewer.utils.ResourceAuthorizer; -import org.apache.storm.ui.InvalidRequestException; -import org.apache.storm.utils.ObjectReader; -import org.apache.storm.utils.ServerUtils; -import org.apache.storm.utils.Utils; -import org.json.simple.JSONAware; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import javax.ws.rs.core.Response; import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; @@ -58,15 +46,29 @@ import java.util.Map; import java.util.regex.Pattern; import java.util.zip.GZIPInputStream; -import static java.util.stream.Collectors.toList; -import static org.apache.storm.daemon.utils.ListFunctionalSupport.drop; -import static org.apache.storm.daemon.utils.ListFunctionalSupport.first; -import static org.apache.storm.daemon.utils.ListFunctionalSupport.last; -import static org.apache.storm.daemon.utils.ListFunctionalSupport.rest; -import static org.apache.storm.daemon.utils.ListFunctionalSupport.takeLast; +import javax.ws.rs.core.Response; + +import org.apache.commons.lang.BooleanUtils; +import org.apache.commons.lang.StringUtils; +import org.apache.storm.DaemonConfig; +import org.apache.storm.daemon.common.JsonResponseBuilder; +import org.apache.storm.daemon.logviewer.LogviewerConstant; +import org.apache.storm.daemon.logviewer.utils.DirectoryCleaner; +import org.apache.storm.daemon.logviewer.utils.LogviewerResponseBuilder; +import org.apache.storm.daemon.logviewer.utils.ResourceAuthorizer; +import org.apache.storm.daemon.logviewer.utils.WorkerLogs; +import org.apache.storm.daemon.utils.StreamUtil; +import org.apache.storm.daemon.utils.UrlBuilder; +import org.apache.storm.ui.InvalidRequestException; +import org.apache.storm.utils.ObjectReader; +import org.apache.storm.utils.ServerUtils; +import org.apache.storm.utils.Utils; +import org.json.simple.JSONAware; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class LogviewerLogSearchHandler { - private final static Logger LOG = LoggerFactory.getLogger(LogviewerLogSearchHandler.class); + private static final Logger LOG = LoggerFactory.getLogger(LogviewerLogSearchHandler.class); public static final int GREP_MAX_SEARCH_SIZE = 1024; public static final int GREP_BUF_SIZE = 2048; @@ -79,6 +81,14 @@ public class LogviewerLogSearchHandler { private final ResourceAuthorizer resourceAuthorizer; private final Integer logviewerPort; + /** + * Constructor. + * + * @param stormConf storm configuration + * @param logRoot log root directory + * @param daemonLogRoot daemon log root directory + * @param resourceAuthorizer {@link ResourceAuthorizer} + */ public LogviewerLogSearchHandler(Map<String, Object> stormConf, String logRoot, String daemonLogRoot, ResourceAuthorizer resourceAuthorizer) { this.stormConf = stormConf; @@ -89,6 +99,19 @@ public class LogviewerLogSearchHandler { this.logviewerPort = ObjectReader.getInt(stormConf.get(DaemonConfig.LOGVIEWER_PORT)); } + /** + * Search from a worker log file. + * + * @param fileName log file + * @param user username + * @param isDaemon whether the log file is regarding worker or daemon + * @param search search string + * @param numMatchesStr the count of maximum matches + * @param offsetStr start offset for log file + * @param callback callback for JSONP + * @param origin origin + * @return Response containing JSON content representing search result + */ public Response searchLogFile(String fileName, String user, boolean isDaemon, String search, String numMatchesStr, String offsetStr, String callback, String origin) throws IOException, InvalidRequestException { @@ -130,6 +153,21 @@ public class LogviewerLogSearchHandler { return response; } + /** + * Deep search across worker log files in a topology. + * + * @param topologyId topology ID + * @param user username + * @param search search string + * @param numMatchesStr the count of maximum matches + * @param portStr worker port, null or '*' if the request wants to search from all worker logs + * @param fileOffsetStr index (offset) of the log files + * @param offsetStr start offset for log file + * @param searchArchived true if the request wants to search also archived files, false if not + * @param callback callback for JSONP + * @param origin origin + * @return Response containing JSON content representing search result + */ public Response deepSearchLogsForTopology(String topologyId, String user, String search, String numMatchesStr, String portStr, String fileOffsetStr, String offsetStr, Boolean searchArchived, String callback, String origin) { @@ -178,8 +216,8 @@ public class LogviewerLogSearchHandler { if (!containsPort) { returnValue = new ArrayList<>(); } else { - File portDir = new File(rootDir + Utils.FILE_PATH_SEPARATOR + topologyId + - Utils.FILE_PATH_SEPARATOR + port); + File portDir = new File(rootDir + Utils.FILE_PATH_SEPARATOR + topologyId + + Utils.FILE_PATH_SEPARATOR + port); if (!portDir.exists() || logsForPort(user, portDir).isEmpty()) { returnValue = new ArrayList<>(); @@ -218,22 +256,19 @@ public class LogviewerLogSearchHandler { } @VisibleForTesting - Map<String,Object> substringSearchDaemonLog(File file, String searchString) throws InvalidRequestException { - return substringSearch(file, searchString, true, 10, 0); - } - - @VisibleForTesting Map<String,Object> substringSearch(File file, String searchString, int numMatches, int startByteOffset) throws InvalidRequestException { return substringSearch(file, searchString, false, numMatches, startByteOffset); } - private Map<String,Object> substringSearch(File file, String searchString, boolean isDaemon, Integer numMatches, Integer startByteOffset) throws InvalidRequestException { + private Map<String,Object> substringSearch(File file, String searchString, boolean isDaemon, Integer numMatches, + Integer startByteOffset) throws InvalidRequestException { try { if (StringUtils.isEmpty(searchString)) { throw new IllegalArgumentException("Precondition fails: search string should not be empty."); } if (searchString.getBytes("UTF-8").length > GREP_MAX_SEARCH_SIZE) { - throw new IllegalArgumentException("Precondition fails: the length of search string should be less than " + GREP_MAX_SEARCH_SIZE); + throw new IllegalArgumentException("Precondition fails: the length of search string should be less than " + + GREP_MAX_SEARCH_SIZE); } boolean isZipFile = file.getName().endsWith(".gz"); @@ -255,9 +290,8 @@ public class LogviewerLogSearchHandler { } ByteBuffer buf = ByteBuffer.allocate(GREP_BUF_SIZE); - byte[] bufArray = buf.array(); - int totalBytesRead = 0; - byte[] searchBytes = searchString.getBytes("UTF-8"); + final byte[] bufArray = buf.array(); + final byte[] searchBytes = searchString.getBytes("UTF-8"); numMatches = numMatches != null ? numMatches : 10; startByteOffset = startByteOffset != null ? startByteOffset : 0; @@ -273,6 +307,7 @@ public class LogviewerLogSearchHandler { Arrays.fill(bufArray, (byte) 0); + int totalBytesRead = 0; int bytesRead = stream.read(bufArray, 0, Math.min((int) fileLength, GREP_BUF_SIZE)); buf.limit(bytesRead); totalBytesRead += bytesRead; @@ -295,7 +330,7 @@ public class LogviewerLogSearchHandler { // The start index is positioned to find any possible // occurrence search string that did not quite fit in the // buffer on the previous read. - int newBufOffset = Math.min(buf.limit(), GREP_MAX_SEARCH_SIZE) - searchBytes.length; + final int newBufOffset = Math.min(buf.limit(), GREP_MAX_SEARCH_SIZE) - searchBytes.length; totalBytesRead = rotateGrepBuffer(buf, stream, totalBytesRead, file, fileLength); if (totalBytesRead < 0) { @@ -326,6 +361,10 @@ public class LogviewerLogSearchHandler { } } + @VisibleForTesting + Map<String,Object> substringSearchDaemonLog(File file, String searchString) throws InvalidRequestException { + return substringSearch(file, searchString, true, 10, 0); + } /** * Get the filtered, authorized, sorted log files for a port. @@ -370,7 +409,7 @@ public class LogviewerLogSearchHandler { String fileName = WorkerLogs.getTopologyPortWorkerLog(firstLog); - List<Map<String, Object>> newMatches = new ArrayList<>(matches); + final List<Map<String, Object>> newMatches = new ArrayList<>(matches); Map<String, Object> currentFileMatch = new HashMap<>(theseMatches); currentFileMatch.put("fileName", fileName); List<String> splitPath; @@ -412,7 +451,8 @@ public class LogviewerLogSearchHandler { private SubstringSearchResult bufferSubstringSearch(boolean isDaemon, File file, int fileLength, int offsetToBuf, int initBufOffset, BufferedInputStream stream, Integer bytesSkipped, int bytesRead, ByteBuffer haystack, byte[] needle, - List<Map<String, Object>> initialMatches, Integer numMatches, byte[] beforeBytes) throws IOException { + List<Map<String, Object>> initialMatches, Integer numMatches, byte[] beforeBytes) + throws IOException { int bufOffset = initBufOffset; List<Map<String, Object>> matches = initialMatches; @@ -422,8 +462,8 @@ public class LogviewerLogSearchHandler { while (true) { int offset = offsetOfBytes(haystack.array(), needle, bufOffset); if (matches.size() < numMatches && offset >= 0) { - int fileOffset = offsetToBuf + offset; - int bytesNeededAfterMatch = haystack.limit() - GREP_CONTEXT_SIZE - needle.length; + final int fileOffset = offsetToBuf + offset; + final int bytesNeededAfterMatch = haystack.limit() - GREP_CONTEXT_SIZE - needle.length; byte[] beforeArg = null; byte[] afterArg = null; @@ -541,7 +581,8 @@ public class LogviewerLogSearchHandler { * Tries once to read ahead in the stream to fill the context and * resets the stream to its position before the call. */ - private byte[] tryReadAhead(BufferedInputStream stream, ByteBuffer haystack, int offset, int fileLength, int bytesRead) throws IOException { + private byte[] tryReadAhead(BufferedInputStream stream, ByteBuffer haystack, int offset, int fileLength, int bytesRead) + throws IOException { int numExpected = Math.min(fileLength - bytesRead, GREP_CONTEXT_SIZE); byte[] afterBytes = new byte[numExpected]; stream.mark(numExpected); @@ -617,7 +658,7 @@ public class LogviewerLogSearchHandler { @VisibleForTesting String urlToMatchCenteredInLogPage(byte[] needle, String fname, int offset, Integer port) throws UnknownHostException { - String host = Utils.hostname(); + final String host = Utils.hostname(); String splittedFileName = String.join(Utils.FILE_PATH_SEPARATOR, takeLast(Arrays.asList(fname.split(Utils.FILE_PATH_SEPARATOR)), 3)); @@ -626,12 +667,12 @@ public class LogviewerLogSearchHandler { parameters.put("start", Math.max(0, offset - (LogviewerConstant.DEFAULT_BYTES_PER_PAGE / 2) - (needle.length / -2))); parameters.put("length", LogviewerConstant.DEFAULT_BYTES_PER_PAGE); - return URLBuilder.build(String.format("http://%s:%d/api/v1/log", host, port), parameters); + return UrlBuilder.build(String.format("http://%s:%d/api/v1/log", host, port), parameters); } @VisibleForTesting String urlToMatchCenteredInLogPageDaemonFile(byte[] needle, String fname, int offset, Integer port) throws UnknownHostException { - String host = Utils.hostname(); + final String host = Utils.hostname(); String splittedFileName = String.join(Utils.FILE_PATH_SEPARATOR, takeLast(Arrays.asList(fname.split(Utils.FILE_PATH_SEPARATOR)), 1)); @@ -640,7 +681,7 @@ public class LogviewerLogSearchHandler { parameters.put("start", Math.max(0, offset - (LogviewerConstant.DEFAULT_BYTES_PER_PAGE / 2) - (needle.length / -2))); parameters.put("length", LogviewerConstant.DEFAULT_BYTES_PER_PAGE); - return URLBuilder.build(String.format("http://%s:%d/api/v1/daemonlog", host, port), parameters); + return UrlBuilder.build(String.format("http://%s:%d/api/v1/daemonlog", host, port), parameters); } @VisibleForTesting @@ -651,6 +692,13 @@ public class LogviewerLogSearchHandler { private String searchString; private List<Map<String, Object>> matches; + /** + * Constructor. + * + * @param fileOffset offset (index) of the files + * @param searchString search string + * @param matches map representing matched search result + */ public Matched(int fileOffset, String searchString, List<Map<String, Object>> matches) { this.fileOffset = fileOffset; this.searchString = searchString; http://git-wip-us.apache.org/repos/asf/storm/blob/6a041a98/storm-webapp/src/main/java/org/apache/storm/daemon/logviewer/handler/LogviewerProfileHandler.java ---------------------------------------------------------------------- diff --git a/storm-webapp/src/main/java/org/apache/storm/daemon/logviewer/handler/LogviewerProfileHandler.java b/storm-webapp/src/main/java/org/apache/storm/daemon/logviewer/handler/LogviewerProfileHandler.java index 39608da..d7487e7 100644 --- a/storm-webapp/src/main/java/org/apache/storm/daemon/logviewer/handler/LogviewerProfileHandler.java +++ b/storm-webapp/src/main/java/org/apache/storm/daemon/logviewer/handler/LogviewerProfileHandler.java @@ -18,18 +18,6 @@ package org.apache.storm.daemon.logviewer.handler; -import j2html.tags.DomContent; -import org.apache.commons.lang.StringUtils; -import org.apache.storm.daemon.logviewer.utils.DirectoryCleaner; -import org.apache.storm.daemon.logviewer.utils.ResourceAuthorizer; -import org.apache.storm.daemon.logviewer.utils.LogviewerResponseBuilder; -import org.apache.storm.utils.ServerUtils; - -import javax.ws.rs.core.Response; -import java.io.File; -import java.io.IOException; -import java.util.List; - import static j2html.TagCreator.a; import static j2html.TagCreator.body; import static j2html.TagCreator.head; @@ -40,17 +28,44 @@ import static j2html.TagCreator.title; import static j2html.TagCreator.ul; import static java.util.stream.Collectors.toList; +import j2html.tags.DomContent; + +import java.io.File; +import java.io.IOException; +import java.util.List; +import javax.ws.rs.core.Response; + +import org.apache.commons.lang.StringUtils; +import org.apache.storm.daemon.logviewer.utils.DirectoryCleaner; +import org.apache.storm.daemon.logviewer.utils.LogviewerResponseBuilder; +import org.apache.storm.daemon.logviewer.utils.ResourceAuthorizer; +import org.apache.storm.utils.ServerUtils; + public class LogviewerProfileHandler { public static final String WORKER_LOG_FILENAME = "worker.log"; private final String logRoot; private final ResourceAuthorizer resourceAuthorizer; + /** + * Constructor. + * + * @param logRoot worker log root directory + * @param resourceAuthorizer {@link ResourceAuthorizer} + */ public LogviewerProfileHandler(String logRoot, ResourceAuthorizer resourceAuthorizer) { this.logRoot = logRoot; this.resourceAuthorizer = resourceAuthorizer; } + /** + * Enumerate dump (profile) files for given worker. + * + * @param topologyId topology ID + * @param hostPort host and port of worker + * @param user username + * @return The HTML page representing list page of dump files + */ public Response listDumpFiles(String topologyId, String hostPort, String user) throws IOException { String portStr = hostPort.split(":")[1]; File dir = new File(String.join(ServerUtils.FILE_PATH_SEPARATOR, logRoot, topologyId, portStr)); @@ -68,6 +83,33 @@ public class LogviewerProfileHandler { } } + /** + * Download a dump file. + * + * @param topologyId topology ID + * @param hostPort host and port of worker + * @param fileName dump file name + * @param user username + * @return a Response which lets browsers download that file. + * @see {@link org.apache.storm.daemon.logviewer.utils.LogFileDownloader#downloadFile(String, String, boolean)} + */ + public Response downloadDumpFile(String topologyId, String hostPort, String fileName, String user) throws IOException { + String portStr = hostPort.split(":")[1]; + File dir = new File(String.join(ServerUtils.FILE_PATH_SEPARATOR, logRoot, topologyId, portStr)); + File file = new File(dir, fileName); + + if (dir.exists() && file.exists()) { + String workerFileRelativePath = String.join(ServerUtils.FILE_PATH_SEPARATOR, topologyId, portStr, WORKER_LOG_FILENAME); + if (resourceAuthorizer.isUserAllowedToAccessFile(user, workerFileRelativePath)) { + return LogviewerResponseBuilder.buildDownloadFile(file); + } else { + return LogviewerResponseBuilder.buildResponseUnautohrizedUser(user); + } + } else { + return LogviewerResponseBuilder.buildResponsePageNotFound(); + } + } + private String buildDumpFileListPage(String topologyId, String hostPort, File dir) throws IOException { List<DomContent> liTags = getProfilerDumpFiles(dir).stream() .map(file -> li(a(file).withHref("/api/v1/dumps/" + topologyId + "/" + hostPort + "/" + file))) @@ -86,23 +128,6 @@ public class LogviewerProfileHandler { ).render(); } - public Response downloadDumpFile(String topologyId, String hostPort, String fileName, String user) throws IOException { - String portStr = hostPort.split(":")[1]; - File dir = new File(String.join(ServerUtils.FILE_PATH_SEPARATOR, logRoot, topologyId, portStr)); - File file = new File(dir, fileName); - - if (dir.exists() && file.exists()) { - String workerFileRelativePath = String.join(ServerUtils.FILE_PATH_SEPARATOR, topologyId, portStr, WORKER_LOG_FILENAME); - if (resourceAuthorizer.isUserAllowedToAccessFile(user, workerFileRelativePath)) { - return LogviewerResponseBuilder.buildDownloadFile(file); - } else { - return LogviewerResponseBuilder.buildResponseUnautohrizedUser(user); - } - } else { - return LogviewerResponseBuilder.buildResponsePageNotFound(); - } - } - private List<String> getProfilerDumpFiles(File dir) throws IOException { List<File> filesForDir = DirectoryCleaner.getFilesForDir(dir); return filesForDir.stream().filter(file -> { http://git-wip-us.apache.org/repos/asf/storm/blob/6a041a98/storm-webapp/src/main/java/org/apache/storm/daemon/logviewer/utils/DirectoryCleaner.java ---------------------------------------------------------------------- diff --git a/storm-webapp/src/main/java/org/apache/storm/daemon/logviewer/utils/DirectoryCleaner.java b/storm-webapp/src/main/java/org/apache/storm/daemon/logviewer/utils/DirectoryCleaner.java index c8a4f69..7bbb8d7 100644 --- a/storm-webapp/src/main/java/org/apache/storm/daemon/logviewer/utils/DirectoryCleaner.java +++ b/storm-webapp/src/main/java/org/apache/storm/daemon/logviewer/utils/DirectoryCleaner.java @@ -15,19 +15,20 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.apache.storm.daemon.logviewer.utils; -import java.io.IOException; import java.io.File; +import java.io.IOException; +import java.nio.file.DirectoryStream; import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.DirectoryStream; -import java.util.Stack; import java.util.ArrayList; -import java.util.List; -import java.util.Set; import java.util.Comparator; +import java.util.List; import java.util.PriorityQueue; +import java.util.Set; +import java.util.Stack; import java.util.regex.Pattern; import org.slf4j.Logger; @@ -37,25 +38,35 @@ import org.slf4j.LoggerFactory; * Provide methods to help Logviewer to clean up * files in directories and to get a list of files without * worrying about excessive memory usage. - * */ public class DirectoryCleaner { private static final Logger LOG = LoggerFactory.getLogger(DirectoryCleaner.class); // used to recognize the pattern of active log files, we may remove the "current" from this list private static final Pattern ACTIVE_LOG_PATTERN = Pattern.compile(".*\\.(log|err|out|current|yaml|pid)$"); // used to recognize the pattern of some meta files in a worker log directory - private static final Pattern META_LOG_PATTERN= Pattern.compile(".*\\.(yaml|pid)$"); + private static final Pattern META_LOG_PATTERN = Pattern.compile(".*\\.(yaml|pid)$");// max number of files to delete for every round + + private static final int PQ_SIZE = 1024; + private static final int MAX_ROUNDS = 512; // max rounds of scanning the dirs + public static final int MAX_NUMBER_OF_FILES_FOR_DIR = 1024; // not defining this as static is to allow for mocking in tests + + /** + * Creates DirectoryStream for give directory. + * + * @param dir File instance representing specific directory + * @return DirectoryStream + */ public DirectoryStream<Path> getStreamForDirectory(File dir) throws IOException { - DirectoryStream<Path> stream = Files.newDirectoryStream(dir.toPath()); - return stream; + return Files.newDirectoryStream(dir.toPath()); } /** * If totalSize of files exceeds the either the per-worker quota or global quota, * Logviewer deletes oldest inactive log files in a worker directory or in all worker dirs. * We use the parameter forPerDir to switch between the two deletion modes. + * * @param dirs the list of directories to be scanned for deletion * @param quota the per-dir quota or the total quota for the all directories * @param forPerDir if true, deletion happens for a single dir; otherwise, for all directories globally @@ -64,8 +75,6 @@ public class DirectoryCleaner { */ public int deleteOldestWhileTooLarge(List<File> dirs, long quota, boolean forPerDir, Set<String> activeDirs) throws IOException { - final int PQ_SIZE = 1024; // max number of files to delete for every round - final int MAX_ROUNDS = 512; // max rounds of scanning the dirs long totalSize = 0; int deletedFiles = 0; @@ -133,12 +142,12 @@ public class DirectoryCleaner { round++; if (round >= MAX_ROUNDS) { if (forPerDir) { - LOG.warn("Reach the MAX_ROUNDS: {} during per-dir deletion, you may have too many files in " + - "a single directory : {}, will delete the rest files in next interval.", + LOG.warn("Reach the MAX_ROUNDS: {} during per-dir deletion, you may have too many files in " + + "a single directory : {}, will delete the rest files in next interval.", MAX_ROUNDS, dirs.get(0).getCanonicalPath()); } else { - LOG.warn("Reach the MAX_ROUNDS: {} during global deletion, you may have too many files, " + - "will delete the rest files in next interval.", MAX_ROUNDS); + LOG.warn("Reach the MAX_ROUNDS: {} during global deletion, you may have too many files, " + + "will delete the rest files in next interval.", MAX_ROUNDS); } break; } @@ -165,15 +174,19 @@ public class DirectoryCleaner { return false; } - // Note that to avoid memory problem, we only return the first 1024 files in a directory + /** + * Lists files in directory. + * Note that to avoid memory problem, we only return the first 1024 files in a directory. + * + * @param dir directory to get file list + * @return files in directory + */ public static List<File> getFilesForDir(File dir) throws IOException { List<File> files = new ArrayList<File>(); - final int MAX_NUM = 1024; - try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir.toPath())) { for (Path path : stream) { files.add(path.toFile()); - if (files.size() >= MAX_NUM) { + if (files.size() >= MAX_NUMBER_OF_FILES_FOR_DIR) { break; } } http://git-wip-us.apache.org/repos/asf/storm/blob/6a041a98/storm-webapp/src/main/java/org/apache/storm/daemon/logviewer/utils/LogCleaner.java ---------------------------------------------------------------------- diff --git a/storm-webapp/src/main/java/org/apache/storm/daemon/logviewer/utils/LogCleaner.java b/storm-webapp/src/main/java/org/apache/storm/daemon/logviewer/utils/LogCleaner.java index 6e4e073..bfd80a3 100644 --- a/storm-webapp/src/main/java/org/apache/storm/daemon/logviewer/utils/LogCleaner.java +++ b/storm-webapp/src/main/java/org/apache/storm/daemon/logviewer/utils/LogCleaner.java @@ -18,16 +18,16 @@ package org.apache.storm.daemon.logviewer.utils; +import static java.util.stream.Collectors.joining; +import static java.util.stream.Collectors.toCollection; +import static java.util.stream.Collectors.toList; + +import static org.apache.storm.DaemonConfig.LOGVIEWER_CLEANUP_AGE_MINS; +import static org.apache.storm.DaemonConfig.LOGVIEWER_CLEANUP_INTERVAL_SECS; +import static org.apache.storm.DaemonConfig.LOGVIEWER_MAX_PER_WORKER_LOGS_SIZE_MB; +import static org.apache.storm.DaemonConfig.LOGVIEWER_MAX_SUM_WORKER_LOGS_SIZE_MB; + import com.google.common.annotations.VisibleForTesting; -import org.apache.commons.io.IOUtils; -import org.apache.storm.StormTimer; -import org.apache.storm.daemon.supervisor.SupervisorUtils; -import org.apache.storm.utils.ObjectReader; -import org.apache.storm.utils.Time; -import org.apache.storm.utils.Utils; -import org.jooq.lambda.Unchecked; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import java.io.Closeable; import java.io.File; @@ -41,23 +41,24 @@ import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Objects; -import java.util.Optional; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; import java.util.function.BinaryOperator; import java.util.stream.StreamSupport; -import static java.util.stream.Collectors.joining; -import static java.util.stream.Collectors.toCollection; -import static java.util.stream.Collectors.toList; -import static java.util.stream.Collectors.toMap; -import static org.apache.storm.DaemonConfig.LOGVIEWER_CLEANUP_AGE_MINS; -import static org.apache.storm.DaemonConfig.LOGVIEWER_CLEANUP_INTERVAL_SECS; -import static org.apache.storm.DaemonConfig.LOGVIEWER_MAX_PER_WORKER_LOGS_SIZE_MB; -import static org.apache.storm.DaemonConfig.LOGVIEWER_MAX_SUM_WORKER_LOGS_SIZE_MB; +import org.apache.commons.io.IOUtils; +import org.apache.storm.StormTimer; +import org.apache.storm.utils.ObjectReader; +import org.apache.storm.utils.Time; +import org.apache.storm.utils.Utils; +import org.jooq.lambda.Unchecked; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +/** + * Cleans dead workers logs and directories. + */ public class LogCleaner implements Runnable, Closeable { private static final Logger LOG = LoggerFactory.getLogger(LogCleaner.class); @@ -71,6 +72,14 @@ public class LogCleaner implements Runnable, Closeable { private final long maxSumWorkerLogsSizeMb; private long maxPerWorkerLogsSizeMb; + /** + * Constuctor. + * + * @param stormConf configuration map for Storm cluster + * @param workerLogs {@link WorkerLogs} instance + * @param directoryCleaner {@link DirectoryCleaner} instance + * @param logRootDir root log directory + */ public LogCleaner(Map<String, Object> stormConf, WorkerLogs workerLogs, DirectoryCleaner directoryCleaner, File logRootDir) { this.stormConf = stormConf; @@ -87,6 +96,9 @@ public class LogCleaner implements Runnable, Closeable { maxSumWorkerLogsSizeMb, maxPerWorkerLogsSizeMb); } + /** + * Start log cleanup thread. + */ public void start() { if (intervalSecs != null) { LOG.debug("starting log cleanup thread at interval: {}", intervalSecs); @@ -100,9 +112,9 @@ public class LogCleaner implements Runnable, Closeable { } else { LOG.warn("The interval for log cleanup is not set. Skip starting log cleanup thread."); } - } + @Override public void close() { if (logviewerCleanupTimer != null) { try { @@ -116,6 +128,7 @@ public class LogCleaner implements Runnable, Closeable { /** * Delete old log dirs for which the workers are no longer alive. */ + @Override public void run() { try { int nowSecs = Time.currentTimeSecs(); @@ -152,7 +165,8 @@ public class LogCleaner implements Runnable, Closeable { @VisibleForTesting List<Integer> perWorkerDirCleanup(long size) { return workerLogs.getAllWorkerDirs().stream() - .map(Unchecked.function(dir -> directoryCleaner.deleteOldestWhileTooLarge(Collections.singletonList(dir), size, true, null))) + .map(Unchecked.function(dir -> + directoryCleaner.deleteOldestWhileTooLarge(Collections.singletonList(dir), size, true, null))) .collect(toList()); } @@ -167,14 +181,6 @@ public class LogCleaner implements Runnable, Closeable { return directoryCleaner.deleteOldestWhileTooLarge(workerDirs, size, false, aliveWorkerDirs); } - private Set<String> getAliveIds(int nowSecs) throws Exception { - return SupervisorUtils.readWorkerHeartbeats(stormConf).entrySet().stream() - .filter(entry -> Objects.nonNull(entry.getValue()) - && !SupervisorUtils.isWorkerHbTimedOut(nowSecs, entry.getValue(), stormConf)) - .map(Map.Entry::getKey) - .collect(toCollection(TreeSet::new)); - } - /** * Delete the topo dir if it contains zero port dirs. */ http://git-wip-us.apache.org/repos/asf/storm/blob/6a041a98/storm-webapp/src/main/java/org/apache/storm/daemon/logviewer/utils/LogFileDownloader.java ---------------------------------------------------------------------- diff --git a/storm-webapp/src/main/java/org/apache/storm/daemon/logviewer/utils/LogFileDownloader.java b/storm-webapp/src/main/java/org/apache/storm/daemon/logviewer/utils/LogFileDownloader.java index 7d9cb50..62c08da 100644 --- a/storm-webapp/src/main/java/org/apache/storm/daemon/logviewer/utils/LogFileDownloader.java +++ b/storm-webapp/src/main/java/org/apache/storm/daemon/logviewer/utils/LogFileDownloader.java @@ -18,22 +18,38 @@ package org.apache.storm.daemon.logviewer.utils; -import javax.ws.rs.core.Response; import java.io.File; import java.io.IOException; +import javax.ws.rs.core.Response; + public class LogFileDownloader { private final String logRoot; private final String daemonLogRoot; private final ResourceAuthorizer resourceAuthorizer; + /** + * Constructor. + * + * @param logRoot root worker log directory + * @param daemonLogRoot root daemon log directory + * @param resourceAuthorizer {@link ResourceAuthorizer} + */ public LogFileDownloader(String logRoot, String daemonLogRoot, ResourceAuthorizer resourceAuthorizer) { this.logRoot = logRoot; this.daemonLogRoot = daemonLogRoot; this.resourceAuthorizer = resourceAuthorizer; } + /** + * Checks authorization for the log file and download + * + * @param fileName file to download + * @param user username + * @param isDaemon true if the file is a daemon log, false if the file is an worker log + * @return a Response which lets browsers download that file. + */ public Response downloadFile(String fileName, String user, boolean isDaemon) throws IOException { String rootDir = isDaemon ? daemonLogRoot : logRoot; File file = new File(rootDir, fileName).getCanonicalFile(); http://git-wip-us.apache.org/repos/asf/storm/blob/6a041a98/storm-webapp/src/main/java/org/apache/storm/daemon/logviewer/utils/LogviewerResponseBuilder.java ---------------------------------------------------------------------- diff --git a/storm-webapp/src/main/java/org/apache/storm/daemon/logviewer/utils/LogviewerResponseBuilder.java b/storm-webapp/src/main/java/org/apache/storm/daemon/logviewer/utils/LogviewerResponseBuilder.java index d0f2857..59570ee 100644 --- a/storm-webapp/src/main/java/org/apache/storm/daemon/logviewer/utils/LogviewerResponseBuilder.java +++ b/storm-webapp/src/main/java/org/apache/storm/daemon/logviewer/utils/LogviewerResponseBuilder.java @@ -18,13 +18,12 @@ package org.apache.storm.daemon.logviewer.utils; -import com.google.common.io.ByteStreams; -import org.apache.storm.daemon.common.JsonResponseBuilder; -import org.apache.storm.ui.UIHelpers; +import static j2html.TagCreator.body; +import static j2html.TagCreator.h2; +import static javax.ws.rs.core.Response.Status.OK; +import static org.apache.commons.lang.StringEscapeUtils.escapeHtml; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.StreamingOutput; +import com.google.common.io.ByteStreams; import java.io.BufferedOutputStream; import java.io.File; @@ -35,26 +34,46 @@ import java.io.OutputStream; import java.util.HashMap; import java.util.Map; -import static j2html.TagCreator.body; -import static j2html.TagCreator.h2; -import static javax.ws.rs.core.Response.Status.OK; -import static org.apache.commons.lang.StringEscapeUtils.escapeHtml; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.StreamingOutput; + +import org.apache.storm.daemon.common.JsonResponseBuilder; +import org.apache.storm.ui.UIHelpers; public class LogviewerResponseBuilder { private LogviewerResponseBuilder() { } + /** + * Build a Response object representing success response with HTML entity. + * + * @param content HTML entity content, String type + */ public static Response buildSuccessHtmlResponse(String content) { return Response.status(OK).entity(content) .type(MediaType.TEXT_HTML_TYPE).build(); } + /** + * Build a Response object representing success response with JSON entity. + * + * @param entity entity object to represent it as JSON + * @param callback callback for JSONP + * @param origin origin + * @see {@link JsonResponseBuilder} + */ public static Response buildSuccessJsonResponse(Object entity, String callback, String origin) { return new JsonResponseBuilder().setData(entity).setCallback(callback) .setHeaders(LogviewerResponseBuilder.getHeadersForSuccessResponse(origin)).build(); } + /** + * Build a Response object representing download a file. + * + * @param file file to download + */ public static Response buildDownloadFile(File file) throws IOException { // do not close this InputStream in method: it will be used from jetty server InputStream is = new FileInputStream(file); @@ -65,6 +84,11 @@ public class LogviewerResponseBuilder { .build(); } + /** + * Build a Response object representing unauthorized user, with HTML response. + * + * @param user username + */ public static Response buildResponseUnautohrizedUser(String user) { String entity = buildUnauthorizedUserHtml(user); return Response.status(OK) @@ -73,6 +97,9 @@ public class LogviewerResponseBuilder { .build(); } + /** + * Build a Response object representing page not found. + */ public static Response buildResponsePageNotFound() { return Response.status(404) .entity("Page not found") @@ -80,11 +107,23 @@ public class LogviewerResponseBuilder { .build(); } + /** + * Build a Response object representing unauthorized user, with JSON response. + * + * @param user username + * @param callback callback for JSONP + */ public static Response buildUnauthorizedUserJsonResponse(String user, String callback) { return new JsonResponseBuilder().setData(UIHelpers.unauthorizedUserJson(user)) .setCallback(callback).setStatus(401).build(); } + /** + * Build a Response object representing exception, with JSON response. + * + * @param ex Exception object + * @param callback callback for JSONP + */ public static Response buildExceptionJsonResponse(Exception ex, String callback) { return new JsonResponseBuilder().setData(UIHelpers.exceptionToJson(ex)) .setCallback(callback).setStatus(500).build(); http://git-wip-us.apache.org/repos/asf/storm/blob/6a041a98/storm-webapp/src/main/java/org/apache/storm/daemon/logviewer/utils/ResourceAuthorizer.java ---------------------------------------------------------------------- diff --git a/storm-webapp/src/main/java/org/apache/storm/daemon/logviewer/utils/ResourceAuthorizer.java b/storm-webapp/src/main/java/org/apache/storm/daemon/logviewer/utils/ResourceAuthorizer.java index 55691b2..16f408a 100644 --- a/storm-webapp/src/main/java/org/apache/storm/daemon/logviewer/utils/ResourceAuthorizer.java +++ b/storm-webapp/src/main/java/org/apache/storm/daemon/logviewer/utils/ResourceAuthorizer.java @@ -18,16 +18,10 @@ package org.apache.storm.daemon.logviewer.utils; +import static org.apache.storm.DaemonConfig.UI_FILTER; + import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.Sets; -import org.apache.commons.lang.StringUtils; -import org.apache.storm.Config; -import org.apache.storm.DaemonConfig; -import org.apache.storm.security.auth.AuthUtils; -import org.apache.storm.security.auth.IGroupMappingServiceProvider; -import org.apache.storm.utils.ObjectReader; -import org.apache.storm.utils.ServerConfigUtils; -import org.apache.storm.utils.Utils; import java.io.File; import java.io.IOException; @@ -37,28 +31,51 @@ import java.util.List; import java.util.Map; import java.util.Set; -import static org.apache.storm.DaemonConfig.UI_FILTER; +import org.apache.commons.lang.StringUtils; +import org.apache.storm.Config; +import org.apache.storm.DaemonConfig; +import org.apache.storm.security.auth.AuthUtils; +import org.apache.storm.security.auth.IGroupMappingServiceProvider; +import org.apache.storm.utils.ObjectReader; +import org.apache.storm.utils.ServerConfigUtils; +import org.apache.storm.utils.Utils; public class ResourceAuthorizer { private final Map<String, Object> stormConf; private final IGroupMappingServiceProvider groupMappingServiceProvider; + /** + * Constuctor. + * + * @param stormConf storm configuration + */ public ResourceAuthorizer(Map<String, Object> stormConf) { this.stormConf = stormConf; this.groupMappingServiceProvider = AuthUtils.GetGroupMappingServiceProviderPlugin(stormConf); } + /** + * Checks whether user is allowed to access file via UI. Always true when UI filter is not set. + * + * @param fileName file name to access + * @param user username + */ public boolean isUserAllowedToAccessFile(String fileName, String user) { return isUiFilterNotSet() || isAuthorizedLogUser(user, fileName); } + /** + * Checks whether user is authorized to access file. Checks regardless of UI filter. + * + * @param user username + * @param fileName file name to access + */ public boolean isAuthorizedLogUser(String user, String fileName) { if (StringUtils.isEmpty(user) || StringUtils.isEmpty(fileName) || getLogUserGroupWhitelist(fileName) == null) { return false; } else { - Set<String> groups = getUserGroups(user); LogUserGroupWhitelist whitelist = getLogUserGroupWhitelist(fileName); List<String> logsUsers = new ArrayList<>(); @@ -70,11 +87,18 @@ public class ResourceAuthorizer { logsGroups.addAll(ObjectReader.getStrings(stormConf.get(DaemonConfig.LOGS_GROUPS))); logsGroups.addAll(whitelist.getGroupWhitelist()); - return logsUsers.stream().anyMatch(u -> u.equals(user)) || - Sets.intersection(groups, new HashSet<>(logsGroups)).size() > 0; + Set<String> groups = getUserGroups(user); + + return logsUsers.stream().anyMatch(u -> u.equals(user)) + || Sets.intersection(groups, new HashSet<>(logsGroups)).size() > 0; } } + /** + * Get the whitelist of users and groups for given file. + * + * @param fileName file name to get the whitelist + */ public LogUserGroupWhitelist getLogUserGroupWhitelist(String fileName) { File wlFile = ServerConfigUtils.getLogMetaDataFile(fileName); Map<String, Object> map = (Map<String, Object>) Utils.readYamlFile(wlFile.getAbsolutePath()); http://git-wip-us.apache.org/repos/asf/storm/blob/6a041a98/storm-webapp/src/main/java/org/apache/storm/daemon/logviewer/utils/WorkerLogs.java ---------------------------------------------------------------------- diff --git a/storm-webapp/src/main/java/org/apache/storm/daemon/logviewer/utils/WorkerLogs.java b/storm-webapp/src/main/java/org/apache/storm/daemon/logviewer/utils/WorkerLogs.java index 6018b79..eba6404 100644 --- a/storm-webapp/src/main/java/org/apache/storm/daemon/logviewer/utils/WorkerLogs.java +++ b/storm-webapp/src/main/java/org/apache/storm/daemon/logviewer/utils/WorkerLogs.java @@ -18,16 +18,13 @@ package org.apache.storm.daemon.logviewer.utils; +import static java.util.stream.Collectors.toCollection; +import static java.util.stream.Collectors.toMap; +import static org.apache.storm.Config.SUPERVISOR_RUN_WORKER_AS_USER; +import static org.apache.storm.Config.TOPOLOGY_SUBMITTER_USER; +import static org.apache.storm.daemon.utils.ListFunctionalSupport.takeLast; + import com.google.common.collect.Lists; -import org.apache.storm.daemon.supervisor.ClientSupervisorUtils; -import org.apache.storm.daemon.supervisor.SupervisorUtils; -import org.apache.storm.utils.ObjectReader; -import org.apache.storm.utils.Time; -import org.apache.storm.utils.Utils; -import org.jooq.lambda.Unchecked; -import org.jooq.lambda.tuple.Tuple2; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import java.io.File; import java.io.IOException; @@ -43,12 +40,19 @@ import java.util.SortedSet; import java.util.TreeSet; import java.util.stream.Stream; -import static java.util.stream.Collectors.toCollection; -import static java.util.stream.Collectors.toMap; -import static org.apache.storm.Config.SUPERVISOR_RUN_WORKER_AS_USER; -import static org.apache.storm.Config.TOPOLOGY_SUBMITTER_USER; -import static org.apache.storm.daemon.utils.ListFunctionalSupport.takeLast; +import org.apache.storm.daemon.supervisor.ClientSupervisorUtils; +import org.apache.storm.daemon.supervisor.SupervisorUtils; +import org.apache.storm.utils.ObjectReader; +import org.apache.storm.utils.Time; +import org.apache.storm.utils.Utils; +import org.jooq.lambda.Unchecked; +import org.jooq.lambda.tuple.Tuple2; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +/** + * A class that knows about how to operate with worker log directory. + */ public class WorkerLogs { private static final Logger LOG = LoggerFactory.getLogger(LogCleaner.class); @@ -56,19 +60,30 @@ public class WorkerLogs { private final Map<String, Object> stormConf; private final File logRootDir; + /** + * Constructor. + * + * @param stormConf storm configuration + * @param logRootDir the log root directory + */ public WorkerLogs(Map<String, Object> stormConf, File logRootDir) { this.stormConf = stormConf; this.logRootDir = logRootDir; } + /** + * Set permission of log file so that logviewer can serve the file. + * + * @param fileName log file + */ public void setLogFilePermission(String fileName) throws IOException { File file = new File(logRootDir, fileName).getCanonicalFile(); boolean runAsUser = ObjectReader.getBoolean(stormConf.get(SUPERVISOR_RUN_WORKER_AS_USER), false); File parent = new File(logRootDir, fileName).getParentFile(); Optional<File> mdFile = (parent == null) ? Optional.empty() : getMetadataFileForWorkerLogDir(parent); - Optional<String> topoOwner = mdFile.isPresent() ? - Optional.of(getTopologyOwnerFromMetadataFile(mdFile.get().getCanonicalPath())) : - Optional.empty(); + Optional<String> topoOwner = mdFile.isPresent() + ? Optional.of(getTopologyOwnerFromMetadataFile(mdFile.get().getCanonicalPath())) + : Optional.empty(); if (runAsUser && topoOwner.isPresent() && file.exists() && !Files.isReadable(file.toPath())) { LOG.debug("Setting permissions on file {} with topo-owner {}", fileName, topoOwner); @@ -78,6 +93,9 @@ public class WorkerLogs { } } + /** + * Return a list of all log files from worker directories in root log directory. + */ public List<File> getAllLogsForRootDir() throws IOException { List<File> files = new ArrayList<>(); Set<File> topoDirFiles = getAllWorkerDirs(); @@ -90,6 +108,9 @@ public class WorkerLogs { return files; } + /** + * Return a set of all worker directories in root log directory. + */ public Set<File> getAllWorkerDirs() { File[] rootDirFiles = logRootDir.listFiles(); if (rootDirFiles != null) { @@ -116,6 +137,10 @@ public class WorkerLogs { .collect(toCollection(TreeSet::new)); } + /** + * Return a metadata file (worker.yaml) for given worker log directory. + * @param logDir worker log directory + */ public Optional<File> getMetadataFileForWorkerLogDir(File logDir) throws IOException { File metaFile = new File(logDir, WORKER_YAML); if (metaFile.exists()) { @@ -126,16 +151,31 @@ public class WorkerLogs { } } + /** + * Return worker id from worker meta file. + * + * @param metaFile metadata file + */ public String getWorkerIdFromMetadataFile(String metaFile) { Map<String, Object> map = (Map<String, Object>) Utils.readYamlFile(metaFile); return ObjectReader.getString(map.get("worker-id"), null); } + /** + * Return topology owner from worker meta file. + * + * @param metaFile metadata file + */ public String getTopologyOwnerFromMetadataFile(String metaFile) { Map<String, Object> map = (Map<String, Object>) Utils.readYamlFile(metaFile); return ObjectReader.getString(map.get(TOPOLOGY_SUBMITTER_USER), null); } + /** + * Retrieve the set of alive worker IDs. + * + * @param nowSecs current time in seconds + */ public Set<String> getAliveIds(int nowSecs) throws Exception { return SupervisorUtils.readWorkerHeartbeats(stormConf).entrySet().stream() .filter(entry -> Objects.nonNull(entry.getValue()) @@ -144,6 +184,12 @@ public class WorkerLogs { .collect(toCollection(TreeSet::new)); } + /** + * Finds a worker ID for each directory in set and return it as map. + * + * @param logDirs directories to check whether they're worker directories or not + * @return the pair of worker ID, directory. worker ID will be an empty string if the directory is not a worker directory. + */ public Map<String, File> identifyWorkerLogDirs(Set<File> logDirs) { // we could also make this static, but not to do it due to mock return logDirs.stream().map(Unchecked.function(logDir -> { @@ -156,6 +202,8 @@ public class WorkerLogs { /** * Return the path of the worker log with the format of topoId/port/worker.log.* + * + * @param file worker log */ public static String getTopologyPortWorkerLog(File file) { try { http://git-wip-us.apache.org/repos/asf/storm/blob/6a041a98/storm-webapp/src/main/java/org/apache/storm/daemon/logviewer/webapp/LogviewerApplication.java ---------------------------------------------------------------------- diff --git a/storm-webapp/src/main/java/org/apache/storm/daemon/logviewer/webapp/LogviewerApplication.java b/storm-webapp/src/main/java/org/apache/storm/daemon/logviewer/webapp/LogviewerApplication.java index 32a723e..c44e8cd 100644 --- a/storm-webapp/src/main/java/org/apache/storm/daemon/logviewer/webapp/LogviewerApplication.java +++ b/storm-webapp/src/main/java/org/apache/storm/daemon/logviewer/webapp/LogviewerApplication.java @@ -18,31 +18,32 @@ package org.apache.storm.daemon.logviewer.webapp; +import static org.apache.storm.DaemonConfig.LOGVIEWER_APPENDER_NAME; + +import java.io.File; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import javax.ws.rs.ApplicationPath; +import javax.ws.rs.core.Application; + import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.core.Appender; import org.apache.logging.log4j.core.LoggerContext; import org.apache.logging.log4j.core.appender.RollingFileAppender; import org.apache.storm.daemon.common.AuthorizationExceptionMapper; +import org.apache.storm.daemon.logviewer.handler.LogviewerLogDownloadHandler; +import org.apache.storm.daemon.logviewer.handler.LogviewerLogPageHandler; import org.apache.storm.daemon.logviewer.handler.LogviewerLogSearchHandler; import org.apache.storm.daemon.logviewer.handler.LogviewerProfileHandler; import org.apache.storm.daemon.logviewer.utils.ResourceAuthorizer; -import org.apache.storm.daemon.logviewer.handler.LogviewerLogDownloadHandler; -import org.apache.storm.daemon.logviewer.handler.LogviewerLogPageHandler; import org.apache.storm.daemon.logviewer.utils.WorkerLogs; import org.apache.storm.security.auth.AuthUtils; import org.apache.storm.security.auth.IHttpCredentialsPlugin; import org.apache.storm.utils.ConfigUtils; import org.apache.storm.utils.ObjectReader; -import javax.ws.rs.ApplicationPath; -import javax.ws.rs.core.Application; -import java.io.File; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -import static org.apache.storm.DaemonConfig.LOGVIEWER_APPENDER_NAME; - @ApplicationPath("") public class LogviewerApplication extends Application { private static Map<String, Object> stormConf; @@ -75,6 +76,11 @@ public class LogviewerApplication extends Application { return singletons; } + /** + * Spot to inject storm configuration before initializing LogviewerApplication instance. + * + * @param stormConf storm configuration + */ public static void setup(Map<String, Object> stormConf) { LogviewerApplication.stormConf = stormConf; } http://git-wip-us.apache.org/repos/asf/storm/blob/6a041a98/storm-webapp/src/main/java/org/apache/storm/daemon/logviewer/webapp/LogviewerResource.java ---------------------------------------------------------------------- diff --git a/storm-webapp/src/main/java/org/apache/storm/daemon/logviewer/webapp/LogviewerResource.java b/storm-webapp/src/main/java/org/apache/storm/daemon/logviewer/webapp/LogviewerResource.java index bc6529c..478001d 100644 --- a/storm-webapp/src/main/java/org/apache/storm/daemon/logviewer/webapp/LogviewerResource.java +++ b/storm-webapp/src/main/java/org/apache/storm/daemon/logviewer/webapp/LogviewerResource.java @@ -19,13 +19,25 @@ package org.apache.storm.daemon.logviewer.webapp; import com.codahale.metrics.Meter; + +import java.io.IOException; +import java.net.URLDecoder; +import java.util.Map; + +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.Response; + import org.apache.commons.lang.BooleanUtils; import org.apache.commons.lang.StringUtils; import org.apache.storm.daemon.common.JsonResponseBuilder; -import org.apache.storm.daemon.logviewer.handler.LogviewerLogSearchHandler; -import org.apache.storm.daemon.logviewer.handler.LogviewerProfileHandler; import org.apache.storm.daemon.logviewer.handler.LogviewerLogDownloadHandler; import org.apache.storm.daemon.logviewer.handler.LogviewerLogPageHandler; +import org.apache.storm.daemon.logviewer.handler.LogviewerLogSearchHandler; +import org.apache.storm.daemon.logviewer.handler.LogviewerProfileHandler; import org.apache.storm.metric.StormMetricsRegistry; import org.apache.storm.security.auth.IHttpCredentialsPlugin; import org.apache.storm.ui.InvalidRequestException; @@ -33,24 +45,20 @@ import org.apache.storm.ui.UIHelpers; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.GET; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.Response; -import java.io.IOException; -import java.net.URLDecoder; -import java.util.Map; - +/** + * Handles HTTP requests for Logviewer. + */ @Path("/") public class LogviewerResource { private static final Logger LOG = LoggerFactory.getLogger(LogviewerResource.class); private static final Meter meterLogPageHttpRequests = StormMetricsRegistry.registerMeter("logviewer:num-log-page-http-requests"); - private static final Meter meterDaemonLogPageHttpRequests = StormMetricsRegistry.registerMeter("logviewer:num-daemonlog-page-http-requests"); - private static final Meter meterDownloadLogFileHttpRequests = StormMetricsRegistry.registerMeter("logviewer:num-download-log-file-http-requests"); - private static final Meter meterDownloadLogDaemonFileHttpRequests = StormMetricsRegistry.registerMeter("logviewer:num-download-log-daemon-file-http-requests"); + private static final Meter meterDaemonLogPageHttpRequests = StormMetricsRegistry.registerMeter( + "logviewer:num-daemonlog-page-http-requests"); + private static final Meter meterDownloadLogFileHttpRequests = StormMetricsRegistry.registerMeter( + "logviewer:num-download-log-file-http-requests"); + private static final Meter meterDownloadLogDaemonFileHttpRequests = StormMetricsRegistry.registerMeter( + "logviewer:num-download-log-daemon-file-http-requests"); private static final Meter meterListLogsHttpRequests = StormMetricsRegistry.registerMeter("logviewer:num-list-logs-http-requests"); private final LogviewerLogPageHandler logviewer; @@ -59,6 +67,15 @@ public class LogviewerResource { private final LogviewerLogSearchHandler logSearchHandler; private final IHttpCredentialsPlugin httpCredsHandler; + /** + * Constructor. + * + * @param logviewerParam {@link LogviewerLogPageHandler} + * @param profileHandler {@link LogviewerProfileHandler} + * @param logDownloadHandler {@link LogviewerLogDownloadHandler} + * @param logSearchHandler {@link LogviewerLogSearchHandler} + * @param httpCredsHandler {@link IHttpCredentialsPlugin} + */ public LogviewerResource(LogviewerLogPageHandler logviewerParam, LogviewerProfileHandler profileHandler, LogviewerLogDownloadHandler logDownloadHandler, LogviewerLogSearchHandler logSearchHandler, IHttpCredentialsPlugin httpCredsHandler) { @@ -69,6 +86,9 @@ public class LogviewerResource { this.httpCredsHandler = httpCredsHandler; } + /** + * Handles '/log' request. + */ @GET @Path("/log") public Response log(@Context HttpServletRequest request) throws IOException { @@ -87,6 +107,9 @@ public class LogviewerResource { } } + /** + * Handles '/daemonlog' request. + */ @GET @Path("/daemonlog") public Response daemonLog(@Context HttpServletRequest request) throws IOException { @@ -105,6 +128,9 @@ public class LogviewerResource { } } + /** + * Handles '/searchLogs' request. + */ @GET @Path("/searchLogs") public Response searchLogs(@Context HttpServletRequest request) throws IOException { @@ -117,6 +143,9 @@ public class LogviewerResource { return logviewer.listLogFiles(user, portStr != null ? Integer.parseInt(portStr) : null, topologyId, callback, origin); } + /** + * Handles '/listLogs' request. + */ @GET @Path("/listLogs") public Response listLogs(@Context HttpServletRequest request) throws IOException { @@ -131,6 +160,9 @@ public class LogviewerResource { return logviewer.listLogFiles(user, portStr != null ? Integer.parseInt(portStr) : null, topologyId, callback, origin); } + /** + * Handles '/dumps' (listing dump files) request. + */ @GET @Path("/dumps/{topo-id}/{host-port}") public Response listDumpFiles(@PathParam("topo-id") String topologyId, @PathParam("host-port") String hostPort, @@ -139,6 +171,9 @@ public class LogviewerResource { return profileHandler.listDumpFiles(topologyId, hostPort, user); } + /** + * Handles '/dumps' (downloading specific dump file) request. + */ @GET @Path("/dumps/{topo-id}/{host-port}/{filename}") public Response downloadDumpFile(@PathParam("topo-id") String topologyId, @PathParam("host-port") String hostPort, @@ -147,6 +182,9 @@ public class LogviewerResource { return profileHandler.downloadDumpFile(topologyId, hostPort, fileName, user); } + /** + * Handles '/download' (downloading specific log file) request. + */ @GET @Path("/download") public Response downloadLogFile(@Context HttpServletRequest request) throws IOException { @@ -158,6 +196,9 @@ public class LogviewerResource { return logDownloadHandler.downloadLogFile(decodedFileName, user); } + /** + * Handles '/daemondownload' (downloading specific daemon log file) request. + */ @GET @Path("/daemondownload") public Response downloadDaemonLogFile(@Context HttpServletRequest request) throws IOException { @@ -169,6 +210,9 @@ public class LogviewerResource { return logDownloadHandler.downloadDaemonLogFile(decodedFileName, user); } + /** + * Handles '/search' (searching from specific worker or daemon log file) request. + */ @GET @Path("/search/{file}") public Response search(@PathParam("file") String file, @Context HttpServletRequest request) throws IOException { @@ -191,6 +235,9 @@ public class LogviewerResource { } } + /** + * Handles '/deepSearch' request. + */ @GET @Path("/deepSearch/{topoId}") public Response deepSearch(@PathParam("topoId") String topologyId,
