DEV Community

Charlie Fubon
Charlie Fubon

Posted on

Dev 1


import java.io.*;
import java.util.*;
import java.util.regex.*;
import java.util.stream.Collectors;

public class SQLLogAnalyzer {
    // Updated pattern to capture the full query, including the "select" keyword
    private static final Pattern LOG_PATTERN = Pattern.compile(".*?(\\[(ACTIVE|STUCK)\\])?.*?ExecuteThread:\\s+'(\\d+)'.*?((?:preparing: )?)(select.*)");
    private final Map<String, QueryStats> queryDistribution;

    public SQLLogAnalyzer() {
        this.queryDistribution = new HashMap<>();
    }

    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 QueryStats {
        private final String normalizedQuery;
        private int occurrences;
        private final Map<String, ThreadState> threadStates;
        private final Set<Integer> lineNumbers;

        public QueryStats(String query) {
            this.normalizedQuery = normalizeQuery(query);
            this.occurrences = 0;
            this.threadStates = new HashMap<>();
            this.lineNumbers = new HashSet<>();
        }

        private static String normalizeQuery(String query) {
            return query.trim();  // Keep original case and spacing
        }

        public void addOccurrence(String threadId, String state, int lineNumber) {
            occurrences++;
            state = (state == null) ? "UNKNOWN" : state;
            threadStates.computeIfAbsent(threadId, id -> new ThreadState())
                       .addState(state);
            lineNumbers.add(lineNumber);
        }

        @Override
        public String toString() {
            return String.format(
                "Query: %s%n" +
                "Total Occurrences: %d%n" +
                "Thread State Analysis:%n%s%n" +
                "Line Numbers: %s%n",
                normalizedQuery,
                occurrences,
                getThreadAnalysis(),
                lineNumbers.stream()
                    .sorted()
                    .map(String::valueOf)
                    .collect(Collectors.joining(", "))
            );
        }

        private String getThreadAnalysis() {
            Map<String, Integer> stateCount = new HashMap<>();
            Map<String, Set<String>> stateThreads = new HashMap<>();

            Arrays.asList("ACTIVE", "STUCK", "UNKNOWN").forEach(state -> {
                stateCount.put(state, 0);
                stateThreads.put(state, new HashSet<>());
            });

            threadStates.forEach((threadId, state) -> {
                state.getStateCounts().forEach((stateName, count) -> {
                    stateCount.merge(stateName, count, Integer::sum);
                    stateThreads.get(stateName).add(threadId);
                });
            });

            StringBuilder analysis = new StringBuilder();
            analysis.append("  Thread Distribution:\n");

            Arrays.asList("ACTIVE", "STUCK", "UNKNOWN").forEach(state -> {
                if (stateCount.get(state) > 0) {
                    analysis.append(String.format("    %s Threads: %d occurrences across %d threads%n", 
                        state, stateCount.get(state), stateThreads.get(state).size()));
                    analysis.append("    Thread IDs: ").append(
                        stateThreads.get(state).stream()
                            .sorted(Comparator.comparingInt(Integer::parseInt))
                            .collect(Collectors.joining(", ")))
                        .append("\n");
                }
            });

            return analysis.toString();
        }
    }

    static class ThreadState {
        private final Map<String, Integer> stateCounts;

        public ThreadState() {
            this.stateCounts = new HashMap<>();
        }

        public void addState(String state) {
            stateCounts.merge(state, 1, Integer::sum);
        }

        public Map<String, Integer> getStateCounts() {
            return stateCounts;
        }
    }

    public void analyzeLogs(String filePath) throws IOException {
        File file = new File(filePath);
        if (!file.exists()) {
            throw new FileNotFoundException("File not found: " + filePath);
        }

        try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
            String line;
            int lineNumber = 0;

            System.out.println("Analyzing file: " + file.getAbsolutePath());
            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(2); // This might be null
            String threadId = matcher.group(3);
            String query = matcher.group(5).trim();

            queryDistribution
                .computeIfAbsent(query, QueryStats::new)
                .addOccurrence(threadId, state, lineNumber);
        }
    }

    public void printAnalysis() {
        System.out.println("\n=== SQL Query Analysis ===\n");

        if (queryDistribution.isEmpty()) {
            System.out.println("No SQL queries found in the log file.");
            return;
        }

        queryDistribution.values().stream()
            .sorted((a, b) -> Integer.compare(b.occurrences, a.occurrences))
            .forEach(stats -> {
                System.out.println(stats);
                System.out.println(repeatString("-", 80) + "\n");
            });

        printSummaryStats();
    }

    private void printSummaryStats() {
        System.out.println("=== Summary Statistics ===");

        System.out.println("Total unique queries: " + queryDistribution.size());

        Map<String, Integer> totalStateDistribution = new HashMap<>();
        Map<String, Set<String>> totalThreadsByState = new HashMap<>();

        queryDistribution.values().forEach(stats -> 
            stats.threadStates.forEach((threadId, state) -> 
                state.getStateCounts().forEach((stateName, count) -> {
                    totalStateDistribution.merge(stateName, count, Integer::sum);
                    totalThreadsByState.computeIfAbsent(stateName, k -> new HashSet<>()).add(threadId);
                })));

        System.out.println("\nOverall Thread State Distribution:");
        totalStateDistribution.forEach((state, count) -> 
            System.out.printf("%s: %d occurrences across %d unique threads%n",
                state, count, totalThreadsByState.get(state).size()));
    }

    public static void main(String[] args) {
        SQLLogAnalyzer analyzer = new SQLLogAnalyzer();
        Scanner scanner = new Scanner(System.in);
        String filePath;

        if (args.length == 1) {
            filePath = args[0];
        } else {
            System.out.println("Please enter the path to your log file:");
            System.out.println("(Example: C:/logs/weblogic.log or ./logs/weblogic.log)");
            filePath = scanner.nextLine().trim();
        }

        try {
            analyzer.analyzeLogs(filePath);
            analyzer.printAnalysis();
        } catch (FileNotFoundException e) {
            System.err.println("Error: " + e.getMessage());
            System.err.println("Please check if the file path is correct and try again.");
        } catch (IOException e) {
            System.err.println("Error processing log file: " + e.getMessage());
            e.printStackTrace();
        } finally {
            scanner.close();
        }
    }
}

Enter fullscreen mode Exit fullscreen mode

Top comments (0)