How the JSON Output Mode in witr Handles Multiple Input Sources
When using the --json flag with multiple inputs, witr aggregates all results into a single JSON array, including both successful process lookups and error entries for failed targets.
The pranshuparmar/witr CLI tool provides a machine-readable JSON output mode designed for scripting and integration workflows. Understanding how this mode behaves with multiple input sources requires examining the core aggregation logic in the application layer and the serialization utilities in the output package.
Single-Target vs. Multi-Target JSON Processing
witr distinguishes between single and multiple input sources to determine output formatting strategy.
Single-Target JSON Output
For a single input source, witr serializes the model.Result struct immediately using dedicated helper functions. The internal/output/json.go file defines multiple serialization formats:
ToJSON– Full process details including ancestry and childrenToShortJSON– Compact format with essential fields onlyToTreeJSON– Hierarchical tree representationToWarningsJSON– Warning-focused outputToEnvJSON– Environment variable inspection
These helpers write directly to standard output without buffering.
Multi-Target JSON Aggregation
When multiple sources are provided, the behavior shifts fundamentally. In internal/app/app.go, witr accumulates results in a slice variable named jsonResults rather than emitting each result immediately.
The aggregation flow follows this pattern:
- Initialize empty
jsonResultsslice - For each input target, perform lookup and serialization
- Append serialized JSON string to
jsonResults - After all targets processed, marshal complete slice as JSON array
- Write final array to standard output in single operation
This design ensures valid, parseable JSON output regardless of how many sources succeed or fail.
Error Handling in Multi-Target JSON Mode
Failed lookups do not terminate execution or corrupt the JSON structure. The jsonErrorEntry function in internal/app/app.go generates standardized error objects:
// Error entry format consistent with successful results
{"error": "descriptive message", "target": "input specification"}
These error entries interleave with successful results in the final array, preserving target order and enabling callers to correlate errors with specific inputs.
JSON Structure and Field Behavior
The underlying data model in internal/model defines the Result struct with omitempty JSON tags. Key characteristics include:
- Consistent schema – All entries share the same top-level structure
- Sparse fields – Empty arrays and zero values are omitted (e.g.,
Childrenfield absent when no child processes exist) - Deterministic ordering – Array preserves input target sequence
Practical Usage Examples
Single Process Inspection
witr --json pid 1234
Output:
{"PID":1234,"Command":"nginx","Ancestry":[{"PID":1,"Command":"systemd"}],"Children":[]}
Multiple Port Inspection with Mixed Results
witr --json port 8080 port 5432 port 9999
Output:
[
{"Port":8080,"PID":5678,"Command":"node","Ancestry":[...]},
{"Port":5432,"PID":9012,"Command":"postgres","Ancestry":[...]},
{"error":"no process listening on port 9999","target":"port 9999"}
]
Programmatic Processing with jq
witr --json pid $(pgrep -d',' -f "python") | jq '.[] | select(.Command | contains("worker"))'
Source Code Reference
| File | Purpose | GitHub URL |
|---|---|---|
internal/app/app.go |
CLI entry point, --json flag handling, jsonResults aggregation, jsonErrorEntry |
https://github.com/pranshuparmar/witr/blob/main/internal/app/app.go |
internal/output/json.go |
JSON serialization helpers: ToJSON, ToShortJSON, ToTreeJSON, ToWarningsJSON, ToEnvJSON |
https://github.com/pranshuparmar/witr/blob/main/internal/output/json.go |
internal/app/render_test.go |
Test coverage for JSON output formatting and multi-target accumulation | https://github.com/pranshuparmar/witr/blob/main/internal/app/render_test.go |
internal/app/collect_test.go |
Verification of error entry generation and inclusion in JSON arrays | https://github.com/pranshuparmar/witr/blob/main/internal/app/collect_test.go |
internal/model/result.go |
Result struct definition with JSON tags |
https://github.com/pranshuparmar/witr/blob/main/internal/model/result.go |
Performance Characteristics
The multi-target JSON mode maintains O(n) memory complexity relative to input count, as all results are buffered in jsonResults before final output. For extremely large target lists (thousands of PIDs or ports), consider:
- Piping through
witrin batches - Using
--shortflag to reduce per-entry serialization overhead - Filtering targets at the shell level before passing to witr
Summary
- Single targets serialize immediately via
internal/output/json.gohelpers - Multiple targets aggregate in
jsonResultsslice and emit as unified JSON array - Errors return as structured objects via
jsonErrorEntry, maintaining parseable output omitemptytags produce compact JSON by excluding empty fields- Source order preservation enables reliable result-to-input correlation
Frequently Asked Questions
What happens if one target fails when using JSON mode with multiple inputs?
witr continues processing remaining targets. The failure is recorded as a JSON error entry in the final array using jsonErrorEntry, allowing the complete output to parse successfully while indicating which specific input failed.
Does the JSON output mode support streaming for large target lists?
No. As implemented in internal/app/app.go, witr buffers all jsonResults in memory before the final write. The output is emitted as a single valid JSON array after all targets complete, trading memory usage for guaranteed structural validity.
How do I distinguish between different result types in the JSON array?
Each object contains identifying fields based on lookup type: PID for process ID lookups, Port for network port inspections, Label for container labels, or error for failed operations. The presence of these fields serves as implicit type discrimination.
Can I combine JSON output with other formatting flags like --short or --tree?
Yes. The --json flag accepts modifier flags that route to specific serializers: --short uses ToShortJSON, and --tree uses ToTreeJSON. These produce different JSON schemas optimized for specific use cases while maintaining the same multi-target aggregation behavior.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →