The tree was a debranded snapshot of upstream taken around 2026-02-20, and upstream has moved about five months since. This replaces it with upstream master, mechanically re-derived: bin/debrand does the rename, and the pipeline and squashed migrations are restored from the shipped tree. Not a merge. The previous import replaced the tree wholesale in a single commit, so there is no shared content history to merge against. Reproducing the rename on the new tree is what actually converges. What survives: .hanzo/workflows, the Dockerfiles and bin/docker* entrypoints, compose files, LLM.md, and insights/migrations. The squashed initial migration is load-bearing — the live database records it by name, so upstream's ~918 individual migrations cannot come back. Nine products are dropped as unreferenced. Twenty-one more that we do not run are kept anyway because the core app still imports them; unpicking those is a refactor and does not belong in a rebase. This base does not build yet, and the reasons are not in the rename: upstream now wants Python 3.13.13 against our 3.12.12 and Django 5.2 against our 4.2, and the dependency manifests still name upstream packages that we republish under our own names. Those are decisions, not renames.
4.5 KiB
Mine recurring log templates ("patterns") from the logs matching a filter set, ordered by frequency. Each pattern is a message template with the variable parts masked — e.g. Connected to <ip> in <num>ms — plus occurrence estimates, severity mix, the services it appears in, and a ready-made predicate for fetching its matching lines.
This is the fastest way to understand what a log stream is saying without reading raw rows: one call summarizes millions of lines into at most 200 templates.
All parameters must be nested inside a query object.
When to use
- To triage an unfamiliar or noisy stream: mine the last hour, scan the top templates by
estimated_count, and look for anything with a non-zero error share inseverity_counts. - To find what's new or dominant during an incident window: mine with
severityLevels: ["error", "fatal"]and adateRangecovering the incident. - As the entry point of a drill-down loop: mine → pick a suspicious pattern → filter logs to exactly its lines using
match_regex(see below) → read the raw rows withquery-logs. - To quantify repetition before proposing log sampling or cleanup:
volume_share_pcttells you how much of the stream one template accounts for.
Pick the right tool
- Raw log lines matching a filter →
query-logs. - A single number (how many logs match) →
logs-count; per-time-bucket counts →logs-count-ranges. - Distribution of a dimension (which services emit the errors?) →
logs-facet-values-create. - Use this tool to summarize message content — what distinct things the logs say and how often.
Reading the response
pattern— the mined template. Masked tokens:<uuid>,<ip>,<hex>,<num>, and<*>for any word position that varied.estimated_count/estimated_error_count— occurrences extrapolated to the full window. Whensampledis false these are exact.severity_counts— sampled occurrences per severity. A template split acrossinfoanderroroften means the same code path logging both outcomes.services— up to 4 service names the pattern was seen in.match_regex— a regex over raw log bodies that matches this pattern's lines, pre-validated against the pattern's own sampled lines. Null when no trustworthy regex could be compiled.match_literal— longest literal run of the template, a plain-text fallback whenmatch_regexis null.
Mining samples the window (sampled: true when it did): counts are estimates, and rare patterns (below roughly 1 in scanned_count of the volume) may be missing entirely. Narrow the dateRange or filters to mine a finer-grained sample.
Pivoting to a pattern's raw lines
To fetch the lines behind a pattern, call query-logs with a message filter in filterGroup:
- If
match_regexis set:{ "key": "message", "value": "<match_regex>", "operator": "regex", "type": "log" } - Else if
match_literalis set:{ "key": "message", "value": "<match_literal>", "operator": "icontains", "type": "log" }
Also pass the pattern's services as serviceNames and (when every entry is one of trace/debug/info/warn/error/fatal) the keys of severity_counts as severityLevels — both make the query dramatically cheaper.
Parameters
query.dateRange
Date range to mine. Defaults to the last hour (-1h).
date_from: ISO 8601 timestamp or relative format:-1h,-6h,-1d,-7d.date_to: Same format. Omit or null for "now".
query.severityLevels
Mine only these severities: trace, debug, info, warn, error, fatal. Omit to include all levels.
query.serviceNames
Restrict mining to these services. Recommended once you know the target service — it prunes the scan and spends the whole sample budget on one service's templates.
query.searchTerm
Full-text search over log bodies applied before mining. Useful to mine only the sub-stream around a keyword (e.g. timeout).
query.filterGroup
Property filters applied before mining. Same format as query-logs filters.
Examples
What is this stream saying? (last hour, everything)
{ "query": { "dateRange": { "date_from": "-1h" } } }
Dominant error templates during an incident
{
"query": {
"severityLevels": ["error", "fatal"],
"dateRange": { "date_from": "2024-01-15T09:00:00Z", "date_to": "2024-01-15T11:00:00Z" }
}
}
Mine one service's logs around a keyword
{
"query": {
"serviceNames": ["checkout"],
"searchTerm": "payment",
"dateRange": { "date_from": "-6h" }
}
}