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.7 KiB
Compare the log patterns of two time windows and return what changed: templates that are new, templates whose rate shifted (with magnitude, e.g. 4x), and templates that are gone. This is the single most useful call for incident triage — "what is different about now vs. before it broke" in one round trip, instead of mining two windows yourself and hand-matching templates.
All parameters must be nested inside a query object, with an optional sibling baselineDateRange.
When to use
- Incident triage (the primary loop): set
query.dateRangeto the incident window. OmitbaselineDateRange(defaults to the same window one week earlier) or set it to a known-good window just before the incident. Thenewand biggestrate_shiftentries are your suspects; pivot to their raw lines withquery-logs. - Explain a spike: when a count or sparkline shows a spike (e.g. from
logs-count-ranges), setquery.dateRangeto the spike window andbaselineDateRangeto the window just before it — the topnewandrate_shiftentries are the explanation. This is one call; do not mine the two windows separately and diff them yourself. - Post-deploy check: current window = since the deploy;
baselineDateRange= the same-length window just before it. New error-severity templates right after a deploy are the classic regression signature. - "What changed this week?" — current =
-1d, baseline auto. Good periodic sweep for a service you own.
Pick the right tool
- Just summarize one window's content →
logs-patterns. - Explain a count change without needing message content →
logs-count-ranges(cheaper). - Use this tool when you need to know which messages are new or behaving differently between two periods.
Reading the response
Entries come sorted most-interesting-first: new (by volume), then rate_shift (by magnitude), then gone, then unchanged.
classification— trust the labels; the thresholds already handle sampling honesty:newrequires clearing a novelty floor (~1% volume share, or any error/fatal lines). Below the floor, absence from the baseline sample is not evidence of novelty.rate_shiftrequires ≥2x change in per-second rate (windows of different lengths are normalized) and enough raw samples on both sides.unchangedmeans "no confident claim", not "provably identical".
rate_ratio— current rate / baseline rate. 4.0 = 4x faster now, 0.25 = quartered.pattern— full pattern stats includingmatch_regex/match_literal, so you can pivot straight to the matching lines (same recipe aslogs-patterns: message regex filter + the pattern's services/severities).- Check
baseline.total_countbefore trusting a wall ofnewentries — an empty or tiny baseline (logging only started recently, service didn't exist last week) makes everything look new. - Both windows are mined from samples (
sampled: true), so counts are estimates and templates rarer than ~1 in 10,000 rows may be invisible in either window.
Template identity across the two windows is fingerprint-based (literal content), so the miner rendering User <*> not found in one window and User <num> not found in the other still compares as one pattern rather than a false new+gone pair.
Parameters
query.dateRange
The current (foreground) window — the period you are investigating. Same format as logs-patterns.
baselineDateRange
Optional, sibling of query (not inside it). The comparison window. Omit for the default: the current window shifted back exactly one week, which absorbs daily/weekly volume cycles. Provide explicitly to compare against a pre-deploy or pre-incident period. It does not need to be the same length as the current window — rates are normalized per second.
query.severityLevels / query.serviceNames / query.searchTerm / query.filterGroup
Same as logs-patterns; applied to both windows. Scoping by service is recommended — it prunes both scans and focuses the sample budget.
Examples
What changed during the incident vs. just before it
{
"query": {
"dateRange": { "date_from": "2026-07-07T09:30:00Z", "date_to": "2026-07-07T10:30:00Z" },
"serviceNames": ["checkout"]
},
"baselineDateRange": { "date_from": "2026-07-07T08:00:00Z", "date_to": "2026-07-07T09:00:00Z" }
}
What is new or different today vs. the same time last week
{ "query": { "dateRange": { "date_from": "-1d" } } }
Did the deploy change what the service logs?
{
"query": {
"dateRange": { "date_from": "2026-07-07T14:00:00Z" },
"serviceNames": ["api"]
},
"baselineDateRange": { "date_from": "2026-07-07T10:00:00Z", "date_to": "2026-07-07T14:00:00Z" }
}