Skip to content
/ cutl Public

A terminal-based structured log viewer with real-time filtering, highlighting, and live tailing.

License

Notifications You must be signed in to change notification settings

joealcorn/cutl

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

24 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

cutl

A terminal-based structured log viewer with real-time filtering, highlighting, and live tailing.

Preview

Features

  • Live tailing - Follow logs in real-time from files or stdin
  • Multiple formats - JSON and logfmt with automatic detection
  • CEL filtering - Filter logs using Common Expression Language expressions
  • Field highlighting - Highlight specific fields or CEL-matched values
  • Multi-line selection - Select multiple lines and copy to clipboard
  • Syntax highlighting - Color-coded keys, levels, and IDs for readability
  • Humanized display - Readable timestamps and shortened trace IDs
  • Disk-backed - Efficiently handles large log files with LRU caching (file mode only; stdin streams to memory)
  • File watching - Automatically picks up new content as files grow

Installation

Homebrew:

brew tap joealcorn/tap
brew install cutl

Go:

go install github.com/joealcorn/cutl/cmd/cutl@latest

Binary:

Download from releases.

Usage

# View a JSON log file (default)
cutl app.log

# Pipe from another command
kubectl logs -f my-pod | cutl

# Specify format explicitly
cutl -f logfmt app.log
cutl -f json app.log

# Plain output (no interactive TUI)
cat app.log | cutl -p

Flags

Flag Description
-f Log format: json (default), logfmt, guess
-p Plain output mode (no interactive TUI)
--filter CEL filter expression (plain mode only)

Examples with flags

# Filter errors in plain mode (use line.field syntax)
cutl -p --filter 'line.level == "error"' app.log

# Filter by severity
cutl -p --filter 'line.level.gte("warn")' app.log

Note: In plain mode, use line.field syntax to access fields (e.g., line.level, line.msg). The interactive TUI mode supports direct field names.

Supported Formats

JSON (JSONL/NDJSON)

{"level":"info","msg":"Server started","time":"2024-01-15T10:30:00Z","port":8080}
{"level":"error","msg":"Connection failed","time":"2024-01-15T10:30:01Z","error":"timeout"}

logfmt

level=info msg="Server started" time=2024-01-15T10:30:00Z port=8080
level=error msg="Connection failed" time=2024-01-15T10:30:01Z error=timeout

Non-parseable lines are displayed as-is.

Key Bindings

Key Action
j / Move down
k / Move up
J / Shift+↓ Extend selection down
K / Shift+↑ Extend selection up
Ctrl+d / PgDn Page down
Ctrl+u / PgUp Page up
Shift+PgDn Extend selection page down
Shift+PgUp Extend selection page up
g / End Go to tail (newest)
G / Home Go to head (oldest)
c Copy selected line(s) to clipboard
f Open filter prompt
m Cycle display mode (human/normal/raw)
h Open highlight manager
q / Ctrl+c Quit

Filtering

Press f to enter filter mode. Filters use CEL (Common Expression Language) syntax.

  1. A field picker appears - select a field to start your expression
  2. The field is inserted and you can continue typing
  3. Press Tab to auto-complete field names as you type

Examples

# Filter by level
level == "error"

# Filter by level severity (gte, gt, lte, lt)
level.gte("warn")              # warn, error, fatal
level.gt("info")               # warn, error, fatal
level.lte("info")              # trace, debug, info
level.lt("warn")               # trace, debug, info

# Filter by message content
message.contains("timeout")

# Combine conditions
level == "error" && message.contains("database")

# Filter by trace ID
trace_id == "abc123"

# Check if a field exists
has(line.status_code)

# Access any field via line map
line.custom_field == "value"

Available Fields

All field names from your logs are automatically available as top-level variables. For example, if your logs contain request_id, user_id, and status_code, you can use them directly:

request_id == "abc123"
status_code >= 400
user_id.contains("admin")

The line map is also available for accessing any field: line.field_name.

Highlighting

Press h to open the highlight manager. From there:

  • Enter: Toggle a highlight on/off
  • e: Edit a CEL expression (when selected)
  • /: Add a new CEL highlight
  • Esc: Close

Field Highlights

Select a field from the current log line and press Enter to highlight all values of that field.

CEL Highlights

Press / to add a CEL expression highlight using CEL syntax:

level == "error"           # Highlights "error" values in level field
status_code >= 400         # Highlights matching status codes
msg.contains("timeout")    # Highlights matching messages

Multiple highlights can be active with different colors.

Display Modes

Press m to cycle through display modes:

Human Mode (default)

  • Timestamps are converted to local 24-hour time: 2024/01/15 15:04:05
  • Trace IDs are shortened to 4-character hashes for visual correlation
  • Keys and values are styled with colors

Normal Mode

  • Timestamps shown as-is
  • IDs shown in full with colored styling
  • Keys styled with colors

Raw Mode

  • Original log line shown exactly as it appears in the file

Status Bar

[LIVE] Lines 1-50 of 1000 | f:filter(2) | m:human | h:highlight(3) | c:copy | g:follow | q:quit
  • [LIVE] - Following tail (green)
  • [PAUSED] - Scrolled away from tail (orange)
  • [EOF] - Input stream ended (gray)
  • 123/s - Lines per second (when streaming)
  • f:filter(N) - Number of active filters
  • m:human/normal/raw - Current display mode
  • h:highlight(N) - Number of active highlights
  • c:copy - Copy selected lines

License

MIT

About

A terminal-based structured log viewer with real-time filtering, highlighting, and live tailing.

Resources

License

Stars

Watchers

Forks

Languages