{
  "name": "hard-regex-log-parser",
  "description": "Parse complex log files and extract structured data",
  "dataset": "terminal-bench-local",
  "difficulty": "hard",
  "instruction": "There is a file called server.log in the current directory containing web server access logs. Write a Python script called analyze_logs.py that:\n\n1. Parses each log line (Apache combined log format)\n2. Finds the top 3 most requested paths\n3. Finds the IP address with the most requests\n4. Calculates the total bytes transferred\n5. Finds the number of requests that returned 5xx status codes\n\nWrite the results to results.json as a JSON object with keys: top_paths (list of [path, count] pairs), top_ip (string), total_bytes (int), error_5xx_count (int).",
  "setup_files": {
    "server.log": "192.168.1.1 - frank [10/Oct/2023:13:55:36 -0700] \"GET /index.html HTTP/1.1\" 200 2326 \"http://www.example.com/\" \"Mozilla/5.0\"\n192.168.1.2 - - [10/Oct/2023:13:56:01 -0700] \"GET /api/users HTTP/1.1\" 200 1543 \"-\" \"curl/7.68.0\"\n192.168.1.1 - frank [10/Oct/2023:13:56:12 -0700] \"POST /api/login HTTP/1.1\" 302 0 \"http://www.example.com/login\" \"Mozilla/5.0\"\n10.0.0.5 - - [10/Oct/2023:13:56:20 -0700] \"GET /index.html HTTP/1.1\" 200 2326 \"-\" \"Googlebot/2.1\"\n192.168.1.3 - alice [10/Oct/2023:13:56:45 -0700] \"GET /api/users HTTP/1.1\" 200 1543 \"-\" \"Mozilla/5.0\"\n192.168.1.1 - frank [10/Oct/2023:13:57:01 -0700] \"GET /dashboard HTTP/1.1\" 200 8542 \"http://www.example.com/\" \"Mozilla/5.0\"\n10.0.0.5 - - [10/Oct/2023:13:57:15 -0700] \"GET /api/users HTTP/1.1\" 500 234 \"-\" \"Googlebot/2.1\"\n192.168.1.2 - - [10/Oct/2023:13:57:30 -0700] \"GET /index.html HTTP/1.1\" 200 2326 \"-\" \"curl/7.68.0\"\n192.168.1.1 - frank [10/Oct/2023:13:58:00 -0700] \"DELETE /api/users/42 HTTP/1.1\" 503 128 \"-\" \"Mozilla/5.0\"\n10.0.0.5 - - [10/Oct/2023:13:58:20 -0700] \"GET /robots.txt HTTP/1.1\" 200 45 \"-\" \"Googlebot/2.1\"\n192.168.1.3 - alice [10/Oct/2023:13:58:45 -0700] \"GET /index.html HTTP/1.1\" 200 2326 \"-\" \"Mozilla/5.0\"\n192.168.1.1 - frank [10/Oct/2023:13:59:00 -0700] \"GET /api/users HTTP/1.1\" 200 1543 \"-\" \"Mozilla/5.0\"\n",
    "test_analyze_logs.py": "import json\nimport sys\n\ntry:\n    with open('results.json') as f:\n        results = json.load(f)\nexcept Exception as e:\n    print(f\"ERROR: Cannot read results.json: {e}\")\n    sys.exit(1)\n\npassed = 0\nfailed = 0\n\ndef check(desc, got, expected):\n    global passed, failed\n    if got == expected:\n        passed += 1\n    else:\n        print(f\"FAIL {desc}: got {got}, expected {expected}\")\n        failed += 1\n\n# Top 3 paths: /api/users (4), /index.html (4), /dashboard (1) or /api/login or others\ntop_paths = results.get('top_paths', [])\ntop_path_names = [p[0] for p in top_paths[:3]]\n# /api/users appears 4 times, /index.html appears 4 times\ncheck('top path 1 count', top_paths[0][1], 4)\ncheck('top path 2 count', top_paths[1][1], 4)\ncheck('has /api/users in top 2', '/api/users' in top_path_names[:2], True)\ncheck('has /index.html in top 2', '/index.html' in top_path_names[:2], True)\n\n# Top IP: 192.168.1.1 with 5 requests\ncheck('top_ip', results.get('top_ip'), '192.168.1.1')\n\n# Total bytes: 2326+1543+0+2326+1543+8542+234+2326+128+45+2326+1543 = 22882\ncheck('total_bytes', results.get('total_bytes'), 22882)\n\n# 5xx errors: line 7 (500) and line 9 (503) = 2\ncheck('error_5xx_count', results.get('error_5xx_count'), 2)\n\nprint(f\"{passed}/{passed+failed} tests passed\")\nif failed > 0:\n    sys.exit(1)\n"
  },
  "verify": "cd $BENCH_WORK_DIR && python3 analyze_logs.py && python3 test_analyze_logs.py",
  "timeout": 240000,
  "tags": ["hard", "parsing", "regex", "data-processing", "python"]
}
