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):
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;