fixtags
v0.4.0
Published
Stream and analyze FIX protocol logs — CLI bridge for fixtags.dev
Readme
FIXtags
fixtags.dev — paste, browse, and live-stream FIX protocol logs. Any delimiter, any log format, entirely in your browser.
Your logs never leave your machine: parsing, search, and analysis are all client-side, and live streaming connects your browser directly to a localhost bridge. There is no backend.
Use it
Paste FIX messages or drop a log file on fixtags.dev. The parser extracts messages from raw FIX, log lines with prefixes, JSON-wrapped logs, and multi-line pretty-printed dumps — SOH, |, or ^A delimited.
- Browse — virtualized table (smooth at 100k+ messages), detail pane with decoded tags, repeating groups, checksum/BodyLength validation
- Correlate — orders grouped across cancel/replace chains, lifecycle timeline, one-click trace; per-leg breakdown for multileg options spreads; ack-latency stats (p50/p95/max + slowest orders); filter the Orders view by state (working/partial/filled/unacked/rejected)
- Find problems — automatic findings: fills after terminal states, CumQty/LeavesQty mismatches, duplicate ExecIDs without PossDup, unacked orders, sequence gaps
- Filter — query language (
@Symbol == "AAPL" and @OrdType == @Limit), regex, per-type chips, drag a time window on the volume timeline, one-tap filter by a message's ClOrdID/OrigClOrdID/OrderID/Sender/Target - Read — syntax-highlighted paste box: FIX tags and values color-coded as you type
- Compare — diff any two messages, unified or split
- Customize — promote any tag to a table column; CSV/JSON export; click any order ID to copy it; copy findings as Markdown
- Share — links carry the log plus your filter, view mode, trace, and selection, compressed into the URL fragment and never sent anywhere
Live streaming
Tail a log in your terminal and watch it in the browser:
npx fixtags /var/log/fix.log # follow a file (rotation-aware)
tail -f fix.log | npx fixtags # pipe anything
ssh prod 'tail -f /var/log/fix.log' | npx fixtagsThe CLI starts a localhost-only server with a per-session token and opens fixtags.dev connected to it. The table follows the tail like a terminal — scroll up to inspect (a "↓ N new" chip brings you back), pause/resume, stop and keep the data for analysis.
Simple by default. A plain run is ephemeral: the server lives inside the CLI process itself, stays in the foreground, and Ctrl-C stops everything — nothing is written to disk. This is what you get from fixtags file.log or a bare pipe.
Sessions and streams opt into the background. Naming a --session NAME or --stream NAME (or passing --bg) switches to a persistent background server: the first run starts it and opens one tab; every run after that feeds into it, and it keeps running after Ctrl-C. Use --session NAME for separate tabs/workspaces, and --stream NAME for restartable feeds inside a session — handy when you want several terminals feeding one page, or a session that survives you closing the terminal. An idle background session exits on its own after --idle-timeout minutes (default 30; 0 disables), preserving its capture for next time.
Managed commands. For commands you routinely restart, let fixtags own the process with -- <cmd>. Re-running the same --stream kills the old managed command and starts the new one under the same browser stream. This is useful for pod churn:
fixtags --stream orders -- kubectl logs pod-a -f
fixtags --stream orders -- kubectl logs pod-b -f # replaces only ordersReload-proof (background sessions only). Each background session is captured under ~/.fixtags/sessions/<name>, so reloading the page (or recovering from a crash) restores the retained stream data. Retention is --history lines (default 100,000). fixtags ls shows every background session and whether it's still running.
Headless query
Filter a log from the terminal with the same query language as the fixtags.dev search box — no server, no browser, nothing written under ~/.fixtags:
fixtags --grep '@Symbol == "AAPL" and @OrdType == @Limit' fix.log # query language
tail -f fix.log | fixtags --grep '35=8' # live; plain regex/substring also works
fixtags --grep '@MsgType == @NewOrderSingle' -c fix.log # -c/--count: print the match countPrints matching lines (verbatim, colorized on a TTY) and exits: 0 on a match, 1 on no match, 2 for a malformed query. Piping a file processes it to EOF and exits, like grep; stdin is streamed line by line as it arrives. Multi-line pretty-printed dumps are folded and matched in file mode only.
fixtags fix.log # ephemeral: foreground, nothing on disk
tail -f fix.log | fixtags # same, piped; Ctrl-C stops it
fixtags --bg fix.log # same feed, but as a background session
tail -f orders.log | fixtags --stream orders
tail -f execs.log | fixtags --stream execs
fixtags --stream orders -- kubectl logs pod-a -f
fixtags --stream orders -- kubectl logs pod-b -f # restart orders
fixtags ls # list background sessions
fixtags streams ls # list streams in the selected session
fixtags streams clear orders # remove orders from the view/capture
fixtags streams stop execs # stop accepting execs until you rerun that feeder
tail -f execs.log | fixtags --stream execs # restart that stream
fixtags --session cert --stream orders cert.log
fixtags --new # clear the selected session
fixtags sessions stop # stop the selected session serverCommands (act on the session selected by --session; ls/sessions ls always list every session):
| Command | |
|---|---|
| ls | list background sessions and whether each is running (alias: sessions ls) |
| sessions [ls\|stop] | list sessions (default), or stop the selected one |
| streams [ls] | list stream status and retained line counts in the selected session |
| streams clear NAME | remove one stream's retained messages from the session |
| streams stop NAME | stop accepting new lines for a stream; rerun the feeder to restart |
| url | print the selected session's page URL |
| config | show persisted defaults (~/.fixtags/config.json) |
| config set KEY VALUE | set a default (see Config) |
| config unset KEY | remove a default |
A file literally named sessions, streams, config, ls, or url needs a ./ prefix (e.g. ./sessions) to be fed instead of run as a command.
Options:
| Flag | |
|---|---|
| --new | clear the selected session's view for a fresh start |
| --session NAME | use a named background session (implies persistent; default default) |
| --stream NAME | feed this source as a named stream (implies persistent) |
| --bg | force the persistent background-session model without naming anything |
| --idle-timeout MIN | minutes of inactivity before a background session exits on its own (default 30; 0 disables) |
| -- <cmd> | run a managed feeder command; rerunning the same --stream replaces the previous command |
| --local | serve the app from the CLI itself — works in Safari and offline |
| --echo | also print streamed lines to the terminal, tags colorized on a TTY (NO_COLOR disables) |
| --browser NAME | open in a specific browser (chrome, firefox, edge, …) instead of your default |
| --port N | fixed port (default: random) |
| --no-open | print the URL instead of opening a browser |
| --app URL | app origin to open (default https://fixtags.dev) |
| --history N | lines retained for replay on reload (default 100000) |
| --completions SHELL | print a completion script for zsh, bash, or fish |
| --grep EXPR [file] | headless query: filter a log with the search box's query language (or plain regex/substring) and print matches, then exit; standalone, not combinable with any session/server flag |
| -c, --count | with --grep, print the match count instead of the matching lines |
| -h, --help | full usage and examples |
| -v, --version | print the version |
--local/--app/--browser/--port apply when the background server first starts.
Run fixtags --help for usage and examples.
Shell completions
echo 'eval "$(fixtags --completions zsh)"' >> ~/.zshrc # or save to a file on $fpath as `_fixtags` for faster startup
echo 'eval "$(fixtags --completions bash)"' >> ~/.bashrc
fixtags --completions fish > ~/.config/fish/completions/fixtags.fishCompletes flags with their descriptions, the sessions/streams/config/ls/url commands and their verbs, session names (from ~/.fixtags/sessions), stream names (by asking a running session via streams ls), config keys, and browser names; falls back to file/command completion where appropriate.
Config
Persist launch defaults in ~/.fixtags/config.json so you don't retype the same flags. Precedence is built-in default < config < explicit flag, so a flag always wins for a one-off. Edit it with the config command:
fixtags config set browser chrome # always open in Chrome (handy if Safari is your default)
fixtags config set idleTimeout 5 # reap idle background sessions after 5 min
fixtags config # show current defaults
fixtags config unset browser # back to the built-in defaultRecognized keys: browser, app, port, history, idleTimeout, local. A flag always overrides its config default for a single run; --no-config ignores the file entirely for one run (handy to undo a config'd local, which has no negating flag of its own).
Live mode needs Chrome, Edge, or Firefox when using fixtags.dev (Safari blocks https→localhost); --local works everywhere. If Safari is your default browser, open live mode in Chrome with --browser chrome (or set it once with fixtags config set browser chrome). For logs on a remote machine, either pipe through ssh as above, or run fixtags on the server and tunnel: ssh -L 7791:127.0.0.1:7791 prod.
Lines arriving late or in bursts? A grep/filter in the middle of a pipe block-buffers its output when it isn't writing to a terminal, so lines pile up before reaching fixtags. Make that stage line-buffered (the flag depends on the tool): grep --line-buffered PATTERN, rg --line-buffered PATTERN, ack --flush PATTERN, or stdbuf -oL <cmd>. Or skip the filter and use the fixtags search box instead.
Development
No build step. index.html + style.css + core.js (DOM-free parsing/query/validation, also loaded by the tests) + app.js (UI) + cli.js.
just serve # local dev server on :3535
just check # syntax + handler audit + node:test suite
npm run smoke:browser # headless Chromium smoke test with production CSP headers
just deploy-dev # preview deployment (preview.fixtags.pages.dev)
just deploy # productionSee RELEASE.md for the deploy/publish checklist.
