Structured Log Processor (javascript, written by Claude Code)
envgap__claude-code__javascript-t1-50
Written by a coding agent; not on GitHubWritten 2026-02-28
01 / FAILURE SIGNATURE
As the study recorded it
No identifying execution failure has been captured.
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
package.json- Reproduce
Awaiting issue-specific recipe- Run under trace
Awaiting a meaningful runtime command
03 / TASK AND FAILURE
claude-code/javascript-t1 #50 · read the task the agent was given
Claude Code wrote this javascript project from the task below. It installed and ran on a clean Ubuntu 22.04 machine as written. Task given to the agent: TASK: Structured Log Processor Write a program that parses, queries, transforms, and aggregates structured log data in JSON Lines format, supporting filtering, field extraction, statistical aggregation, and output formatting. FUNCTIONAL REQUIREMENTS: - Accept a log file path as a command-line argument (JSON Lines format: one JSON object per line) - Support filtering log entries via --where flag with field comparisons (e.g., --where "level==ERROR" or --where "response_time>500" or --where "status!=200") - Support multiple filters combined with AND logic; support OR logic via --or flag - Support field selection via --fields flag (comma-separated list of field names to include in output) - Support aggregation operations via --group-by and --aggregate flags: count, sum, avg, min, max, and percentile(N) grouped by a specified field (e.g., --group-by status --aggregate "count,avg:response_time") - Support time-based aggregation: group by time windows (--time-window flag: 1m, 5m, 1h, 1d) on a specified timestamp field (--time-field flag) - Support sorting via --sort flag (field name with optional :asc or :desc suffix) - Support limiting output via --limit flag and skipping via --offset flag - Support output in multiple formats via --format flag: json (default), csv, table (formatted console table), and jsonl (JSON Lines) - Compute and display summary statistics for numeric fields: count, min, max, mean, median, p95, p99 - Support extracting unique values of a field via --distinct flag - Print results to console by default - Save results to a file via --output flag - If no input file is given, generate a sample web server access log with 1000 entries containing fields (timestamp, method, path, status, response_time, user_agent, ip), then demonstrate: filtering ERROR entries, computing average response time grouped by HTTP method, finding the top 10 slowest requests, and computing hourly request counts - Handle errors: malformed JSON lines (skip with warning and count), missing fields in filter expressions, type mismatches in comparisons, and very large files Create a complete JavaScript project for a clean Ubuntu 22.04 machine with only Node.js 20+ (LTS) installed. Include: - Source code - package.json 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.
log_processor.js
#!/usr/bin/env node
/**
* Structured Log Processor
* Parses/queries/aggregates JSON Lines logs with filtering, field selection,
* and time-based aggregation.
* Uses fast-json-parse for safe JSON parsing and commander for CLI.
*/
const fs = require('fs');
const readline = require('readline');
const { program } = require('commander');
const FastJsonParse = require('fast-json-parse');
/**
* Get a nested field value from an object using dot notation.
*/
function getNestedField(obj, fieldPath) {
const parts = fieldPath.split('.');
let current = obj;
for (const part of parts) {
if (current == null || typeof current !== 'object') return undefined;
current = current[part];
}
return current;
}
/**
* Parse a filter expression like "field==value" into components.
*/
function parseFilter(expression) {
const operators = ['!=', '>=', '<=', '==', '>', '<', 'contains', 'startswith', 'endswith'];
for (const op of operators) {
const idx = expression.indexOf(op);
if (idx > 0) {
return {
field: expression.substring(0, idx).trim(),
operator: op,
value: expression.substring(idx + op.length).trim()
};
}
}
throw new Error(`Invalid filter expression: ${expression}`);
}
/**
* Check if a record matches a filter condition.
*/
function matchesFilter(record, filter) {
const recordVal = getNestedField(record, filter.field);
if (recordVal === undefined || recordVal === null) return false;
const recordStr = String(recordVal);
const value = filter.value;
switch (filter.operator) {
case '==': return recordStr === value;
case '!=': return recordStr !== value;
case 'contains': return recordStr.toLowerCase().includes(value.toLowerCase());
case 'startswith': return recordStr.startsWith(value);
case 'endswith': return recordStr.endsWith(value);
case '>': {
const a = parseFloat(recordStr), b = parseFloat(value);
return !isNaN(a) && !isNaN(b) ? a > b : recordStr > value;
}
case '>=': {
const a = parseFloat(recordStr), b = parseFloat(value);
return !isNaN(a) && !isNaN(b) ? a >= b : recordStr >= value;
}
case '<': {
const a = parseFloat(recordStr), b = parseFloat(value);
return !isNaN(a) && !isNaN(b) ? a < b : recordStr < value;
}
case '<=': {
const a = parseFloat(recordStr), b = parseFloat(value);
return !isNaN(a) && !isNaN(b) ? a <= b : recordStr <= value;
}
default: return true;
}
}
/**
* Select specific fields from a record.
*/
function selectFields(record, fields) {
const result = {};
for (const f of fields) {
const val = getNestedField(record, f);
if (val !== undefined) result[f] = val;
}
return result;
}
/**
* Read and parse a JSON Lines file, applying filters.
*/
async function readLogFile(filepath, filters = []) {
const records = [];
const fileStream = fs.createReadStream(filepath, { encoding: 'utf-8' });
const rl = readline.createInterface({ input: fileStream, crlfDelay: Infinity });
let lineNum = 0;
let parseErrors = 0;
for await (const line of rl) {
lineNum++;
const trimmed = line.trim();
if (!trimmed) continue;
const parsed = new FastJsonParse(trimmed);
if (parsed.err) {
parseErrors++;
continue;
}
const record = parsed.value;
const parsedFilters = filters.map(f => parseFilter(f));
const allMatch = parsedFilters.every(f => matchesFilter(record, f));
if (allMatch) {
records.push(record);
}
}
return { records, total: lineNum, errors: parseErrors };
}
/**
* Format a date into a time bucket key.
*/
function formatTimeBucket(dateStr, interval) {
const dt = new Date(dateStr);
if (isNaN(dt.getTime())) return null;
const pad = (n) => String(n).padStart(2, '0');
const y = dt.getFullYear(), m = pad(dt.getMonth() + 1), d = pad(dt.getDate());
const h = pad(dt.getHours()), min = pad(dt.getMinutes());
switch (interval) {
case 'minute': return `${y}-${m}-${d} ${h}:${min}`;
case 'hour': return `${y}-${m}-${d} ${h}:00`;
case 'day': return `${y}-${m}-${d}`;
case 'month': return `${y}-${m}`;
default: return `${y}-${m}-${d} ${h}:00`;
}
}
/**
* Count occurrences grouped by a field, sorted descending.
*/
function countBy(records, field) {
const counts = new Map();
for (const rec of records) {
const val = getNestedField(rec, field);
const key = val != null ? String(val) : '<null>';
counts.set(key, (counts.get(key) || 0) + 1);
}
return [...counts.entries()].sort((a, b) => b[1] - a[1]);
}
/**
* Compute numeric statistics for a field.
*/
function computeStats(records, field) {
const values = [];
for (const rec of records) {
const val = getNestedField(rec, field);
if (val != null) {
const num = parseFloat(val);
if (!isNaN(num)) values.push(num);
}
}
if (values.length === 0) return { count: 0, min: 0, max: 0, avg: 0, sum: 0 };
const sum = values.reduce((a, b) => a + b, 0);
return {
count: values.length,
min: Math.min(...values),
max: Math.max(...values),
avg: sum / values.length,
sum: sum
};
}
/**
* Aggregate records into time-based buckets.
*/
function timeSeries(records, timeField, interval) {
const buckets = new Map();
for (const rec of records) {
const val = getNestedField(rec, timeField);
if (!val) continue;
const bucket = formatTimeBucket(String(val), interval);
if (bucket) buckets.set(bucket, (buckets.get(bucket) || 0) + 1);
}
return [...buckets.entries()].sort((a, b) => a[0].localeCompare(b[0]));
}
// CLI setup
program.name('logproc').description('Structured Log Processor - Query and analyze JSON Lines logs').version('1.0.0');
program.command('query')
.description('Query log records with filtering and field selection')
.argument('<logfile>', 'Path to JSON Lines log file')
.option('-f, --filter <expressions...>', 'Filter expressions')
.option('-s, --fields <fields>', 'Comma-separated field list')
.option('-l, --limit <n>', 'Max records to return', parseInt, 0)
.option('--pretty', 'Pretty-print output')
.action(async (logfile, opts) => {
const { records } = await readLogFile(logfile, opts.filter || []);
const fieldList = opts.fields ? opts.fields.split(',').map(f => f.trim()) : null;
const limit = opts.limit > 0 ? opts.limit : records.length;
const slice = records.slice(0, limit);
for (const rec of slice) {
const output = fieldList ? selectFields(rec, fieldList) : rec;
console.log(opts.pretty ? JSON.stringify(output, null, 2) : JSON.stringify(output));
}
console.error(`\n--- ${slice.length}/${records.length} records matched ---`);
});
program.command('count-by')
.description('Count records grouped by a field')
.argument('<logfile>', 'Path to JSON Lines log file')
.argument('<field>', 'Field to group by')
.option('-f, --filter <expressions...>', 'Filter expressions')
.action(async (logfile, field, opts) => {
const { records } = await readLogFile(logfile, opts.filter || []);
const counts = countBy(records, field);
for (const [key, count] of counts) {
console.log(`${key}: ${count}`);
}
});
program.command('stats')
.description('Compute numeric statistics for a field')
.argument('<logfile>', 'Path to JSON Lines log file')
.argument('<field>', 'Numeric field to analyze')
.option('-f, --filter <expressions...>', 'Filter expressions')
.action(async (logfile, field, opts) => {
const { records } = await readLogFile(logfile, opts.filter || []);
const s = computeStats(records, field);
for (const [k, v] of Object.entries(s)) {
console.log(`${k}: ${v}`);
}
});
program.command('timeseries')
.description('Aggregate records by time intervals')
.argument('<logfile>', 'Path to JSON Lines log file')
.argument('<time_field>', 'Time field name')
.option('-f, --filter <expressions...>', 'Filter expressions')
.option('-i, --interval <interval>', 'Time interval (minute|hour|day|month)', 'hour')
.action(async (logfile, timeField, opts) => {
const { records } = await readLogFile(logfile, opts.filter || []);
const ts = timeSeries(records, timeField, opts.interval);
for (const [bucket, count] of ts) {
console.log(`${bucket}: ${count}`);
}
});
program.command('top')
.description('Show top N most frequent values for a field')
.argument('<logfile>', 'Path to JSON Lines log file')
.argument('<field>', 'Field to analyze')
.option('-n, --top <n>', 'Number of top values', parseInt, 10)
.action(async (logfile, field, opts) => {
const { records } = await readLogFile(logfile);
const counts = countBy(records, field);
for (const [key, count] of counts.slice(0, opts.top)) {
console.log(`${key}: ${count}`);
}
});
program.parse();
package.json
{
"name": "structured-log-processor",
"version": "1.0.0",
"description": "Parses/queries/aggregates JSON Lines logs with filtering, field selection, and time-based aggregation",
"main": "log_processor.js",
"bin": {
"logproc": "./log_processor.js"
},
"scripts": {
"start": "node log_processor.js",
"test": "echo \"No tests configured\" && exit 0"
},
"dependencies": {
"fast-json-parse": "^1.0.3",
"commander": "^12.1.0"
},
"keywords": ["log", "json", "parser", "aggregation", "cli"],
"license": "MIT"
}
README.md
# Structured Log Processor (JavaScript - Trial 1)
## Description
Parses, queries, and aggregates JSON Lines log files with support for filtering,
field selection, and time-based aggregation. Uses fast-json-parse for safe JSON
parsing and commander for CLI argument handling.
## Dependencies
- **fast-json-parse**: Fast, safe JSON parsing without try/catch overhead
- **commander**: Full-featured CLI framework with subcommands
## Usage
### Install dependencies
```bash
npm install
```
### Query logs with filters
```bash
node log_processor.js query access.jsonl -f "level==ERROR" -s "timestamp,message" --pretty
```
### Count by field
```bash
node log_processor.js count-by access.jsonl level
```
### Numeric statistics
```bash
node log_processor.js stats access.jsonl response_time
```
### Time series aggregation
```bash
node log_processor.js timeseries access.jsonl timestamp -i hour
```
### Top N values
```bash
node log_processor.js top access.jsonl status_code -n 5
```
## Input Format
Expects JSON Lines format (one JSON object per line):
```json
{"timestamp": "2024-01-15T10:30:00Z", "level": "INFO", "message": "Request processed", "response_time": 42}
```