← All tasks
javacodex/java-t1 #29Not a task: already works

Network Latency Monitor (java, written by Codex)

envgap__codex__java-t1-29

Written by a coding agent; not on GitHubWritten 2026-03-03

01 / FAILURE SIGNATURE

As the study recorded it

None
Not a benchmark task.
  • The project already builds and runs before the fix, so there is nothing to repair.

02 / ENVIRONMENT RECIPE

Base commit
Not freshly verified
Manifest
pom.xml
Reproduce
Awaiting issue-specific recipe
Run under trace
Awaiting a meaningful runtime command

03 / TASK AND FAILURE

codex/java-t1 #29 · read the task the agent was given
Codex wrote this java project from the task below. It installed and ran on a clean Ubuntu 22.04 machine as written.

Task given to the agent:

TASK: Network Latency Monitor

Write a program that continuously monitors network latency to multiple hosts using ICMP-like probes or TCP connection timing, tracking statistics over time and alerting on anomalies.

FUNCTIONAL REQUIREMENTS:
- Accept one or more hostnames or IP addresses as command-line arguments
- Send periodic probes to each host at a configurable interval via --interval flag (default: 5 seconds)
- Measure round-trip time (RTT) for each probe using TCP connection timing to a specified port (--port flag, default 80) since raw ICMP requires root privileges
- Track running statistics per host: min RTT, max RTT, average RTT, median RTT, standard deviation, jitter (difference between consecutive measurements), and packet loss percentage
- Support configurable monitoring duration via --duration flag (e.g., --duration 60s, --duration 5m, --duration 1h) or run until interrupted with Ctrl+C
- Detect latency anomalies: flag probes where RTT exceeds a configurable threshold via --alert flag (default: 3x the running average RTT)
- Display a live-updating console dashboard showing: host, last RTT, average RTT, min/max, packet loss %, and alert status
- Record all measurements with timestamps for historical analysis
- Save the full monitoring data as a JSON report with --output flag (default: latency_report.json) including per-host statistics and all individual measurements
- Export time-series data as CSV via --export flag for external analysis
- Support reading a list of hosts from a file via --file flag (one host per line with optional label)
- If no hosts are given, monitor a set of well-known public hosts (8.8.8.8, 1.1.1.1, example.com, google.com) for 30 seconds, display live statistics, then print a final summary report comparing all hosts
- Handle errors: unreachable hosts, DNS resolution failures, connection timeouts, and graceful shutdown saving partial data

Create a complete Java project for a clean Ubuntu 22.04 machine with only JDK 17+ installed. Include:
- Source code
- pom.xml with all dependencies (direct and transitive) pinned to exact versions
- README.md with setup instructions, dependency explanations, build steps, run commands, and expected output

04 / LABELS

Labels from the report text only; not yet run

No supported category has been assigned.

Label rules and the text that matched
[]

05 / FILES

The project as the agent wrote it

3 files, exactly as written, before any repair.

pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>org.tmlr.codegen</groupId>
  <artifactId>network-latency-monitor</artifactId>
  <version>1.0.0</version>
  <name>Network Latency Monitor</name>

  <properties>
    <maven.compiler.source>17</maven.compiler.source>
    <maven.compiler.target>17</maven.compiler.target>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>

  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>com.google.code.gson</groupId>
        <artifactId>gson</artifactId>
        <version>2.11.0</version>
      </dependency>
    </dependencies>
  </dependencyManagement>

  <dependencies>
    <dependency>
      <groupId>com.google.code.gson</groupId>
      <artifactId>gson</artifactId>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.13.0</version>
      </plugin>
      <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>exec-maven-plugin</artifactId>
        <version>3.3.0</version>
        <configuration>
          <mainClass>NetworkLatencyMonitor</mainClass>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>
README.md
# Network Latency Monitor (Java)

Continuously probes host latency via TCP connect timing and tracks RTT statistics, jitter, loss, and alerts.

## Requirements
- Ubuntu 22.04
- JDK 17+
- Maven 3.8+

## Dependencies
- `com.google.code.gson:gson:2.11.0` (JSON report generation)

## Build
```bash
mvn -q -DskipTests compile
```

