Skip to content

Examples

Real examples using Duck Hunt with sample files and actual query results.

pytest (JSON)

Sample: test/samples/pytest.json

{
  "tests": [
    {"nodeid": "tests/test_auth.py::test_login_success", "outcome": "passed", "duration": 0.123},
    {"nodeid": "tests/test_auth.py::test_login_invalid_password", "outcome": "failed", "duration": 0.456},
    {"nodeid": "tests/test_api.py::test_create_user", "outcome": "passed", "duration": 0.089}
  ]
}

Query:

SELECT test_name, status, execution_time
FROM read_duck_hunt_log('test/samples/pytest.json', 'pytest_json');

Result: | test_name | status | execution_time | |-----------------------------|--------|---------------:| | test_login_success | PASS | 0.123 | | test_login_invalid_password | FAIL | 0.456 | | test_create_user | PASS | 0.089 |


ESLint (JSON)

Sample: test/samples/eslint.json

[{
  "filePath": "/src/components/Button.tsx",
  "messages": [
    {"ruleId": "no-unused-vars", "severity": 2, "message": "'useState' is defined but never used.", "line": 1, "column": 10},
    {"ruleId": "prefer-const", "severity": 1, "message": "'count' is never reassigned. Use 'const' instead.", "line": 5, "column": 7}
  ]
}]

Query:

SELECT ref_file, ref_line, error_code, message
FROM read_duck_hunt_log('test/samples/eslint.json', 'eslint_json');

Result: | ref_file | ref_line | error_code | message | |----------------------------|------------:|------------------------------------|---------------------------------------------------| | /src/components/Button.tsx | 1 | no-unused-vars | 'useState' is defined but never used. | | /src/components/Button.tsx | 5 | prefer-const | 'count' is never reassigned. Use 'const' instead. | | /src/utils/helpers.ts | 12 | @typescript-eslint/no-explicit-any | Unexpected any. Specify a different type. |


GNU Make

Sample: test/samples/make.out

gcc -Wall -Wextra -g -c src/main.c -o build/main.o
src/main.c:15:5: error: 'undefined_var' undeclared (first use in this function)
src/main.c:28:12: warning: unused variable 'temp' [-Wunused-variable]
src/utils.c:8:9: error: assignment to expression with array type
make: *** [Makefile:23: build/main.o] Error 1

Query:

SELECT ref_file, ref_line, severity, message
FROM read_duck_hunt_log('test/samples/make.out', 'make_error');

Result: | ref_file | ref_line | severity | message | |-------------|------------:|----------|----------------------------------------------------------------------------------| | src/main.c | 15 | error | 'undefined_var' undeclared (first use in this function) | | src/main.c | 15 | info | each undeclared identifier is reported only once for each function it appears in | | src/main.c | 28 | warning | unused variable 'temp' [-Wunused-variable] | | src/utils.c | 8 | error | assignment to expression with array type | | Makefile | NULL | error | make: *** [Makefile:23: build/main.o] Error 1 |


MyPy

Sample: test/samples/mypy.txt

src/api/routes.py:23: error: Argument 1 to "process" has incompatible type "str"; expected "int"  [arg-type]
src/api/routes.py:45: error: "None" has no attribute "items"  [union-attr]
src/models/user.py:12: warning: Unused "type: ignore" comment  [unused-ignore]
src/utils/helpers.py:8: error: Missing return statement  [return]

Query:

SELECT ref_file, ref_line, error_code, message
FROM read_duck_hunt_log('test/samples/mypy.txt', 'mypy_text');

Result: | ref_file | ref_line | error_code | message | |----------------------|------------:|---------------|---------------------------------------------------------------------| | src/api/routes.py | 23 | arg-type | Argument 1 to "process" has incompatible type "str"; expected "int" | | src/api/routes.py | 45 | union-attr | "None" has no attribute "items" | | src/models/user.py | 12 | unused-ignore | Unused "type: ignore" comment | | src/utils/helpers.py | 8 | return | Missing return statement |


Go Test (JSON)

Sample: test/samples/gotest.json

