- Go 99.7%
- Makefile 0.3%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
|
|
||
| .forgejo/workflows | ||
| cmd/iwatch | ||
| internal | ||
| .gitignore | ||
| AGENTS.md | ||
| go.mod | ||
| go.sum | ||
| Makefile | ||
| README.md | ||
iwatch
iwatch ist eine interaktive Dev-Log-TUI fuer Build- und Entwicklungs-Workflows. Sie kombiniert automatische Command-Erkennung, einen Hauptprozess-Runner und eine Oberflaeche zum Lesen, Filtern und Durchsuchen von Prozess-Output.
Features
- Auto-Mode mit Erkennung von
package.json-Scripts undMakefile-Targets - Manueller Rebuild des aktiven Commands per
r - TUI mit Log-View, Bottom-Query und Events-Pane fuer Systemmeldungen
- Zusaetzliche LogStreams fuer Dateien und Hintergrundprozesse, pro Preset aktivierbar
- Scrollen per Cursor, Live-Filter, logfmt-verstaendige Feldabfragen und Highlight-Regeln
- Selektierbare Logzeilen mit Detailansicht fuer geparste Felder und pretty printed JSON-Werte
- Benannte Filter-Presets mit OR-Klauseln, Highlighting, Logfeld-Anzeige und Zeitformaten
- Konfigurierbarer Ringbuffer, standardmaessig
1_000_000Zeilen - Beenden per
qoderEsc+Esc+Esc
Installation
go mod tidy
go build ./cmd/iwatch
Usage
iwatch
iwatch --command npm:dev
iwatch --buffer-lines 200000
iwatch --config ./.iwatch.config.json
Keybindings
r: aktives Command stoppen und neu starten?: Keymap-Hilfe oeffnen oder schliessenl: Streams-Pane oeffnen oder schliessen/: Bottom-Query oeffnenf: Bottom-Query oeffnenw: Events-Pane oeffnen (Streams, Automations, Config)v: Logfeld-Menue oeffnen, per Tippen filtern und erkannte logfmt-Felder ein-/ausblendenF: Live-Feldfilter oeffnen, Felder waehlen und per Contains-Wert filternb: Gruppierungsfeld waehlen (Standard:component),/.: Gruppierungswert vor/zurueck durchschalten (inkl. alle Werte)g: Fullscreen-Config-Editor oeffnen[/]oderleft/right: zwischen konfigurierten Presets umschaltentab: Fokus zwischen offenen Panes wechselnup/down,j/k: scrollen oder Listen bewegenpgup/pgdown,ctrl+u/ctrl+d: seitenweise scrollenhome/end,G: an Anfang oder Ende springenenter: ans Log-Ende springen oder in der Auswahl die Detailansicht oeffnenenterim Streams-Pane: ausgewaehlten on-demand Stream starten oder laufenden Stream stoppenoim Streams-Pane: Ausgabe eines einzelnen Streams modal anzeigent: Logbuffer leeren und wieder ans Log-Ende springenenterzweimal schnell: visuellen Trenner einfuegenn/N: zum naechsten oder vorherigen sichtbaren TrefferS: Split-Richtung wechselnescoderenterin der Detailansicht: zurueck zur Logansichtesc: Bottom-Query schliesseny: Share/Copy der aktuellen Zeile (Agent-friendly Snippet)Y: Share/Copy der aktuellen Zeile inkl. Kontext (+/- 20 Zeilen)O: erkannte Vite-URL im Browser oeffnen (wenn verfuegbar)q: App beendenescdreimal: App beenden
Query Syntax
- Freitext wie
heartbeatmatcht case-insensitive gegen die gesamte Logzeile. key=value-Filter wielevel=infooderlua-manager.resource=thread-examplematchen gegen logfmt-aehnliche Felder in der Zeile.- Mehrere Tokens werden mit
ANDkombiniert, zum Beispiellevel=info heartbeatodermsg=heartbeat level=info. - Feldwerte matchen case-insensitive als Substring, also passt
msg=heartauch aufmsg="thread example heartbeat".
Die Bottom-Query wirkt zusaetzlich auf das aktive Preset. Strukturierte OR-Filter werden in der Config-Datei ueber Presets gepflegt.
Der Live-Feldfilter unter F wirkt nur fuer die laufende Sitzung. Mehrere gesetzte Feldfilter werden mit AND kombiniert und matchen case-insensitive per Contains gegen erkannte logfmt-Felder.
Die Gruppierung filtert live nach einem strukturierten Feld (Default component, konfigurierbar als ui.logView.groupField). , und . schalten exakte Werte dieses Feldes durch (inkl. „alle“). Das ist getrennt vom Contains-Feldfilter unter F und kombiniert sich per AND mit Preset, Query und Feldfiltern.
Structured Logging (logfmt + JSON Lines)
iwatch erkennt strukturierte Felder sowohl aus logfmt-aehnlichen key=value Tokens als auch aus JSON Lines (eine JSON pro Zeile).\n\nBeispiele:\n\n- logfmt:\n - level=INFO msg=\"hello\" request_id=abc\n- JSON Lines:\n - {\"level\":\"INFO\",\"msg\":\"hello\",\"http\":{\"port\":8080}}\n\nNested JSON wird als dot.path flach gemacht (z.B. http.port=8080) und ist dann im Filter nutzbar.
Dev-Flow: Go Backend + Vite (Port Handshake)
Wenn du Go-Backend und Vite getrennt startest, kann iwatch den Backend-Port/URL aus den Logs erkennen und diese Info an den Vite-Devserver als ENV weitergeben.\n\nEmpfohlen:\n\n- Backend loggt structured url=... oder port=... (logfmt oder JSON Lines)\n- Vite wird als Stream mit role: \"vite\" konfiguriert, Backend als role: \"backend\"\n\nMinimal-Beispiel fuer streams:\n\njson\n{\n \"streams\": [\n {\n \"id\": \"backend\",\n \"title\": \"Go backend\",\n \"type\": \"process\",\n \"role\": \"backend\",\n \"cmd\": \"go run ./cmd/server\",\n \"autoStart\": true\n },\n {\n \"id\": \"vite\",\n \"title\": \"Vite dev\",\n \"type\": \"process\",\n \"role\": \"vite\",\n \"cmd\": \"npm run dev\",\n \"autoStart\": false\n }\n ]\n}\n\n\nSobald iwatch eine Backend-URL erkennt, setzt es beim Vite-Stream:\n\n- BACKEND_URL\n- BACKEND_PORT (wenn ableitbar)\n\nund startet den Vite-Stream (on-demand) automatisch.\n\nWenn iwatch die Vite-URL erkennt, kannst du sie mit O direkt oeffnen.
Config Editor
goeffnet einen Fullscreen-Editor fuer Presets, Filter, Highlighting, sichtbare Logfelder und Darstellungsoptionen.up/downoderj/kbewegt die Auswahl.pgup/pgdownoderctrl+u/ctrl+dbewegt sich seitenweise.entertoggelt oder fuehrt die aktuelle Aktion aus.ebearbeitet Textfelder wie Preset-ID, Clauses, Highlight-Regeln oder sichtbare Logfelder.afuegt je nach Bereich Presets, OR-Clauses oder Highlight-Regeln hinzu.dloescht den aktuellen Preset-/Clause-/Rule-Eintrag.ydupliziert das aktive Preset.qbeendet die App direkt,escverlaesst den Editor.- Speichern schreibt projektlokal nach
./.iwatch/config.json, bestehende./.iwatch.config.jsonwird weiterverwendet.
Config
Geladene Config-Dateien in Prioritaetsreihenfolge:
~/.iwatch/config.json./.iwatch/config.json./.iwatch.config.json
CLI-Flags haben Vorrang vor Config-Werten.
Beispiel:
{
"bufferLines": 1000000,
"defaultCommand": "npm:dev",
"commands": [
{
"id": "go:test",
"title": "go test",
"cmd": "go test ./...",
"source": "config"
}
],
"streams": [
{
"id": "app-log",
"title": "App log",
"type": "file",
"source": "./logs/app.log"
},
{
"id": "worker",
"title": "Worker",
"type": "process",
"cmd": "npm run worker",
"cwd": "./",
"autoStart": false
}
],
"highlightRules": [
{
"id": "errors",
"pattern": "(?i)error|failed",
"style": "error",
"priority": 100
},
{
"id": "warn",
"pattern": "(?i)warn",
"style": "warn",
"priority": 50
}
],
"ui": {
"openPanes": ["log", "events"],
"splitDirection": "vertical",
"focusPane": "log",
"activePreset": "ops",
"presets": [
{
"id": "ops",
"title": "Ops",
"streams": ["app-log", "worker"],
"clauses": [
{
"conditions": [
{ "field": "level", "value": "error" }
]
},
{
"conditions": [
{ "value": "panic" }
]
}
],
"highlightRules": [
{
"id": "panic",
"pattern": "(?i)panic|fatal",
"style": "error",
"priority": 100
}
]
},
{
"id": "heartbeat",
"title": "Heartbeat",
"clauses": [
{
"conditions": [
{ "field": "msg", "value": "heartbeat" },
{ "field": "level", "value": "info" }
]
}
]
}
],
"logView": {
"visibleFields": ["level", "msg", "lua-manager.resource"],
"hiddenFields": ["debug"],
"showRawMessage": true,
"showSource": false,
"showTimestamp": true,
"timeFormat": "date-short",
"wrapMode": "field",
"palette": "default"
}
}
}
Preset-Struktur
ui.presets[*].clausesist eine OR-Liste.clauses[*].conditionsinnerhalb einer Clause werden mit AND kombiniert.- Bedingungen mit
fieldmatchen gegen erkannte logfmt-Felder. - Bedingungen ohne
fieldmatchen als Freitext gegen die komplette Logzeile. ui.presets[*].streamsaktiviert gezielt Stream-IDs fuer dieses Preset. Wenn kein Preset ueberhauptstreamsdefiniert, gelten alle konfigurierten Streams als aktiv;autoStart: falsebleibt dabei on-demand.
LogStreams
- Root-
streamsdefiniert zusaetzliche Eingaben fuer die globale LogView. type: "file"liestsourceals Datei- oder Glob-Pfad. Beim Start springt der Stream ans aktuelle Dateiende und liest nur neue Zeilen. Truncate, Delete oder Recreate werden toleriert; nach Cleanup wird wieder ab Dateiende weitergelesen.type: "process"startetcmdmit optionalemcwdals Hintergrundprozess.autoStart: falsemacht den Stream on-demand startbar im Streams-Pane.- Nur Streams, die im aktiven Preset genannt sind und laufen, schreiben in den globalen Buffer. Dadurch wirken Bottom-Query, globale Suche, Feldfilter, Highlighting und Detailansicht unveraendert auch auf Stream-Zeilen.
Log View
timeFormat:time,date-short,relativeBeidate-shortundrelativewird keine separate Zeitspalte gerendert; die feste Zeitspalte ist nur fuertimesichtbar.wrapMode:off,simple,fieldpalette:default,contrast,ocean,forest,emberIndefaultwird die Zeit dunkelgrau, der logfmt-Key hellgrau und der Wert weiss gerendert.visibleFields: steuert die bevorzugte Reihenfolge erkannter logfmt-Felder; neue Felder werden automatisch angehaengthiddenFields: blendet erkannte logfmt-Felder aus; das Feld-Menue schreibt diese Liste erst nach explizitem Speichern in die ConfiggroupField: Standard-Gruppierungsfeld fuerbund,/.(Default:component)- Root-
highlightRulesbleiben als Rueckwaertskompatibilitaets-Fallback aktiv, wenn ein Preset keine eigenen Regeln hat