import java.io.*;
import java.util.*;
import java.util.regex.*;
import java.util.stream.Collectors;
public class SQLAnalyzer {
// Simplified pattern specifically for SELECT queries
private static final Pattern LOG_PATTERN = Pattern.compile("\\[((?:ACTIVE|STUCK|Async-Pool-\\d+-Thread-\\d+))\\].*?" +
"(?:ExecuteThread: '(\\d+)'|Async-Pool-\\d+-Thread-\\d+).*?" +
"(?:preparing:|select)\\s+(.+?)(?=\\[|$)", Pattern.CASE_INSENSITIVE);
private final Map<String, QueryStats> queryDistribution;
private final Set<String> knownStates;
private PrintWriter writer;
public SQLAnalyzer() {
this.queryDistribution = new HashMap<>();
this.knownStates = new HashSet<>();
}
private static String repeatString(String str, int count) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < count; i++) {
sb.append(str);
}
return sb.toString();
}
public static class ThreadInfo {
private final String threadId;
private int occurrences;
private final Set<Integer> lineNumbers;
private final boolean isAsyncPool;
public ThreadInfo(String threadId, boolean isAsyncPool) {
this.threadId = threadId;
this.occurrences = 0;
this.lineNumbers = new HashSet<>();
this.isAsyncPool = isAsyncPool;
}
public void addOccurrence(int lineNumber) {
occurrences++;
lineNumbers.add(lineNumber);
}
@Override
public String toString() {
if (isAsyncPool) {
return String.format("%s, Lines: %s",
threadId,
lineNumbers.stream()
.sorted()
.map(String::valueOf)
.collect(Collectors.joining(", ")));
} else {
return String.format("Thread %s: %d occurrences, Lines: %s",
threadId,
occurrences,
lineNumbers.stream()
.sorted()
.map(String::valueOf)
.collect(Collectors.joining(", ")));
}
}
}
public static class StateInfo {
private final Map<String, ThreadInfo> threadStats;
private int totalOccurrences;
private final boolean isAsyncPool;
public StateInfo(boolean isAsyncPool) {
this.threadStats = new HashMap<>();
this.totalOccurrences = 0;
this.isAsyncPool = isAsyncPool;
}
public void addOccurrence(String threadId, int lineNumber) {
threadStats.computeIfAbsent(threadId, id -> new ThreadInfo(id, isAsyncPool))
.addOccurrence(lineNumber);
totalOccurrences++;
}
public int getTotalOccurrences() {
return totalOccurrences;
}
public Collection<ThreadInfo> getThreadStats() {
return threadStats.values();
}
public boolean isAsyncPool() {
return isAsyncPool;
}
}
public static class QueryStats {
private final String originalQuery;
private int occurrences;
private final Map<String, StateInfo> stateStats;
public QueryStats(String query) {
this.originalQuery = query.trim();
this.occurrences = 0;
this.stateStats = new HashMap<>();
}
public void addOccurrence(String threadId, String state, int lineNumber, boolean isAsyncPool) {
occurrences++;
stateStats.computeIfAbsent(state, k -> new StateInfo(isAsyncPool))
.addOccurrence(threadId, lineNumber);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("Query: ").append(originalQuery.replaceAll("\\s+", " ")).append("\n");
sb.append(String.format("Total Occurrences: %d%n", occurrences));
sb.append("State Distribution:\n");
stateStats.entrySet().stream()
.sorted((e1, e2) -> {
boolean isAsync1 = e1.getValue().isAsyncPool();
boolean isAsync2 = e2.getValue().isAsyncPool();
if (isAsync1 != isAsync2) {
return isAsync1 ? 1 : -1;
}
return e1.getKey().compareTo(e2.getKey());
})
.forEach(entry -> {
String state = entry.getKey();
StateInfo stateInfo = entry.getValue();
if (stateInfo.isAsyncPool()) {
sb.append(String.format(" %s (Total: %d):%n", state, stateInfo.getTotalOccurrences()));
} else {
sb.append(String.format(" [%s] (Total: %d):%n", state, stateInfo.getTotalOccurrences()));
}
stateInfo.getThreadStats().forEach(threadInfo ->
sb.append(String.format(" %s%n", threadInfo))
);
});
return sb.toString();
}
}
public void analyzeLogs(String filePath) throws IOException {
try (BufferedReader reader = new BufferedReader(new FileReader(filePath))) {
String line;
int lineNumber = 0;
while ((line = reader.readLine()) != null) {
lineNumber++;
processLine(line, lineNumber);
}
}
}
private void processLine(String line, int lineNumber) {
Matcher matcher = LOG_PATTERN.matcher(line);
if (matcher.find()) {
String state = matcher.group(1);
String threadId = state.startsWith("Async-Pool") ? state : matcher.group(2);
String sqlStatement = matcher.group(3).trim();
boolean isAsyncPool = state.startsWith("Async-Pool");
if (!isAsyncPool && threadId == null) {
return;
}
// Simple normalization for SELECT queries
String normalizedSQL = sqlStatement.replaceAll("\\s+", " ").trim();
queryDistribution
.computeIfAbsent(normalizedSQL, QueryStats::new)
.addOccurrence(threadId, state, lineNumber, isAsyncPool);
knownStates.add(state);
}
}
private void writeAndPrint(String text) {
System.out.print(text);
writer.print(text);
writer.flush();
}
private void printSummaryStats() {
StringBuilder sb = new StringBuilder("\n=== Summary Statistics ===\n");
int totalQueries = queryDistribution.values().stream()
.mapToInt(stats -> stats.occurrences)
.sum();
sb.append("Total unique SELECT queries: ").append(queryDistribution.size()).append("\n");
sb.append("Total query executions: ").append(totalQueries).append("\n");
Map<String, Integer> stateDistribution = new HashMap<>();
queryDistribution.values().forEach(stats ->
stats.stateStats.forEach((state, stateInfo) ->
stateDistribution.merge(state, stateInfo.getTotalOccurrences(), Integer::sum))
);
sb.append("\nQuery distribution by state:\n");
stateDistribution.entrySet().stream()
.sorted((e1, e2) -> {
boolean isAsync1 = e1.getKey().startsWith("Async-Pool");
boolean isAsync2 = e2.getKey().startsWith("Async-Pool");
if (isAsync1 != isAsync2) {
return isAsync1 ? 1 : -1;
}
return e1.getKey().compareTo(e2.getKey());
})
.forEach(entry -> {
String state = entry.getKey();
double percentage = (entry.getValue() * 100.0) / totalQueries;
if (state.startsWith("Async-Pool")) {
sb.append(String.format(" %s: %d queries (%.2f%%)%n",
state, entry.getValue(), percentage));
} else {
sb.append(String.format(" [%s]: %d queries (%.2f%%)%n",
state, entry.getValue(), percentage));
}
});
sb.append("\nThread type summary:\n");
int regularThreads = (int) stateDistribution.keySet().stream()
.filter(s -> !s.startsWith("Async-Pool"))
.count();
int asyncThreads = (int) stateDistribution.keySet().stream()
.filter(s -> s.startsWith("Async-Pool"))
.count();
sb.append(String.format(" Regular threads: %d%n", regularThreads));
sb.append(String.format(" Async-Pool threads: %d%n", asyncThreads));
writeAndPrint(sb.toString());
}
public void writeAnalysisToFile(String outputPath) throws IOException {
this.writer = new PrintWriter(new FileWriter(outputPath));
writeAndPrint("=== SQL Query Analysis ===\n\n");
// Sort queries by occurrence count
queryDistribution.values().stream()
.sorted((a, b) -> Integer.compare(b.occurrences, a.occurrences))
.forEach(stats -> {
writeAndPrint(stats.toString());
writeAndPrint(repeatString("-", 80) + "\n\n");
});
printSummaryStats();
writer.close();
}
public static void main(String[] args) {
if (args.length != 2) {
System.out.println("Usage: java SQLAnalyzer <log-file-path> <output-file-path>");
return;
}
SQLAnalyzer analyzer = new SQLAnalyzer();
try {
analyzer.analyzeLogs(args[0]);
analyzer.writeAnalysisToFile(args[1]);
System.out.println("\nAnalysis complete. Results have been written to: " + args[1]);
} catch (IOException e) {
System.err.println("Error: " + e.getMessage());
e.printStackTrace();
}
}
}
For further actions, you may consider blocking this person and/or reporting abuse
Top comments (0)