aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCow <cow@volloeko.de>2021-04-24 20:30:56 +0200
committerCow <cow@volloeko.de>2021-04-24 20:30:56 +0200
commitefb5a50de5924d0185e80269cccd67da77a337b3 (patch)
treecf6e24090a90787329bc5699d746543e83dd1cfc
parent8f7e875f1e79d6e728c737bbe6318143f9b41a43 (diff)
downloadCowlection-efb5a50de5924d0185e80269cccd67da77a337b3.tar.gz
Cowlection-efb5a50de5924d0185e80269cccd67da77a337b3.tar.bz2
Cowlection-efb5a50de5924d0185e80269cccd67da77a337b3.zip
Improved speed of Log Search
-rw-r--r--CHANGELOG.md3
-rw-r--r--src/main/java/de/cowtipper/cowlection/search/LogFilesSearcher.java156
2 files changed, 80 insertions, 79 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e6ed15a..0f81699 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -41,11 +41,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- colored overlay is now also disable-able via config
- Player lookup now shows - in addition to the active pet - a spirit pet
- Dungeon Performance Overlay: added an alternative text border option
-- Dungeon item tooltips: Gear Score can now be hidden separately (instead of getting replaced by Item Quality)
+- Dungeon item tooltips: Gear Score can now be hidden separately (instead of getting replaced by Item Quality)
### Fixed
- Fixed issue with 'no dung class selected'
- Unexpected API-related exceptions no longer void all chat output
+- Greatly increased speed of the Log Search (`/moo search`)
## [1.8.9-0.12.0] - 03.01.2021
### Added
diff --git a/src/main/java/de/cowtipper/cowlection/search/LogFilesSearcher.java b/src/main/java/de/cowtipper/cowlection/search/LogFilesSearcher.java
index a3ed781..c6b647e 100644
--- a/src/main/java/de/cowtipper/cowlection/search/LogFilesSearcher.java
+++ b/src/main/java/de/cowtipper/cowlection/search/LogFilesSearcher.java
@@ -1,22 +1,23 @@
package de.cowtipper.cowlection.search;
-import com.mojang.realmsclient.util.Pair;
import de.cowtipper.cowlection.config.MooConfig;
import net.minecraft.util.EnumChatFormatting;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.ImmutableTriple;
import java.io.*;
-import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.*;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.Comparator;
import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
+import java.util.stream.Stream;
import java.util.zip.GZIPInputStream;
class LogFilesSearcher {
@@ -32,77 +33,100 @@ class LogFilesSearcher {
private int analyzedFilesWithHits = 0;
ImmutableTriple<Integer, Integer, List<LogEntry>> searchFor(String searchQuery, boolean chatOnly, boolean matchCase, boolean removeFormatting, LocalDate dateStart, LocalDate dateEnd) throws IOException {
- List<Pair<Path, LocalDate>> files = new ArrayList<>();
+ AtomicInteger foundLogs = new AtomicInteger();
+ List<LogEntry> searchResults = Collections.synchronizedList(new ArrayList<>());
for (String logsDirPath : MooConfig.logsDirs) {
File logsDir = new File(logsDirPath);
- if (logsDir.exists() && logsDir.isDirectory()) {
- try {
- files.addAll(fileList(logsDir, dateStart, dateEnd));
- } catch (IOException e) {
- throw new IOException(EnumChatFormatting.DARK_RED + "ERROR: An error occurred trying to read/parse '" + EnumChatFormatting.RED + logsDirPath + EnumChatFormatting.DARK_RED + "':\n"
- + EnumChatFormatting.GOLD + e.getLocalizedMessage(), e);
+ if (!logsDir.exists() || !logsDir.isDirectory()) {
+ continue;
+ }
+ try (Stream<Path> paths = Files.find(logsDir.toPath(), 1, (path, attr) -> {
+ if (!attr.isRegularFile()) {
+ return false;
}
+ String fileName = path.getFileName().toString();
+ return fileName.endsWith(".log.gz") || "latest.log".equals(fileName);
+ }).collect(Collectors.toList()).parallelStream()) {
+ paths.forEach(path -> {
+ String fileName = path.getFileName().toString();
+ if (fileName.endsWith("z")) { // .log.gz
+ Matcher fileNameMatcher = LOG_FILE_PATTERN.matcher(fileName);
+ if (fileNameMatcher.matches()) {
+ LocalDate fileLocalDate = LocalDate.of(Integer.parseInt(fileNameMatcher.group(1)),
+ Integer.parseInt(fileNameMatcher.group(2)), Integer.parseInt(fileNameMatcher.group(3)));
+ if (!fileLocalDate.isBefore(dateStart) && !fileLocalDate.isAfter(dateEnd)) {
+ foundLogs.incrementAndGet();
+ searchResults.addAll(analyzeFile(path, true, fileLocalDate, searchQuery, chatOnly, matchCase, removeFormatting));
+ }
+ }
+ } else if (fileName.equals("latest.log")) {
+ LocalDate lastModified = Instant.ofEpochMilli(path.toFile().lastModified()).atZone(ZoneId.systemDefault()).toLocalDate();
+ if (!lastModified.isBefore(dateStart) && !lastModified.isAfter(dateEnd)) {
+ foundLogs.incrementAndGet();
+ searchResults.addAll(analyzeFile(path, false, lastModified, searchQuery, chatOnly, matchCase, removeFormatting));
+ }
+ }
+ });
+ } catch (IOException e) {
+ throw new IOException(EnumChatFormatting.DARK_RED + "ERROR: An error occurred trying to read/parse '" + EnumChatFormatting.RED + logsDirPath + EnumChatFormatting.DARK_RED + "':\n"
+ + EnumChatFormatting.GOLD + e.getLocalizedMessage(), e);
}
}
- if (files.isEmpty()) {
+ if (foundLogs.get() == 0) {
throw new FileNotFoundException(EnumChatFormatting.DARK_RED + "ERROR: No Minecraft log files could be found for the selected date range. Please check if the dates as well as the directories of the log files are set correctly (Log Search ➡ Settings).");
} else {
- List<LogEntry> searchResults = analyzeFiles(files, searchQuery, chatOnly, matchCase, removeFormatting)
+ List<LogEntry> sortedSearchResults = searchResults
.stream().sorted(Comparator.comparing(LogEntry::getTime)).collect(Collectors.toList());
- return new ImmutableTriple<>(files.size(), analyzedFilesWithHits, searchResults);
+ return new ImmutableTriple<>(foundLogs.get(), analyzedFilesWithHits, sortedSearchResults);
}
}
- private List<LogEntry> analyzeFiles(List<Pair<Path, LocalDate>> pathsData, String searchTerm, boolean chatOnly, boolean matchCase, boolean removeFormatting) {
+ private List<LogEntry> analyzeFile(Path path, boolean isGzipped, LocalDate date, String searchTerm, boolean chatOnly, boolean matchCase, boolean removeFormatting) {
List<LogEntry> searchResults = new ArrayList<>();
- for (Pair<Path, LocalDate> pathData : pathsData) {
- Path path = pathData.first();
- boolean foundSearchTermInFile = false;
- try (BufferedReader in = (path.endsWith("latest.log")
- ? new BufferedReader(new InputStreamReader(new FileInputStream(path.toFile()))) // latest.log
- : new BufferedReader(new InputStreamReader(new GZIPInputStream(new FileInputStream(path.toFile())))))) { // ....log.gz
- LocalDate date = pathData.second();
- String content;
- LogEntry logEntry = null;
- while ((content = in.readLine()) != null) {
- Matcher logLineMatcher = LOG4J_PATTERN.matcher(content);
- if (logLineMatcher.matches()) { // current line is a new log entry
- if (logEntry != null) {
- // we had a previous log entry; analyze it!
- LogEntry result = analyzeLogEntry(logEntry, searchTerm, matchCase, removeFormatting);
- if (result != null) {
- searchResults.add(result);
- foundSearchTermInFile = true;
- }
- logEntry = null;
- }
- // handle first line of new log entry
- if (chatOnly && logLineMatcher.group("isChat") == null) {
- // not a chat log entry, although we're only searching for chat messages, abort!
- continue;
+ boolean foundSearchTermInFile = false;
+ try (BufferedReader in = (isGzipped
+ ? new BufferedReader(new InputStreamReader(new GZIPInputStream(new FileInputStream(path.toFile())))) // ....log.gz
+ : new BufferedReader(new InputStreamReader(new FileInputStream(path.toFile()))))) { // latest.log
+ String content;
+ LogEntry logEntry = null;
+ while ((content = in.readLine()) != null) {
+ Matcher logLineMatcher = LOG4J_PATTERN.matcher(content);
+ if (logLineMatcher.matches()) { // current line is a new log entry
+ if (logEntry != null) {
+ // we had a previous log entry; analyze it!
+ LogEntry result = analyzeLogEntry(logEntry, searchTerm, matchCase, removeFormatting);
+ if (result != null) {
+ searchResults.add(result);
+ foundSearchTermInFile = true;
}
- LocalDateTime dateTime = getDate(date, logLineMatcher);
- logEntry = new LogEntry(dateTime, path, logLineMatcher.group("message"));
- } else if (logEntry != null) {
- // multiline log entry
- logEntry.addLogLine(content);
+ logEntry = null;
}
- }
- if (logEntry != null) {
- // end of file! analyze last log entry in file
- LogEntry result = analyzeLogEntry(logEntry, searchTerm, matchCase, removeFormatting);
- if (result != null) {
- searchResults.add(result);
- foundSearchTermInFile = true;
+ // handle first line of new log entry
+ if (chatOnly && logLineMatcher.group("isChat") == null) {
+ // not a chat log entry, although we're only searching for chat messages, abort!
+ continue;
}
+ LocalDateTime dateTime = getDate(date, logLineMatcher);
+ logEntry = new LogEntry(dateTime, path, logLineMatcher.group("message"));
+ } else if (logEntry != null) {
+ // multiline log entry
+ logEntry.addLogLine(content);
}
- if (foundSearchTermInFile) {
- analyzedFilesWithHits++;
+ }
+ if (logEntry != null) {
+ // end of file! analyze last log entry in file
+ LogEntry result = analyzeLogEntry(logEntry, searchTerm, matchCase, removeFormatting);
+ if (result != null) {
+ searchResults.add(result);
+ foundSearchTermInFile = true;
}
- } catch (IOException ignored) {
- // most likely corrupted .log.gz file - skip it.
}
+ if (foundSearchTermInFile) {
+ analyzedFilesWithHits++;
+ }
+ } catch (IOException ignored) {
+ // most likely corrupted .log.gz file - skip it.
}
return searchResults;
}
@@ -138,28 +162,4 @@ class LogFilesSearcher {
return logEntry;
}
-
- private List<Pair<Path, LocalDate>> fileList(File directory, LocalDate startDate, LocalDate endDate) throws IOException {
- List<Pair<Path, LocalDate>> fileNames = new ArrayList<>();
- try (DirectoryStream<Path> directoryStream = Files.newDirectoryStream(directory.toPath())) {
- for (Path path : directoryStream) {
- if (path.toString().endsWith(".log.gz")) {
- Matcher fileNameMatcher = LOG_FILE_PATTERN.matcher(path.getFileName().toString());
- if (fileNameMatcher.matches()) {
- LocalDate fileLocalDate = LocalDate.of(Integer.parseInt(fileNameMatcher.group(1)),
- Integer.parseInt(fileNameMatcher.group(2)), Integer.parseInt(fileNameMatcher.group(3)));
- if (!fileLocalDate.isBefore(startDate) && !fileLocalDate.isAfter(endDate)) {
- fileNames.add(Pair.of(path, fileLocalDate));
- }
- }
- } else if (path.getFileName().toString().equals("latest.log")) {
- LocalDate lastModified = Instant.ofEpochMilli(path.toFile().lastModified()).atZone(ZoneId.systemDefault()).toLocalDate();
- if (!lastModified.isBefore(startDate) && !lastModified.isAfter(endDate)) {
- fileNames.add(Pair.of(path, lastModified));
- }
- }
- }
- }
- return fileNames;
- }
}