DEV Community

Charlie Fubon
Charlie Fubon

Posted on

F1


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();
        }
    }
}

Enter fullscreen mode Exit fullscreen mode

Top comments (0)