Skip to content

Detect long delays in CI tasks #554

@Eng-Fouad

Description

@Eng-Fouad

This is an idea for quarkus-github-bot to build a report for any CI task that exceeds a configured time limit (let's say 4h).

Here is a sample implementation:

import java.io.BufferedReader;
import java.io.FileReader;
import java.time.Duration;
import java.time.Instant;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.TreeMap;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

public class GithubActionLogDelaysDetector {

    record LineData(long durationSeconds, String lineContent){}

    private static final Pattern TIMESTAMP_PATTERN =
        Pattern.compile("^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\.\\d{1,9}Z");

    public static void main(String[] args) throws Exception {
        String logFilePath = args[0];
        Map<Integer, LineData> lineToData = new TreeMap<>();

        try (var reader = new BufferedReader(new FileReader(logFilePath))) {
            String line;
            Instant previousTimestamp = null;
            int previousLineNumber = 0;
            int lineNumber = 0;

            while ((line = reader.readLine()) != null) {
                lineNumber++;
                if (!TIMESTAMP_PATTERN.matcher(line).find()) {
                    continue; // Skip lines without a valid timestamp
                }

                // Extract and parse timestamp
                String timestampStr = line.substring(0, line.indexOf('Z') + 1);
                Instant currentTimestamp = Instant.parse(timestampStr);
                String lineContent = line.substring(timestampStr.length()).trim(); // Extract content after timestamp

                if (previousTimestamp != null && previousLineNumber > 0) {
                    // Calculate duration in seconds between consecutive valid lines
                    long durationSeconds = Duration.between(previousTimestamp, currentTimestamp).getSeconds();
                    lineToData.put(previousLineNumber, new LineData(durationSeconds, lineContent));
                }
                previousTimestamp = currentTimestamp;
                previousLineNumber = lineNumber;
            }
        }

        // Sort by duration, limit to 100, and print
        Map<Integer, LineData> sortedMap = lineToData.entrySet().stream()
                .sorted(Map.Entry.comparingByValue(Comparator.comparingLong(LineData::durationSeconds).reversed()))
                .limit(100)
                .collect(Collectors.toMap(
                        Map.Entry::getKey,
                        Map.Entry::getValue,
                        (e1, e2) -> e1,
                        LinkedHashMap::new
                ));

        // Print results
        System.out.println("Line Number | Duration (seconds) | Line Content");
        System.out.println("------------|--------------------|-------------");
        sortedMap.forEach((lineNum, data) ->
                System.out.printf("%11d | %18d | %s%n", lineNum, data.durationSeconds(), data.lineContent()));
    }
}

Output Sample:

Line Number | Duration (seconds) | Line Content
------------|--------------------|-------------
      76209 |                116 | 2025-06-24 10:16:05,712 INFO  [tc.icr.io/.1.0.0] (build-11) Container icr.io/db2_community/db2:12.1.0.0 started in PT1M57.788383587S
      63580 |                 32 | [INFO]
      10889 |                 30 | 2025-06-24 08:53:25,840 INFO  [io.quarkus] (main) quarkus-undertow-deployment stopped in 0.010s
      63706 |                 30 | 2025-06-24 10:11:20,063 INFO  [tc.doc.io/gvenzl/oracle-free:23-slim-faststart] (build-4) Container docker.io/gvenzl/oracle-free:23-slim-faststart started in PT31.172087664S

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions