Work with exported data¶
export turns a capture into flat rows that any tool can read. This page shows what to do with them in jq and in a
spreadsheet. The same rows load into any tool that reads NDJSON or CSV.
wiretrail checkout.har export | jq -s 'map(select(.duration_ms > 1000)) | map({id, host, norm_path, duration_ms})'
[
{
"id": "e000009",
"host": "cdn.shop.example",
"norm_path": "/img/hero.webp",
"duration_ms": 1921.0
},
{
"id": "e000032",
"host": "api.shop.example",
"norm_path": "/v1/products/{id}/price",
"duration_ms": 1009.0
},
{
"id": "e000059",
"host": "cdn.shop.example",
"norm_path": "/img/banner.webp",
"duration_ms": 1900.0
},
{
"id": "e000090",
"host": "api.shop.example",
"norm_path": "/v1/orders",
"duration_ms": 2409.0
},
{
"id": "e000092",
"host": "api.shop.example",
"norm_path": "/v1/uploads/avatar",
"duration_ms": 1104.0
}
]
jq -s reads the whole stream as one array. That is how a group-by or a sort works across entries.
Count by status¶
wiretrail checkout.har export | jq -s 'group_by(.status) | map({status: .[0].status, requests: length}) | sort_by(-.requests) | .[0:4]'
[
{
"status": 200,
"requests": 72
},
{
"status": 302,
"requests": 6
},
{
"status": 401,
"requests": 4
},
{
"status": 429,
"requests": 4
}
]
Bytes by host¶
wiretrail checkout.har export | jq -s 'group_by(.host) | map({host: .[0].host, bytes: (map(.bytes) | add)}) | sort_by(-.bytes) | .[0:3]'
[
{
"host": "cdn.shop.example",
"bytes": 3015095
},
{
"host": "api.shop.example",
"bytes": 61335
},
{
"host": "shop.example",
"bytes": 19840
}
]
Nearly all of the bytes (97%) come from the CDN, and most of those from one 2.1 MiB image. bytes is the response size, so this is download weight and
not upload.
Median and maximum for one host¶
wiretrail checkout.har export --filter "host:api.shop.example" | jq -s 'map(.duration_ms) | sort | {requests: length, median: .[length / 2 | floor], max: .[-1]}'
jq has no percentile function, so this indexes a sorted list. The hosts command prints p50 and p95 itself, with
a method that is documented on Hosts, subsystems and endpoints.
A spreadsheet¶
--format csv opens in any spreadsheet. Filter first, so the sheet holds only what you need:
id,offset_ms,duration_ms,method,host,norm_path,status,bytes,content_type,resource_type,correlation
e000086,63800,887,OPTIONS,payments.shop.example,/v1/charges,204,0,,other,
e000087,64000,201,POST,payments.shop.example,/v1/charges,402,70,application/json,api,req_e4cd9f9aab
e000088,66500,245,POST,payments.shop.example,/v1/charges,200,34,application/json,api,req_466e1b8780
The file ends with an empty line, which some tools read as a row with no fields. Remove it if yours does: wiretrail capture.har export --format csv | sed '$d'.
Join with other data¶
id, offset_ms and correlation are the keys. A correlation id joins a request to the server's own log line, and offset_ms
plus the capture start time in capture.start_ms gives a wall-clock time to join with other timelines. capture.start_ms is in the
envelope of any command's --json output.
What is not in the rows¶
export leaves out headers, cookies, bodies and query values. It also leaves out the status text, the HTTP version, the
server address and the timing phases. For those, use show-entry on one entry, or the JSON of
slowest.
Related¶
- Export lists the fields.
- JSON output for the richer per-command results.
- Handle captures safely for what is safe to put in a shared sheet.