{"Time":"2024-01-15T10:30:00.123Z","Action":"pass","Package":"github.com/example/app","Test":"TestUserCreate","Elapsed":0.333}
{"Time":"2024-01-15T10:30:01.200Z","Action":"fail","Package":"github.com/example/app","Test":"TestUserDelete","Elapsed":0.7}
{"Time":"2024-01-15T10:30:01.500Z","Action":"skip","Package":"github.com/example/app","Test":"TestUserUpdate","Elapsed":0.2}

Query:

SELECT test_name, status, execution_time
FROM read_duck_hunt_log('test/samples/gotest.json', 'gotest_json');

Result: | test_name | status | execution_time | |----------------|--------|---------------:| | TestUserCreate | PASS | 0.333 | | TestUserDelete | FAIL | 0.7 | | TestUserUpdate | SKIP | 0.2 |


GitHub Actions

Sample: test/samples/github_actions.log

2024-01-15T10:00:00.000Z ##[group]Run actions/checkout@v4
2024-01-15T10:00:01.000Z Syncing repository: owner/repo
2024-01-15T10:00:05.000Z ##[endgroup]
2024-01-15T10:00:06.000Z ##[group]Run npm install
2024-01-15T10:00:07.000Z npm WARN deprecated package@1.0.0: This package is deprecated
2024-01-15T10:00:21.000Z ##[error]Process completed with exit code 1.

Query:

SELECT unit, message, severity
FROM read_duck_hunt_workflow_log('test/samples/github_actions.log', 'github_actions')
WHERE length(message) > 0
LIMIT 5;

Result: | unit | message | severity | |-------------------------|----------------------------------------------------------------------------------------|----------| | Run actions/checkout@v4 | 2024-01-15T10:00:00.000Z ##[group]Run actions/checkout@v4 | info | | Run actions/checkout@v4 | 2024-01-15T10:00:01.000Z Syncing repository: owner/repo | info | | Run actions/checkout@v4 | 2024-01-15T10:00:05.000Z ##[endgroup] | info | | Run npm install | 2024-01-15T10:00:06.000Z ##[group]Run npm install | info | | Run npm install | 2024-01-15T10:00:07.000Z npm WARN deprecated package@1.0.0: This package is deprecated | warning |


Status Badges

Query:

SELECT status_badge(status) as badge, tool_name, ref_file, message
FROM read_duck_hunt_log('test/samples/make.out', 'make_error')
WHERE ref_file NOT LIKE '%Makefile%';

Result: | badge | tool_name | ref_file | message | |--------|-----------|-------------|----------------------------------------------------------------------------------| | [FAIL] | make | src/main.c | 'undefined_var' undeclared (first use in this function) | | [ ?? ] | make | src/main.c | each undeclared identifier is reported only once for each function it appears in | | [WARN] | make | src/main.c | unused variable 'temp' [-Wunused-variable] | | [FAIL] | make | src/utils.c | assignment to expression with array type |


Aggregation

Query:

SELECT tool_name,
       COUNT(*) as total,
       COUNT(*) FILTER (WHERE status = 'ERROR') as errors,
       COUNT(*) FILTER (WHERE status = 'WARNING') as warnings
FROM read_duck_hunt_log('test/samples/make.out', 'make_error')
GROUP BY tool_name;

Result: | tool_name | total | errors | warnings | |-----------|------:|-------:|---------:| | make | 5 | 3 | 1 |


Dynamic Regexp Parser

Input (inline):

ERROR: Connection failed
WARNING: Retrying in 5s
ERROR: Max retries exceeded
INFO: Shutting down

Query:

SELECT severity, message
FROM parse_duck_hunt_log(
  'ERROR: Connection failed
   WARNING: Retrying in 5s
   ERROR: Max retries exceeded
   INFO: Shutting down',
  'regexp:(?P<severity>ERROR|WARNING|INFO):\s+(?P<message>.+)'
);

Result: | severity | message | |----------|----------------------| | error | Connection failed | | warning | Retrying in 5s | | error | Max retries exceeded | | info | Shutting down |


Context Extraction

View surrounding log lines for each event to understand the context of errors.

Input:

Starting build process
Compiling main.c
src/main.c:15:5: error: 'ptr' undeclared
Compilation failed
Build terminated

Query:

SELECT
    ref_file,
    message,
    context
FROM parse_duck_hunt_log(
  'Starting build process
Compiling main.c
src/main.c:15:5: error: ''ptr'' undeclared
Compilation failed
Build terminated',
  'make_error',
  context := 2
);

Result: | ref_file | message | context | |----------|---------|---------| | src/main.c | 'ptr' undeclared | [{line_number: 1, content: Starting build process, is_event: false}, {line_number: 2, content: Compiling main.c, is_event: false}, {line_number: 3, content: src/main.c:15:5: error: 'ptr' undeclared, is_event: true}, {line_number: 4, content: Compilation failed, is_event: false}, {line_number: 5, content: Build terminated, is_event: false}] |

Accessing Context Data

-- Get line numbers of context
SELECT context[1].line_number, context[1].content
FROM parse_duck_hunt_log(log_text, 'make_error', context := 2);

-- Find just the event lines within context
SELECT list_filter(context, x -> x.is_event) as event_only
FROM read_duck_hunt_log('build.log', context := 3);

-- Count how many lines of context we got
SELECT len(context) as context_size
FROM read_duck_hunt_log('build.log', context := 5);

Pipeline Integration

Bash: Real-time Analysis

# Parse build output and show errors
make 2>&1 | duckdb -markdown -s "
  SELECT status_badge(status) as badge, ref_file, ref_line, message
  FROM read_duck_hunt_log('/dev/stdin', 'auto')
  WHERE status = 'ERROR'
"

# JSON output for CI
./build.sh 2>&1 | duckdb -json -s "
  SELECT tool_name, COUNT(*) as errors
  FROM read_duck_hunt_log('/dev/stdin', 'auto')
  WHERE status = 'ERROR'
  GROUP BY tool_name
"

Quality Gate

-- Fail if error count exceeds threshold
SELECT CASE
  WHEN COUNT(*) FILTER (WHERE status = 'ERROR') > 5 THEN 'FAIL'
  WHEN COUNT(*) FILTER (WHERE status = 'WARNING') > 20 THEN 'WARN'
  ELSE 'PASS'
END as gate_status
FROM read_duck_hunt_log('build.log', 'auto');

Error Pattern Analysis

Duck Hunt automatically clusters similar errors together, making it easy to understand the types of issues in a build rather than scrolling through hundreds of individual errors.

Sample: test/samples/large_build.out - A build log with 24 issues across 4 source files.

Cluster by Pattern

Query:

SELECT
    pattern_id,
    COUNT(*) as occurrences,
    ANY_VALUE(message) as example_message
FROM read_duck_hunt_log('test/samples/large_build.out', 'make_error')
GROUP BY pattern_id
ORDER BY occurrences DESC;

Result: | pattern_id | occurrences | example_message | |-----------:|------------:|-----------------| | 1 | 8 | 'ptr' undeclared (first use in this function) | | 3 | 8 | unused variable 'temp' [-Wunused-variable] | | 2 | 4 | each undeclared identifier is reported only once for each function it appears in | | 4 | 3 | undefined reference to 'initialize_system' | | 5 | 1 | make: *** [Makefile:45: build/app] Error 1 |

Instead of 24 individual errors, you see 5 distinct issue types. The 8 "undeclared" errors across different files (ptr, counter, buffer, data, handle, response, token, node) all cluster together because the fingerprinting normalizes out the variable names.

Aggregate by Pattern

Query:

SELECT
    pattern_id,
    COUNT(*) as total,
    ANY_VALUE(fingerprint) as fingerprint_sample
FROM read_duck_hunt_log('test/samples/large_build.out', 'make_error')
GROUP BY pattern_id;

Result: | pattern_id | total | fingerprint_sample | |-----------:|------:|-------------------| | 1 | 8 | abc123... | | 2 | 4 | def456... |

Cross-Run Analysis

Compare errors across multiple CI runs to identify recurring issues:

-- Find patterns that appear in multiple build logs
SELECT
    fingerprint,
    COUNT(DISTINCT log_file) as runs_affected,
    COUNT(*) as total_occurrences,
    ANY_VALUE(message) as example
FROM read_duck_hunt_log('logs/build-*.log', 'auto')
GROUP BY fingerprint
HAVING COUNT(DISTINCT log_file) > 1
ORDER BY runs_affected DESC;