## Run
```bash
mvn -q exec:java -Dexec.args="google.com 1.1.1.1 --interval 5 --port 80 --duration 60s"
```

Using host file and CSV export:
```bash
mvn -q exec:java -Dexec.args="--file hosts.txt --interval 2 --duration 5m --output latency_report.json --export latency_series.csv"
```

Alert threshold:
```bash
mvn -q exec:java -Dexec.args="google.com --alert 3x"
mvn -q exec:java -Dexec.args="google.com --alert 200"
```

## Host file format
```text
8.8.8.8 Google-DNS
1.1.1.1,Cloudflare
example.com
```

## Output
- Live dashboard in terminal
- JSON report (`--output`, default `latency_report.json`)
- Optional CSV time-series export (`--export`)
src/main/java/NetworkLatencyMonitor.java
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

import java.io.BufferedWriter;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Instant;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public final class NetworkLatencyMonitor {
    private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create();
    private static volatile boolean STOP = false;

    private NetworkLatencyMonitor() {}

    private record HostTarget(String host, String label) {}
    private record ProbeResult(boolean success, Double rttMs, String error) {}
    private record AlertConfig(String mode, double value) {}

    private static final class Measurement {
        String timestamp;
        boolean success;
        Double rttMs;
        String error;
        boolean alert;
    }

    private static final class HostState {
        final String host;
        final String label;
        int sent = 0;
        int lost = 0;
        Double lastRtt = null;
        boolean lastAlert = false;
        final List<Double> rtts = new ArrayList<>();
        final List<Double> jitters = new ArrayList<>();
        final List<Measurement> measurements = new ArrayList<>();

        HostState(String host, String label) {
            this.host = host;
            this.label = label;
        }

        Map<String, Object> stats() {
            Map<String, Object> out = new LinkedHashMap<>();
            int n = rtts.size();
            Double avg = n == 0 ? null : rtts.stream().mapToDouble(Double::doubleValue).average().orElse(0.0);
            Double med = n == 0 ? null : median(rtts);
            Double std = n == 0 ? null : stddev(rtts, avg == null ? 0.0 : avg);
            Double jitter = jitters.isEmpty() ? null : jitters.stream().mapToDouble(Double::doubleValue).average().orElse(0.0);
            out.put("sent", sent);
            out.put("lost", lost);
            out.put("success_count", n);
            out.put("min_rtt_ms", n == 0 ? null : rtts.stream().min(Double::compare).orElse(null));
            out.put("max_rtt_ms", n == 0 ? null : rtts.stream().max(Double::compare).orElse(null));
            out.put("avg_rtt_ms", avg);
            out.put("median_rtt_ms", med);
            out.put("stddev_rtt_ms", std);
            out.put("jitter_ms", jitter);
            out.put("packet_loss_pct", sent == 0 ? 0.0 : (lost * 100.0) / sent);
            out.put("last_rtt_ms", lastRtt);
            return out;
        }
    }

    private static final class Config {
        double intervalSec = 5.0;
        int port = 80;
        Long durationMs = null;
        AlertConfig alert = new AlertConfig("multiplier", 3.0);
        String output = "latency_report.json";
        String exportCsv = null;
        String filePath = null;
        int timeoutMs = 5000;
        final List<HostTarget> hosts = new ArrayList<>();
    }

    public static void main(String[] args) {
        try {
            Runtime.getRuntime().addShutdownHook(new Thread(() -> STOP = true));
            Config cfg = parseArgs(args);
            if (cfg.filePath != null) cfg.hosts.addAll(parseHostFile(Path.of(cfg.filePath)));
            if (cfg.hosts.isEmpty()) {
                cfg.hosts.add(new HostTarget("8.8.8.8", "Google DNS"));
                cfg.hosts.add(new HostTarget("1.1.1.1", "Cloudflare DNS"));
                cfg.hosts.add(new HostTarget("example.com", "Example"));
                cfg.hosts.add(new HostTarget("google.com", "Google"));
                cfg.durationMs = 30_000L;
            }

            List<HostState> states = cfg.hosts.stream().map(h -> new HostState(h.host(), h.label())).toList();
            long startedAt = System.currentTimeMillis();
            long endAt = cfg.durationMs == null ? Long.MAX_VALUE : startedAt + cfg.durationMs;
            ExecutorService pool = Executors.newFixedThreadPool(Math.max(1, Math.min(32, states.size())));
            boolean interrupted = false;

            while (!STOP && System.currentTimeMillis() < endAt) {
                long tickStart = System.currentTimeMillis();
                List<Future<ProbeResult>> futures = new ArrayList<>();
                for (HostState st : states) {
                    futures.add(pool.submit(probeTask(st.host, cfg.port, cfg.timeoutMs)));
                }

                for (int i = 0; i < states.size(); i++) {
                    HostState st = states.get(i);
                    ProbeResult result;
                    try {
                        result = futures.get(i).get();
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                        interrupted = true;
                        STOP = true;
                        break;
                    } catch (ExecutionException e) {
                        result = new ProbeResult(false, null, "probe_exception:" + e.getCause().getMessage());
                    }

                    st.sent++;
                    Measurement m = new Measurement();
                    m.timestamp = Instant.now().toString();
                    m.success = result.success;
                    m.rttMs = result.rttMs;
                    m.error = result.error;
                    m.alert = false;

                    if (!result.success) {
                        st.lost++;
                        st.lastAlert = false;
                        st.measurements.add(m);
                        continue;
                    }

                    double rtt = result.rttMs;
                    boolean alert = shouldAlert(st, cfg.alert, rtt);
                    if (st.lastRtt != null) st.jitters.add(Math.abs(rtt - st.lastRtt));
                    st.lastRtt = rtt;
                    st.lastAlert = alert;
                    st.rtts.add(rtt);
                    m.alert = alert;
                    st.measurements.add(m);
                }

                renderDashboard(states, startedAt, endAt == Long.MAX_VALUE ? null : endAt);
                long elapsed = System.currentTimeMillis() - tickStart;
                long sleepMs = Math.max(0L, (long) (cfg.intervalSec * 1000.0) - elapsed);
                Thread.sleep(sleepMs);
            }

            if (STOP) interrupted = true;
            pool.shutdownNow();

            long endedAt = System.currentTimeMillis();
            renderDashboard(states, startedAt, endAt == Long.MAX_VALUE ? null : endAt);
            printFinalSummary(states);
            writeJsonReport(states, cfg, startedAt, endedAt, interrupted);
            if (cfg.exportCsv != null) writeCsv(states, Path.of(cfg.exportCsv));

            System.out.println("\nSaved JSON report: " + cfg.output);
            if (cfg.exportCsv != null) System.out.println("Saved CSV export: " + cfg.exportCsv);
        } catch (Exception ex) {
            System.err.println("Error: " + ex.getMessage());
            System.exit(1);
        }
    }

    private static Config parseArgs(String[] args) throws IOException {
        Config cfg = new Config();
        for (int i = 0; i < args.length; i++) {
            String arg = args[i];
            if (!arg.startsWith("--")) {
                cfg.hosts.add(new HostTarget(arg, arg));
                continue;
            }
            if (i + 1 >= args.length) throw new IllegalArgumentException("Missing value for " + arg);
            String value = args[++i];
            switch (arg) {
                case "--interval" -> cfg.intervalSec = Double.parseDouble(value);
                case "--port" -> cfg.port = Integer.parseInt(value);
                case "--duration" -> cfg.durationMs = parseDurationMs(value);
                case "--alert" -> cfg.alert = parseAlert(value);
                case "--output" -> cfg.output = value;
                case "--export" -> cfg.exportCsv = value;
                case "--file" -> cfg.filePath = value;
                case "--timeout" -> cfg.timeoutMs = Integer.parseInt(value);
                default -> throw new IllegalArgumentException("Unknown option: " + arg);
            }
        }
        if (cfg.intervalSec <= 0) throw new IllegalArgumentException("--interval must be > 0");
        if (cfg.port < 1 || cfg.port > 65535) throw new IllegalArgumentException("--port must be 1..65535");
        if (cfg.timeoutMs <= 0) throw new IllegalArgumentException("--timeout must be > 0");
        return cfg;
    }

    private static long parseDurationMs(String s) {
        String v = s.trim().toLowerCase(Locale.ROOT);
        if (v.endsWith("ms")) return Long.parseLong(v.substring(0, v.length() - 2));
        if (v.endsWith("s")) return Long.parseLong(v.substring(0, v.length() - 1)) * 1_000L;
        if (v.endsWith("m")) return Long.parseLong(v.substring(0, v.length() - 1)) * 60_000L;
        if (v.endsWith("h")) return Long.parseLong(v.substring(0, v.length() - 1)) * 3_600_000L;
        throw new IllegalArgumentException("Invalid --duration value: " + s);
    }

    private static AlertConfig parseAlert(String s) {
        String v = s.trim().toLowerCase(Locale.ROOT);
        if (v.endsWith("x")) return new AlertConfig("multiplier", Double.parseDouble(v.substring(0, v.length() - 1)));
        return new AlertConfig("absolute", Double.parseDouble(v));
    }

    private static List<HostTarget> parseHostFile(Path p) throws IOException {
        List<HostTarget> out = new ArrayList<>();
        for (String raw : Files.readAllLines(p, StandardCharsets.UTF_8)) {
            String line = raw.trim();
            if (line.isEmpty() || line.startsWith("#")) continue;
            if (line.contains(",")) {
                String[] parts = line.split(",", 2);
                String host = parts[0].trim();
                String label = parts[1].trim().isEmpty() ? host : parts[1].trim();
                out.add(new HostTarget(host, label));
            } else {
                String[] parts = line.split("\\s+");
                String host = parts[0];
                String label = parts.length > 1 ? line.substring(host.length()).trim() : host;
                out.add(new HostTarget(host, label));
            }
        }
        return out;
    }

    private static Callable<ProbeResult> probeTask(String host, int port, int timeoutMs) {
        return () -> {
            long start = System.nanoTime();
            try (Socket socket = new Socket()) {
                socket.connect(new InetSocketAddress(host, port), timeoutMs);
                double rttMs = (System.nanoTime() - start) / 1_000_000.0;
                return new ProbeResult(true, rttMs, null);
            } catch (IOException ex) {
                return new ProbeResult(false, null, ex.getClass().getSimpleName() + ":" + ex.getMessage());
            }
        };
    }

    private static boolean shouldAlert(HostState st, AlertConfig alert, double rtt) {
        if ("absolute".equals(alert.mode)) return rtt > alert.value;
        if (st.rtts.isEmpty()) return false;
        double avg = st.rtts.stream().mapToDouble(Double::doubleValue).average().orElse(0.0);
        return rtt > avg * alert.value;
    }

    private static void renderDashboard(List<HostState> states, long startedAt, Long endAt) {
        System.out.print("\033[2J\033[H");
        System.out.println("Network Latency Monitor  " + Instant.now());
        if (endAt == null) {
            long elapsed = (System.currentTimeMillis() - startedAt) / 1000;
            System.out.println("Elapsed: " + elapsed + "s");
        } else {
            long rem = Math.max(0L, (endAt - System.currentTimeMillis()) / 1000);
            System.out.println("Remaining: " + rem + "s");
        }
        System.out.println("--------------------------------------------------------------------------------------");
        System.out.println("Host                      Last RTT   Avg RTT    Min/Max            Loss %   Alert");
        System.out.println("--------------------------------------------------------------------------------------");
        for (HostState st : states) {
            Map<String, Object> s = st.stats();
            String hostCol = (st.label + " (" + st.host + ")");
            if (hostCol.length() > 25) hostCol = hostCol.substring(0, 22) + "...";
            hostCol = String.format("%-25s", hostCol);
            String minMax = String.format("%s/%s", fmtMs((Double) s.get("min_rtt_ms")), fmtMs((Double) s.get("max_rtt_ms")));
            System.out.printf(
                    Locale.US,
                    "%s %-10s %-10s %-18s %6.2f%%   %s%n",
                    hostCol,
                    fmtMs((Double) s.get("last_rtt_ms")),
                    fmtMs((Double) s.get("avg_rtt_ms")),
                    minMax,
                    (Double) s.get("packet_loss_pct"),
                    st.lastAlert ? "ALERT" : "OK"
            );
        }
        System.out.println("--------------------------------------------------------------------------------------");
    }

    private static String fmtMs(Double v) {
        return v == null ? "-" : String.format(Locale.US, "%.2fms", v);
    }

    private static void printFinalSummary(List<HostState> states) {
        List<HostState> ranked = new ArrayList<>(states);
        ranked.sort(Comparator.comparingDouble(st -> {
            Double avg = (Double) st.stats().get("avg_rtt_ms");
            return avg == null ? Double.POSITIVE_INFINITY : avg;
        }));
        System.out.println("\nFinal Summary (best average RTT first)");
        System.out.println("--------------------------------------------------------------------------");
        for (HostState st : ranked) {
            Map<String, Object> s = st.stats();
            System.out.printf(
                    Locale.US,
                    "%s (%s)  avg=%s  min=%s  max=%s  loss=%.2f%%  jitter=%s%n",
                    st.label,
                    st.host,
                    fmtMs((Double) s.get("avg_rtt_ms")),
                    fmtMs((Double) s.get("min_rtt_ms")),
                    fmtMs((Double) s.get("max_rtt_ms")),
                    (Double) s.get("packet_loss_pct"),
                    fmtMs((Double) s.get("jitter_ms"))
            );
        }
    }

    private static void writeJsonReport(List<HostState> states, Config cfg, long startedAt, long endedAt, boolean interrupted)
            throws IOException {
        Map<String, Object> root = new LinkedHashMap<>();
        root.put("generated_at", Instant.now().toString());

        Map<String, Object> monitoring = new LinkedHashMap<>();
        monitoring.put("started_at", Instant.ofEpochMilli(startedAt).toString());
        monitoring.put("ended_at", Instant.ofEpochMilli(endedAt).toString());
        monitoring.put("interrupted", interrupted);
        monitoring.put("interval_sec", cfg.intervalSec);
        monitoring.put("port", cfg.port);
        monitoring.put("timeout_ms", cfg.timeoutMs);
        monitoring.put("duration_ms", cfg.durationMs);
        monitoring.put("alert", Map.of("mode", cfg.alert.mode, "value", cfg.alert.value));
        root.put("monitoring", monitoring);

        List<Map<String, Object>> hosts = new ArrayList<>();
        for (HostState st : states) {
            Map<String, Object> h = new LinkedHashMap<>();
            h.put("host", st.host);
            h.put("label", st.label);
            h.put("stats", st.stats());

            List<Map<String, Object>> ms = new ArrayList<>();
            for (Measurement m : st.measurements) {
                Map<String, Object> row = new LinkedHashMap<>();
                row.put("timestamp", m.timestamp);
                row.put("success", m.success);
                row.put("rtt_ms", m.rttMs);
                row.put("error", m.error);
                row.put("alert", m.alert);
                ms.add(row);
            }
            h.put("measurements", ms);
            hosts.add(h);
        }
        root.put("hosts", hosts);
        Files.writeString(Path.of(cfg.output), GSON.toJson(root), StandardCharsets.UTF_8);
    }

    private static void writeCsv(List<HostState> states, Path out) throws IOException {
        try (BufferedWriter w = Files.newBufferedWriter(out, StandardCharsets.UTF_8)) {
            w.write("timestamp,host,label,success,rtt_ms,error,alert\n");
            for (HostState st : states) {
                for (Measurement m : st.measurements) {
                    w.write(csv(m.timestamp) + "," + csv(st.host) + "," + csv(st.label) + "," + m.success + ","
                            + (m.rttMs == null ? "" : String.format(Locale.US, "%.3f", m.rttMs)) + ","
                            + csv(m.error == null ? "" : m.error) + "," + m.alert + "\n");
                }
            }
        }
    }

    private static String csv(String s) {
        String v = s.replace("\"", "\"\"");
        return "\"" + v + "\"";
    }

    private static double median(List<Double> values) {
        List<Double> sorted = new ArrayList<>(values);
        Collections.sort(sorted);
        int n = sorted.size();
        if (n % 2 == 0) return (sorted.get(n / 2 - 1) + sorted.get(n / 2)) / 2.0;
        return sorted.get(n / 2);
    }

    private static double stddev(List<Double> values, double mean) {
        if (values.size() < 2) return 0.0;
        double sumSq = 0.0;
        for (double v : values) sumSq += (v - mean) * (v - mean);
        return Math.sqrt(sumSq / values.size());
    }
}