> Disclosure: I work on InstaCharts. The `jq` recipes below are tool-agnostic and useful on their own, and I've tried to be straight about where InstaCharts fits and where it doesn't.
You just shipped a deploy and you want one answer: did error rates move? Your logs are right there, structured JSON, one object per event. But getting a chart out of them usually means one of two annoying paths. Either you stand up (or pay for) a full observability stack for a question you'll ask once, or you write a throwaway script to parse the logs and hand them to a charting library, which is thirty minutes of work for a chart nobody looks at again.
There's a faster path for the one-off case, and the part that's actually worth your attention is how you shape the data before it ever hits a chart tool.
Why JSON logs are already halfway there
Structured logs are the easy case for visualization because they're already key-value data. A typical line looks like this:
{"timestamp":"2026-07-28T14:22:31Z","level":"error","status":500,"endpoint":"/api/checkout","latency_ms":812}
InstaCharts reads .json and .jsonl files directly. When you upload one, it turns the JSON into a spreadsheet: each object becomes a row, and each property becomes a column. It then analyzes each column's type and cardinality and instantly renders a recommended chart, which you can switch to any of its 13 chart types (bar, line, area, scatterplot, pie, radar, heatmap, mekko, and a sortable data table, among others).
So in principle the workflow is: drop in the file, get a chart, tweak the axes, export or embed. For clean logs that really is a two-minute job.
The catch is that raw logs are rarely that clean, and that's where a little prep pays off.
The part that matters: shape the logs with jq first
Two things will bite you if you upload raw logs, and both are worth understanding regardless of which chart tool you use.
Volume. Log files are big. Free and lower tiers on hosted chart tools have row limits (InstaCharts' free tier caps rows, so check the current number before you rely on it), and honestly, nobody needs to plot 200,000 individual log lines. You almost always want counts, not raw events.
Dirty columns. InstaCharts documents a specific gotcha: if a column is mostly numbers but has any text in it, the whole column gets treated as text, which quietly breaks charting on that field. A single "latency_ms":"-" in an otherwise numeric column is enough to do it.
jq handles both cleanly. A few recipes I reach for:
Convert newline-delimited logs (JSONL) into a single JSON array, and keep only the fields you care about:
jq -s '[.[] | {ts: .timestamp, level, status, endpoint, latency_ms}]' \
app.log > clean.json
Coerce a field that's sometimes a string so it stays numeric, defaulting bad values to null (which InstaCharts treats as blank, and you can filter out):
jq -s '[.[] | .latency_ms = (.latency_ms | tonumber? // null)]' \
app.log > clean.json
Pre-aggregate so you upload a handful of rows instead of the whole log. Count events per status code:
jq -s 'group_by(.status)
| map({status: .[0].status, count: length})' \
app.log > status_counts.json
Bucket errors by hour for a time series. This slices an ISO 8601 timestamp like 2026-07-28T14:22:31Z down to the hour (2026-07-28T14) and counts:
jq -s '
map(select(.level == "error"))
| group_by(.timestamp[0:13])
| map({hour: .[0].timestamp[0:13], errors: length})
' app.log > errors_per_hour.json
That last one turns a giant log into maybe 24 rows. Upload errors_per_hour.json, and you get a clean errors-over-time line chart in seconds, well under any row limit.
A few log charts worth making
Once the data is shaped, these are the views that tend to answer real questions:
- Errors over time (line or area, from the hourly bucket above). The fastest way to see whether a deploy or incident moved the needle.
-
Status code distribution (bar, from
status_counts.json). A quick read on how healthy traffic is right now. - Slowest endpoints (bar, limited to a top-N view). InstaCharts can limit a chart to the largest or smallest N items, so you don't have to trim the data yourself for this one.
- Latency distribution (histogram-style bar using its automatic numeric binning). Useful for spotting a long tail that an average hides.
You can add trend lines, annotations, and data labels to any of these from the sidebar, which is handy when you're dropping the chart into a postmortem and want to mark where the incident started.
Export it, or keep it live
For a one-off (a postmortem, a Slack update, a blog post), export to SVG or PNG and you're done. SVG stays crisp in docs at any size.
For something recurring, like a weekly reliability summary, the more useful setup is to skip re-uploading entirely. If your aggregation job writes its output to a Google Sheet, InstaCharts can connect to that sheet and refresh on a schedule (on change, or daily, weekly, or monthly), and the embedded chart updates on its own. There's also a Zapier integration if you'd rather push the data from somewhere else in your pipeline. Embed the chart once, and the report keeps itself current.
Where this does not fit
Being clear about the edges, because that's the difference between a recommendation and an ad:
- This is not observability. There's no alerting, no live tailing, no querying across a fleet in real time. For continuous monitoring you want a real stack (Grafana, an ELK/OpenSearch setup, a hosted APM). This is for answering a specific question quickly, or for a lightweight recurring report.
-
You'll almost always pre-aggregate. Raw log volume plus row limits mean the
jqstep isn't optional for anything but small samples. That's fine, since aggregated data makes better charts anyway, but it's a step. -
It's tabular-first. Deeply nested log structures need flattening (again,
jq) before they map cleanly to columns.
The takeaway
The chart tool is the fast, boring part. The leverage is in the ten lines of jq that turn a messy multi-megabyte log into a clean, pre-aggregated file. Do that, and going from "something feels off in the logs" to a shareable chart is a couple of minutes instead of a detour into whatever charting library you half-remember.
Next time you're staring at a log file trying to eyeball a trend, try piping it through jq and dropping the result into a chart instead. Worst case, you've written a reusable aggregation snippet.
Got a favorite jq one-liner for wrangling logs? Drop it in the comments, I collect these.
Top comments (0)