Search in sources :

Example 1 with InvalidRequestException

use of org.apache.storm.daemon.ui.InvalidRequestException in project storm by apache.

the class LogviewerLogPageHandler method pageFile.

private String pageFile(String path, boolean isZipFile, long fileLength, Integer start, Integer readLength) throws IOException, InvalidRequestException {
    try (InputStream input = isZipFile ? new GZIPInputStream(new FileInputStream(path)) : new FileInputStream(path);
        ByteArrayOutputStream output = new ByteArrayOutputStream()) {
        if (start >= fileLength) {
            throw new InvalidRequestException("Cannot start past the end of the file");
        }
        if (start > 0) {
            StreamUtil.skipBytes(input, start);
        }
        byte[] buffer = new byte[1024];
        while (output.size() < readLength) {
            int size = input.read(buffer, 0, Math.min(1024, readLength - output.size()));
            if (size > 0) {
                output.write(buffer, 0, size);
            } else {
                break;
            }
        }
        numPageRead.mark();
        return output.toString();
    } catch (FileNotFoundException e) {
        numFileOpenExceptions.mark();
        throw e;
    } catch (IOException e) {
        numFileReadExceptions.mark();
        throw e;
    }
}
Also used : GZIPInputStream(java.util.zip.GZIPInputStream) GZIPInputStream(java.util.zip.GZIPInputStream) FileInputStream(java.io.FileInputStream) InputStream(java.io.InputStream) FileNotFoundException(java.io.FileNotFoundException) InvalidRequestException(org.apache.storm.daemon.ui.InvalidRequestException) ByteArrayOutputStream(java.io.ByteArrayOutputStream) UncheckedIOException(java.io.UncheckedIOException) IOException(java.io.IOException) FileInputStream(java.io.FileInputStream)

Example 2 with InvalidRequestException

use of org.apache.storm.daemon.ui.InvalidRequestException in project storm by apache.

the class LogviewerLogPageHandler method logPage.

/**
 * Provides a worker log file to view, starting from the specified position
 * or default starting position of the most recent page.
 *
 * @param fileName file to view
 * @param start start offset, or null if the most recent page is desired
 * @param length length to read in this page, or null if default page length is desired
 * @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 {
    Path rawFile = logRoot.resolve(fileName);
    Path absFile = rawFile.toAbsolutePath().normalize();
    if (!absFile.startsWith(logRoot) || !rawFile.normalize().toString().equals(rawFile.toString())) {
        // Ensure filename doesn't contain ../ parts
        return LogviewerResponseBuilder.buildResponsePageNotFound();
    }
    if (resourceAuthorizer.isUserAllowedToAccessFile(user, fileName)) {
        workerLogs.setLogFilePermission(fileName);
        Path topoDir = absFile.getParent().getParent();
        if (absFile.toFile().exists()) {
            SortedSet<Path> logFiles;
            try {
                logFiles = Arrays.stream(topoDir.toFile().listFiles()).flatMap(Unchecked.function(portDir -> directoryCleaner.getFilesForDir(portDir.toPath()).stream())).filter(Files::isRegularFile).collect(toCollection(TreeSet::new));
            } catch (UncheckedIOException e) {
                throw e.getCause();
            }
            List<String> reorderedFilesStr = logFiles.stream().map(WorkerLogs::getTopologyPortWorkerLog).filter(fileStr -> !StringUtils.equals(fileName, fileStr)).collect(toList());
            reorderedFilesStr.add(fileName);
            length = length != null ? Math.min(10485760, length) : LogviewerConstant.DEFAULT_BYTES_PER_PAGE;
            final boolean isZipFile = absFile.getFileName().toString().endsWith(".gz");
            long fileLength = getFileLength(absFile.toFile(), isZipFile);
            if (start == null) {
                start = Long.valueOf(fileLength - length).intValue();
            }
            String logString = isTxtFile(fileName) ? escapeHtml(pageFile(absFile.toString(), isZipFile, fileLength, start, length)) : escapeHtml("This is a binary file and cannot display! You may download the full file.");
            List<DomContent> bodyContents = new ArrayList<>();
            if (StringUtils.isNotEmpty(grep)) {
                String matchedString = String.join("\n", Arrays.stream(logString.split("\n")).filter(str -> str.contains(grep)).collect(toList()));
                bodyContents.add(pre(matchedString).withId("logContent"));
            } else {
                DomContent pagerData = null;
                if (isTxtFile(fileName)) {
                    pagerData = pagerLinks(fileName, start, length, Long.valueOf(fileLength).intValue(), "log");
                }
                bodyContents.add(searchFileForm(fileName, "no"));
                // list all files for this topology
                bodyContents.add(logFileSelectionForm(reorderedFilesStr, fileName, "log"));
                if (pagerData != null) {
                    bodyContents.add(pagerData);
                }
                bodyContents.add(downloadLink(fileName));
                bodyContents.add(pre(logString).withClass("logContent"));
                if (pagerData != null) {
                    bodyContents.add(pagerData);
                }
            }
            String content = logTemplate(bodyContents, fileName, user).render();
            return LogviewerResponseBuilder.buildSuccessHtmlResponse(content);
        } else {
            return LogviewerResponseBuilder.buildResponsePageNotFound();
        }
    } else {
        if (resourceAuthorizer.getLogUserGroupWhitelist(fileName) == null) {
            return LogviewerResponseBuilder.buildResponsePageNotFound();
        } else {
            return LogviewerResponseBuilder.buildResponseUnauthorizedUser(user);
        }
    }
}
Also used : Path(java.nio.file.Path) Arrays(java.util.Arrays) GZIPInputStream(java.util.zip.GZIPInputStream) StringUtils(org.apache.commons.lang.StringUtils) SortedSet(java.util.SortedSet) LogviewerConstant(org.apache.storm.daemon.logviewer.LogviewerConstant) TagCreator.h3(j2html.TagCreator.h3) TagCreator.head(j2html.TagCreator.head) Matcher(java.util.regex.Matcher) TagCreator.link(j2html.TagCreator.link) Map(java.util.Map) ExceptionMeterNames(org.apache.storm.daemon.logviewer.utils.ExceptionMeterNames) UIHelpers(org.apache.storm.daemon.ui.UIHelpers) TagCreator.input(j2html.TagCreator.input) Path(java.nio.file.Path) UrlBuilder(org.apache.storm.daemon.utils.UrlBuilder) Unchecked(org.jooq.lambda.Unchecked) LogviewerResponseBuilder(org.apache.storm.daemon.logviewer.utils.LogviewerResponseBuilder) FileNotFoundException(java.io.FileNotFoundException) UncheckedIOException(java.io.UncheckedIOException) List(java.util.List) ConfigUtils(org.apache.storm.utils.ConfigUtils) Response(javax.ws.rs.core.Response) TagCreator.title(j2html.TagCreator.title) Pattern(java.util.regex.Pattern) InvalidRequestException(org.apache.storm.daemon.ui.InvalidRequestException) TagCreator.text(j2html.TagCreator.text) TagCreator.body(j2html.TagCreator.body) ByteArrayOutputStream(java.io.ByteArrayOutputStream) TagCreator.html(j2html.TagCreator.html) HashMap(java.util.HashMap) TreeSet(java.util.TreeSet) TagCreator.pre(j2html.TagCreator.pre) ArrayList(java.util.ArrayList) TagCreator.option(j2html.TagCreator.option) Collectors.toCollection(java.util.stream.Collectors.toCollection) Meter(com.codahale.metrics.Meter) DirectoryCleaner(org.apache.storm.daemon.logviewer.utils.DirectoryCleaner) ServerUtils(org.apache.storm.utils.ServerUtils) ResourceAuthorizer(org.apache.storm.daemon.logviewer.utils.ResourceAuthorizer) StormMetricsRegistry(org.apache.storm.metric.StormMetricsRegistry) TagCreator.p(j2html.TagCreator.p) Files(java.nio.file.Files) TagCreator.select(j2html.TagCreator.select) IOException(java.io.IOException) FileInputStream(java.io.FileInputStream) TagCreator.form(j2html.TagCreator.form) TagCreator.a(j2html.TagCreator.a) WorkerLogs(org.apache.storm.daemon.logviewer.utils.WorkerLogs) File(java.io.File) StreamUtil(org.apache.storm.daemon.utils.StreamUtil) DomContent(j2html.tags.DomContent) Collectors.toList(java.util.stream.Collectors.toList) StringEscapeUtils.escapeHtml(org.apache.commons.lang.StringEscapeUtils.escapeHtml) Paths(java.nio.file.Paths) TagCreator.div(j2html.TagCreator.div) Collections(java.util.Collections) InputStream(java.io.InputStream) ArrayList(java.util.ArrayList) DomContent(j2html.tags.DomContent) UncheckedIOException(java.io.UncheckedIOException) WorkerLogs(org.apache.storm.daemon.logviewer.utils.WorkerLogs) TreeSet(java.util.TreeSet) Files(java.nio.file.Files)

Example 3 with InvalidRequestException

use of org.apache.storm.daemon.ui.InvalidRequestException in project storm by apache.

the class LogviewerLogPageHandler method daemonLogPage.

/**
 * Provides a daemon log file to view.
 *
 * @param fileName file to view
 * @param start start offset, or null if the most recent page is desired
 * @param length length to read in this page, or null if default page length is desired
 * @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 {
    Path file = daemonLogRoot.resolve(fileName).toAbsolutePath().normalize();
    if (!file.startsWith(daemonLogRoot) || Paths.get(fileName).getNameCount() != 1) {
        // Prevent fileName from pathing into worker logs, or outside daemon log root
        return LogviewerResponseBuilder.buildResponsePageNotFound();
    }
    if (file.toFile().exists()) {
        // all types of files included
        List<File> logFiles = Arrays.stream(daemonLogRoot.toFile().listFiles()).filter(File::isFile).collect(toList());
        List<String> reorderedFilesStr = logFiles.stream().map(File::getName).filter(fName -> !StringUtils.equals(fileName, fName)).collect(toList());
        reorderedFilesStr.add(fileName);
        length = length != null ? Math.min(10485760, length) : LogviewerConstant.DEFAULT_BYTES_PER_PAGE;
        final boolean isZipFile = file.getFileName().toString().endsWith(".gz");
        long fileLength = getFileLength(file.toFile(), isZipFile);
        if (start == null) {
            start = Long.valueOf(fileLength - length).intValue();
        }
        String logString = isTxtFile(fileName) ? escapeHtml(pageFile(file.toString(), isZipFile, fileLength, start, length)) : escapeHtml("This is a binary file and cannot display! You may download the full file.");
        List<DomContent> bodyContents = new ArrayList<>();
        if (StringUtils.isNotEmpty(grep)) {
            String matchedString = String.join("\n", Arrays.stream(logString.split("\n")).filter(str -> str.contains(grep)).collect(toList()));
            bodyContents.add(pre(matchedString).withId("logContent"));
        } else {
            DomContent pagerData = null;
            if (isTxtFile(fileName)) {
                pagerData = pagerLinks(fileName, start, length, Long.valueOf(fileLength).intValue(), "daemonlog");
            }
            bodyContents.add(searchFileForm(fileName, "yes"));
            // list all daemon logs
            bodyContents.add(logFileSelectionForm(reorderedFilesStr, fileName, "daemonlog"));
            if (pagerData != null) {
                bodyContents.add(pagerData);
            }
            bodyContents.add(daemonDownloadLink(fileName));
            bodyContents.add(pre(logString).withClass("logContent"));
            if (pagerData != null) {
                bodyContents.add(pagerData);
            }
        }
        String content = logTemplate(bodyContents, fileName, user).render();
        return LogviewerResponseBuilder.buildSuccessHtmlResponse(content);
    } else {
        return LogviewerResponseBuilder.buildResponsePageNotFound();
    }
}
Also used : Path(java.nio.file.Path) Arrays(java.util.Arrays) GZIPInputStream(java.util.zip.GZIPInputStream) StringUtils(org.apache.commons.lang.StringUtils) SortedSet(java.util.SortedSet) LogviewerConstant(org.apache.storm.daemon.logviewer.LogviewerConstant) TagCreator.h3(j2html.TagCreator.h3) TagCreator.head(j2html.TagCreator.head) Matcher(java.util.regex.Matcher) TagCreator.link(j2html.TagCreator.link) Map(java.util.Map) ExceptionMeterNames(org.apache.storm.daemon.logviewer.utils.ExceptionMeterNames) UIHelpers(org.apache.storm.daemon.ui.UIHelpers) TagCreator.input(j2html.TagCreator.input) Path(java.nio.file.Path) UrlBuilder(org.apache.storm.daemon.utils.UrlBuilder) Unchecked(org.jooq.lambda.Unchecked) LogviewerResponseBuilder(org.apache.storm.daemon.logviewer.utils.LogviewerResponseBuilder) FileNotFoundException(java.io.FileNotFoundException) UncheckedIOException(java.io.UncheckedIOException) List(java.util.List) ConfigUtils(org.apache.storm.utils.ConfigUtils) Response(javax.ws.rs.core.Response) TagCreator.title(j2html.TagCreator.title) Pattern(java.util.regex.Pattern) InvalidRequestException(org.apache.storm.daemon.ui.InvalidRequestException) TagCreator.text(j2html.TagCreator.text) TagCreator.body(j2html.TagCreator.body) ByteArrayOutputStream(java.io.ByteArrayOutputStream) TagCreator.html(j2html.TagCreator.html) HashMap(java.util.HashMap) TreeSet(java.util.TreeSet) TagCreator.pre(j2html.TagCreator.pre) ArrayList(java.util.ArrayList) TagCreator.option(j2html.TagCreator.option) Collectors.toCollection(java.util.stream.Collectors.toCollection) Meter(com.codahale.metrics.Meter) DirectoryCleaner(org.apache.storm.daemon.logviewer.utils.DirectoryCleaner) ServerUtils(org.apache.storm.utils.ServerUtils) ResourceAuthorizer(org.apache.storm.daemon.logviewer.utils.ResourceAuthorizer) StormMetricsRegistry(org.apache.storm.metric.StormMetricsRegistry) TagCreator.p(j2html.TagCreator.p) Files(java.nio.file.Files) TagCreator.select(j2html.TagCreator.select) IOException(java.io.IOException) FileInputStream(java.io.FileInputStream) TagCreator.form(j2html.TagCreator.form) TagCreator.a(j2html.TagCreator.a) WorkerLogs(org.apache.storm.daemon.logviewer.utils.WorkerLogs) File(java.io.File) StreamUtil(org.apache.storm.daemon.utils.StreamUtil) DomContent(j2html.tags.DomContent) Collectors.toList(java.util.stream.Collectors.toList) StringEscapeUtils.escapeHtml(org.apache.commons.lang.StringEscapeUtils.escapeHtml) Paths(java.nio.file.Paths) TagCreator.div(j2html.TagCreator.div) Collections(java.util.Collections) InputStream(java.io.InputStream) ArrayList(java.util.ArrayList) DomContent(j2html.tags.DomContent) File(java.io.File)

Example 4 with InvalidRequestException

use of org.apache.storm.daemon.ui.InvalidRequestException in project storm by apache.

the class LogviewerLogSearchHandler method searchLogFile.

/**
 * 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 callbackParameterName 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 {
    boolean noResult = true;
    Path rootDir = isDaemon ? daemonLogRoot : logRoot;
    Path rawFile = rootDir.resolve(fileName);
    Path absFile = rawFile.toAbsolutePath().normalize();
    if (!absFile.startsWith(rootDir) || !rawFile.normalize().toString().equals(rawFile.toString())) {
        // Ensure filename doesn't contain ../ parts
        return searchLogFileNotFound(callback);
    }
    if (isDaemon && Paths.get(fileName).getNameCount() != 1) {
        // Don't permit path traversal for calls intended to read from the daemon logs
        return searchLogFileNotFound(callback);
    }
    Response response;
    if (absFile.toFile().exists()) {
        if (isDaemon || resourceAuthorizer.isUserAllowedToAccessFile(user, fileName)) {
            Integer numMatchesInt = numMatchesStr != null ? tryParseIntParam("num-matches", numMatchesStr) : null;
            Integer offsetInt = offsetStr != null ? tryParseIntParam("start-byte-offset", offsetStr) : null;
            try {
                if (StringUtils.isNotEmpty(search) && search.getBytes("UTF-8").length <= GREP_MAX_SEARCH_SIZE) {
                    Map<String, Object> entity = new HashMap<>();
                    entity.put("isDaemon", isDaemon ? "yes" : "no");
                    Map<String, Object> res = substringSearch(absFile, search, isDaemon, numMatchesInt, offsetInt);
                    entity.putAll(res);
                    noResult = ((List) res.get("matches")).isEmpty();
                    response = LogviewerResponseBuilder.buildSuccessJsonResponse(entity, callback, origin);
                } else {
                    throw new InvalidRequestException("Search substring must be between 1 and 1024 " + "UTF-8 bytes in size (inclusive)");
                }
            } catch (Exception ex) {
                response = LogviewerResponseBuilder.buildExceptionJsonResponse(ex, callback);
            }
        } else {
            // unauthorized
            response = LogviewerResponseBuilder.buildUnauthorizedUserJsonResponse(user, callback);
        }
    } else {
        response = searchLogFileNotFound(callback);
    }
    if (noResult) {
        numSearchRequestNoResult.mark();
    }
    return response;
}
Also used : Path(java.nio.file.Path) Response(javax.ws.rs.core.Response) HashMap(java.util.HashMap) InvalidRequestException(org.apache.storm.daemon.ui.InvalidRequestException) UnsupportedEncodingException(java.io.UnsupportedEncodingException) InvalidRequestException(org.apache.storm.daemon.ui.InvalidRequestException) JsonProcessingException(com.fasterxml.jackson.core.JsonProcessingException) IOException(java.io.IOException) UnknownHostException(java.net.UnknownHostException)

Example 5 with InvalidRequestException

use of org.apache.storm.daemon.ui.InvalidRequestException in project storm by apache.

the class LogviewerLogSearchHandler method substringSearch.

private Map<String, Object> substringSearch(Path file, String searchString, boolean isDaemon, Integer numMatches, Integer startByteOffset) throws InvalidRequestException {
    if (StringUtils.isEmpty(searchString)) {
        throw new IllegalArgumentException("Precondition fails: search string should not be empty.");
    }
    if (searchString.getBytes(StandardCharsets.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);
    }
    boolean isZipFile = file.toString().endsWith(".gz");
    try (InputStream fis = Files.newInputStream(file)) {
        try (InputStream gzippedInputStream = isZipFile ? new GZIPInputStream(fis) : fis;
            BufferedInputStream stream = new BufferedInputStream(gzippedInputStream)) {
            // It's more likely to be a file read exception here, so we don't differentiate
            int fileLength = isZipFile ? (int) ServerUtils.zipFileSize(file.toFile()) : (int) Files.size(file);
            ByteBuffer buf = ByteBuffer.allocate(GREP_BUF_SIZE);
            final byte[] bufArray = buf.array();
            final byte[] searchBytes = searchString.getBytes(StandardCharsets.UTF_8);
            numMatches = numMatches != null ? numMatches : 10;
            startByteOffset = startByteOffset != null ? startByteOffset : 0;
            // Allow searching when start-byte-offset == file-len so it doesn't blow up on 0-length files
            if (startByteOffset > fileLength) {
                throw new InvalidRequestException("Cannot search past the end of the file");
            }
            if (startByteOffset > 0) {
                StreamUtil.skipBytes(stream, startByteOffset);
            }
            Arrays.fill(bufArray, (byte) 0);
            int totalBytesRead = 0;
            int bytesRead = stream.read(bufArray, 0, Math.min(fileLength, GREP_BUF_SIZE));
            buf.limit(bytesRead);
            totalBytesRead += bytesRead;
            List<Map<String, Object>> initialMatches = new ArrayList<>();
            int initBufOffset = 0;
            int byteOffset = startByteOffset;
            byte[] beforeBytes = null;
            Map<String, Object> ret = new HashMap<>();
            while (true) {
                SubstringSearchResult searchRet = bufferSubstringSearch(isDaemon, file, fileLength, byteOffset, initBufOffset, stream, startByteOffset, totalBytesRead, buf, searchBytes, initialMatches, numMatches, beforeBytes);
                List<Map<String, Object>> matches = searchRet.getMatches();
                Integer newByteOffset = searchRet.getNewByteOffset();
                byte[] newBeforeBytes = searchRet.getNewBeforeBytes();
                if (matches.size() < numMatches && totalBytesRead + startByteOffset < fileLength) {
                    // The start index is positioned to find any possible
                    // occurrence search string that did not quite fit in the
                    // buffer on the previous read.
                    final int newBufOffset = Math.min(buf.limit(), GREP_MAX_SEARCH_SIZE) - searchBytes.length;
                    totalBytesRead = rotateGrepBuffer(buf, stream, totalBytesRead, fileLength);
                    if (totalBytesRead < 0) {
                        throw new InvalidRequestException("Cannot search past the end of the file");
                    }
                    initialMatches = matches;
                    initBufOffset = newBufOffset;
                    byteOffset = newByteOffset;
                    beforeBytes = newBeforeBytes;
                } else {
                    ret.put("isDaemon", isDaemon ? "yes" : "no");
                    Integer nextByteOffset = null;
                    if (matches.size() >= numMatches || totalBytesRead < fileLength) {
                        nextByteOffset = (Integer) last(matches).get("byteOffset") + searchBytes.length;
                        if (fileLength <= nextByteOffset) {
                            nextByteOffset = null;
                        }
                    }
                    ret.putAll(mkGrepResponse(searchBytes, startByteOffset, matches, nextByteOffset));
                    break;
                }
            }
            return ret;
        } catch (UnknownHostException | UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            numFileReadExceptions.mark();
            throw new RuntimeException(e);
        }
    } catch (IOException e) {
        numFileOpenExceptions.mark();
        throw new RuntimeException(e);
    }
}
Also used : UnknownHostException(java.net.UnknownHostException) HashMap(java.util.HashMap) GZIPInputStream(java.util.zip.GZIPInputStream) BufferedInputStream(java.io.BufferedInputStream) InputStream(java.io.InputStream) ArrayList(java.util.ArrayList) UnsupportedEncodingException(java.io.UnsupportedEncodingException) IOException(java.io.IOException) ByteBuffer(java.nio.ByteBuffer) GZIPInputStream(java.util.zip.GZIPInputStream) BufferedInputStream(java.io.BufferedInputStream) InvalidRequestException(org.apache.storm.daemon.ui.InvalidRequestException) Map(java.util.Map) HashMap(java.util.HashMap)

Aggregations

InvalidRequestException (org.apache.storm.daemon.ui.InvalidRequestException)7 IOException (java.io.IOException)6 HashMap (java.util.HashMap)5 InputStream (java.io.InputStream)4 Path (java.nio.file.Path)4 ArrayList (java.util.ArrayList)4 Map (java.util.Map)4 GZIPInputStream (java.util.zip.GZIPInputStream)4 ByteArrayOutputStream (java.io.ByteArrayOutputStream)3 FileInputStream (java.io.FileInputStream)3 FileNotFoundException (java.io.FileNotFoundException)3 UncheckedIOException (java.io.UncheckedIOException)3 Response (javax.ws.rs.core.Response)3 Meter (com.codahale.metrics.Meter)2 TagCreator.a (j2html.TagCreator.a)2 TagCreator.body (j2html.TagCreator.body)2 TagCreator.div (j2html.TagCreator.div)2 TagCreator.form (j2html.TagCreator.form)2 TagCreator.h3 (j2html.TagCreator.h3)2 TagCreator.head (j2html.TagCreator.head)2