A log analyzer for files that don't fit in memory, with filters that nest.
A from-scratch reimplementation of TextAnalysisTool.NET.
Download · Install · Filters · Reading the log · Fields · Files · Settings · Keyboard
Download Cascade.exe and run it. One file, no installer, and it keeps itself up to date.
curl.exe -fL --remove-on-error -o Cascade.exe https://github.com/rahul-ramadas/Cascade/releases/latest/download/Cascade.exeYou need Windows and the .NET 10 Desktop Runtime. Cascade draws with GDI rather than a GPU, so it stays quick over a remote desktop.
A filter matches a substring, a .NET regular expression, or marked by marker 1–8, and it either includes lines or excludes them. A line is shown when an enabled include matches it and no enabled exclude does. Colour, background, bold, italic and underline are each on, off, or inherited.
A child narrows its parent: [ERROR] → payment-svc means errors, in payments only. Nest with Alt+→, or by dragging.
- A child matches a line only if every one of its ancestors matches it too — whether or not those ancestors are switched on. So switching a parent off does not stop it narrowing its children: you can scope a branch without showing everything the parent matches.
- An enabled filter nested under an exclude overrules it on the lines they both match, so exceptions read straight down the list.
A→≠ AB→ABCshows everything withA, exceptAB, but keepsABC. Nest another exclude under that one and it takes over again, as deep as you like. Switch the nested filter off and the exclude has the last word again. - Only nesting overrules. An exclude elsewhere in the list — a sibling, or another branch — still takes the line away outright.
- A line takes its colour from the first enabled include in the list that matched it, refined by whichever enabled includes nested under that one matched too — never by a filter in a branch further down, however deeply nested.
- Excludes never colour anything, and an exception nested under one colours a line only if it also sits under the include that claimed it. Otherwise the line comes back in that include's colour.
- Whatever the winning filter leaves unset it takes from its nearest ancestor that sets it, attribute by attribute, and from the view's defaults when none does.
An exclude beats everything by default, wherever it sits in the list, which is how TextAnalysisTool.NET behaves. If you would rather an exclude took its precedence from its place in the list, like any other filter, Preferences ▸ Exclude filters offers the other rule: Always hide what they match, wherever they are listed is the default, Hide only what an earlier filter has not claimed the alternative.
Pick the second and the list reads as one ordered set of rules. The first enabled filter that matches a line decides everything about it, whether you see it and what colour it is, and only a filter nested under that one may take over. An exclude is then just a filter whose answer is hidden: put it above what it should beat and below what should beat it.
- An exception then needs no special case.
≠ Heartbeat→Errorhides heartbeats and keeps the ones that are errors, because a nested filter takes over from the one it sits under. - To say show the rest of the file, end the list with a catch-all: a filter with an empty pattern matches every line, blank ones included.
- Switching between the two rules never re-reads the file. The view is rebuilt from the matches already in memory.
- The exclude column has a crossed-out eye in its header. A crossed-out eye in a row marks an exclude filter; a blank cell means include. Click a cell or press
Ctrl+Shift+Xto switch. Clicking within a selected group sets the group to the clicked filter's opposite kind, without changing its checkboxes or unselected children. Undo restores the original mixed states. - Count is how many lines in the whole file match, not how many are on screen, or how many are in the crop when you have one.
Shift+Spaceswitches a filter's whole subtree on or off.- The list docks to any edge of the window, or hides altogether (
Ctrl+Shift+L), and comes back next time on the same edge at the same size.
Ctrl+N turns the text you have selected inside a line — or the whole line under the caret — into a filter.
Every new filter is made in the same dialog, which asks where it should go:
| Key | Where it goes |
|---|---|
Ctrl+N |
The top of the list, or the end of it, as set in Preferences |
Ctrl+Shift+N |
Directly above the selected filter, as its sibling |
Ctrl+Alt+N |
Under the selected filter, as its child |
The key you press only picks a starting point. All three are offered in the dialog, each with its key beside it, and pressing one of the others moves the choice. Changing your mind after Ctrl+N costs a keystroke and never the mouse.
- The pattern box previews the colours the filter would really take where it is going, inheritance and all, so the preview follows the choice.
- A place that cannot be had — nothing selected to sit above, or nesting that would run past the deepest level — is shown but greyed, and asking for it settles on the default.
- I'm feeling lucky picks a legible colour pair that no other filter is using; Paint chips… shows the rest of the palette.
- A filter with no colour of its own leaves no mark on the match map.
- Select several filters and enable, disable, delete, duplicate, reorder, nest or recolour them in one go.
- Dragging carries the whole selection, nesting and all.
Ctrl+Zundoes filter edits, drags included.
Ctrl+Esearches the list without hiding or reordering it: non-matching filters dim rather than disappear.F4/Shift+F4walk the log through the selected filter's matches, without changing which filters are on.- While cycling through matches, the status bar shows your position and the total, such as 4 of 10 matching lines, counting only lines in the current view. Changing the selection in the log clears the tally.
- A preset names a combination of filters: the payment incident, the slow queries.
- The tick applies a preset; the highlight only says which one the commands act on.
- Ticking two gives you both, and ticking one moves only its own filters, so anything you switched on by hand stays as you left it.
Ctrl+Hswitches between dimming the lines that didn't match and hiding them altogether.- Line numbers are always the file's own, either way.
- Select a run of lines and
Ctrl+[throws the rest of the file away — a two-million-line log of forty test runs becomes the one test run you are reading. - Everything then behaves as though the file held only those lines: the counts, the match map, the scrollbar,
Ctrl+End,Ctrl+A, the find tally, elapsed times, Save Current Lines. Only the line numbers still read the file's own, so you never lose your place in it. - The crop takes everything between the first and last line you selected, including lines the filters are hiding — so
Ctrl+Hinside a crop reveals the rest of that stretch rather than a different one. - Filters still read the whole file, and a filter's count says how much of it is in front of you. Hover one for both numbers.
Ctrl+]puts the file back, and pressing it again returns to the same crop. Looking outside for a moment costs two keystrokes and never a re-selection. Clicking the crop in the menu bar does the same.- Cropping clears the selection, since the lines were only the way of naming the stretch. Lifting the crop hands them back exactly, unless you have chosen something else in the meantime, which always wins.
Ctrl+Fsearches the text, literal or regex, marking every occurrence on screen as you type.- Selecting two or more characters marks their other occurrences on screen, using the find bar's Case sensitive setting even when the bar is closed.
- Find works on every line, not just the shown ones: hits on hidden lines are counted separately, and it says so when a match lands on one.
- Cascade finds the timestamp in your log by itself and puts the time since the previous line in the margin — the previous line on screen, so with the noise filtered away it measures between the lines you kept, which is a latency profile of whatever the filters select.
Ctrl+Ron a line measures everything from that line instead: how long after the trigger each thing happened, with the lines above it reading as negative. Unlike the gap to the previous line, it does not change when you filter.
Ctrl+Shift+Rsteps between the three: the previous line, the start of the file, and your reference. The status bar says which (Δ Prev,Δ Start,Δ Ref) and the column stays the same width whichever it is.- Cropped,
Δ Startmeasures from the start of the crop, so a test case reads from its own first line rather than from the top of the log. Ctrl+Shift+Ggoes back to the reference, wherever you have scrolled to since.- Select a line and the status bar says the same thing in words; select several and it says how long they cover.
- If your timestamp is somewhere the guess cannot reach, name the field it is in under Field Settings.
Ctrl+Shift+MandCtrl+Shift+Bturn the two displays off.
Ctrl+Mputs a strip beside the scrollbar showing the log itself, a pixel a line, in the colours the filters gave it. Markers run down its left edge and find hits down its right.- Where one pixel stands for many lines the exception among them wins, so a lone error among ten thousand ordinary ones still gets a pixel of its own.
- A file too big to fit even at thirty lines a pixel shows as a window around where you are, sliding as you read. The scrollbar beside it is always the whole file.
- Eight colours for marking lines by hand:
Ctrl+1…Ctrl+8on the selection. - The bare number keys
1…8then walk between the lines you marked,Shiftto go backwards. - Marked by marker N is a filter type, so lines you picked out by hand can be treated as a category.
- Hovering a line names every filter that matched it — including the ones that are switched off.
- File ▸ Save Current Lines… writes out exactly what the view is showing — the filters' verdict, and the crop if you have one.
- Edit ▸ Copy with Line Numbers copies the file's own line numbers along with the text.
Long lines are either wrapped (Alt+Z) or split into fields you can hide and reorder.
Ctrl+Shift+Csplits every line into fields for display, guessing a template from the line under the caret.- Filtering and searching always run on the whole raw line, so this can shorten a line but never hide one.
- A line the template does not match is left whole and untouched.
A template is a picture of your line: write one out, replace what changes with *, and wrap each field in { }.
your line: [2026-08-05T09:44:02.118][payment-svc][ERROR] charge declined for order 48210
template: {[*]}{[*]}{[*]} {*}
* |
the text that changes: matches as little as it can, up to whatever you wrote next |
. |
any one character, whatever it happens to be |
{ } |
one field: the unit that gets hidden or moved, punctuation and all |
| anything else | has to be there, except a run of spaces, which matches any run of spaces |
\ |
makes { } * . \ ordinary |
- Punctuation goes with the field, so hiding the middle of
[a][b][c]leaves[a][c]rather than[a][][c].
Columns lays the fields out as a table: drag a header to reorder, double-click it to rename, right-click for the list of which fields are shown.
Inline keeps every row a line and leaves out what you hid — better when one field dwarfs the rest. A strip of chips stands in for the header: click one to put a field away or bring it back, drag it to move the field along the row.
Ctrl+Shift+Dtries the template against real lines from the file: how many of them match, and where a line that doesn't stopped matching.- Detect writes the template for you when the line begins with bracketed groups:
[ ],( )or< >. - Name a field as the time and Cascade proposes the format that reads it — a .NET format string like
yyyy-MM-dd HH:mm:ss.fff, orepoch:ms— and shows that format reading your own log back to you.
- Drop a log on the window, hand it to
Cascade.exeon the command line, or use Open from Clipboard for one you have pasted from somewhere else. F5re-reads the file, for when you have just re-run whatever produced it.- Dropping a
.cascadeor.tatfile loads its filters instead of opening it as a log. - The file is memory-mapped and indexed as it streams, at a little over two bytes a line: a 1 GB, 10-million-line log is on screen in milliseconds and fully indexed in under half a second.
- A byte-order mark is honoured; without one, UTF-8 is assumed.
- View ▸ Encoding reads the file again as UTF-16 or UTF-32 (either endianness), Windows-1252 or the system code page.
.cascadeis JSON — the filter tree with its styles, the presets, and the field template — so a filter set diffs well in source control.Ctrl+Ssaves the set you have; File ▸ Append Filters… merges another set into it.- TextAnalysisTool.NET
.tatfiles are imported, flattened to top-level filters. Saving is always.cascade.
Font, colours, tab size and line spacing, plus the switches not worth a menu entry of their own: which filter wins, loading the last filter file at startup, where Ctrl+N puts a new filter, a memory dump to %TEMP% if the window ever stops responding, and support for screen readers and UI automation. Anything the View menu already toggles stays there rather than being repeated here.
File ▸ Settings ▸ Export / Import moves your preferences to another machine.
Preferences in %APPDATA%\Cascade\settings.json, machine-local state in state.json, and nothing at all in the registry. CASCADE_SETTINGS_DIR moves the folder.
Cascade.exe [file] [/Filters:<path>]
/Filters:also stops the last-used filter file being loaded automatically.CASCADE_UPDATE=offturns off the update check.
Log view
| Key | Action |
|---|---|
↑ ↓ PgUp PgDn |
Move the caret (Shift extends the selection) |
Ctrl+Home / Ctrl+End |
First / last line |
Ctrl+↑ / Ctrl+↓ |
Scroll a line without moving the caret |
← → / Home End |
Scroll sideways / to the far left and right |
| Drag, double-click, triple-click | Select part of a line, a word, the whole line |
Ctrl+A / Ctrl+C |
Select all / copy |
1…8 / Shift+1…8 |
Next / previous line with that marker |
Ctrl+1…Ctrl+8 |
Toggle that marker on the selection |
Ctrl+wheel |
Zoom |
Filter list
| Key | Action |
|---|---|
Space / Shift+Space |
Enable or disable the selection / their subtrees |
Ctrl+Shift+X |
Switch the current filter or selected group between include and exclude |
Enter / Delete / Ctrl+D |
Edit / remove / duplicate the selection |
Shift+↑ ↓ / Ctrl+↑ ↓ / Ctrl+Space / Ctrl+A |
Extend, move through, add to, or take the whole selection |
Alt+↑ ↓ ← → |
Move, nest and un-nest |
F4 / Shift+F4 |
Next / previous line matching the current filter |
Ctrl+E, then Enter / Shift+Enter / F3 |
Search the list and walk the matches |
| Double-click a filter / the empty space below | Edit it / add a new one |
Global
| Key | Action |
|---|---|
Ctrl+O / F5 / Ctrl+S |
Open / reload / save filters |
Ctrl+Z / Ctrl+Y |
Undo / redo a filter edit |
Ctrl+F / F3 / Shift+F3 |
Find / next / previous |
Ctrl+G / Ctrl+Shift+G |
Go to a line you name / to the line you are measuring from |
Ctrl+H |
Show only filtered lines |
Ctrl+[ / Ctrl+] |
Crop to the selected lines / show the whole file again, or return to the crop |
Ctrl+N / Ctrl+Shift+N / Ctrl+Alt+N |
New filter from the selection or the current line, going to the usual end of the list / above the selected filter / under it as a child |
Ctrl+E |
Search the filter list |
Ctrl+L / Ctrl+M / Alt+Z / Ctrl+Shift+C |
Line numbers / match map / word wrap / split lines into fields |
Ctrl+Shift+X / Ctrl+Shift+D |
Switch field layout / field settings |
Ctrl+Shift+M / Ctrl+Shift+B |
Elapsed times in the margin / in the status bar |
Ctrl+R / Ctrl+Shift+R |
Measure from this line / step through what to measure from |
Ctrl+Shift+P / Ctrl+Shift+L |
Show or hide the presets pane / the filter list |
Ctrl+Shift+T / Ctrl+Shift+F |
Focus the log view / the filter list |
Ctrl+Shift+↑ ↓ ← → |
Dock the filter list |
Ctrl++ / Ctrl+- / Ctrl+0 |
Zoom in / out / reset |
Tab |
Move between the log and the filter list |
Esc |
Stop a search, then close the bar and clear its marks |
Needs the .NET 10 SDK.
dotnet build Cascade.slnx -c Release
dotnet test tests/Cascade.Core.Tests/Cascade.Core.Tests.csproj # engine tests
./scripts/Run-UiTests.ps1 -Publish # UI automation, off-screen
./scripts/Build-DocImages.ps1 # every picture above, from the appsrc/Cascade.Core is the UI-agnostic engine — indexing, filtering, find, markers, columns, persistence — and src/Cascade.App is the WinForms GUI.
MIT © 2026 Rahul Ramadas.























