Log Analysis Workflow
VisiData turns raw server logs into queryable, filterable, sortable sheets — all without writing SQL or awk pipelines. This lesson covers Nginx access logs, syslog, and JSON application logs.
Learning Focus
Follow each workflow end-to-end. The goal is not memorizing every step — it is building muscle memory for: open → type columns → filter → frequency table → export.
Nginx Access Log Analysis
Open and Structure the Log
vd /var/log/nginx/access.log
Nginx combined log format has these fields (in order):
ip - - [date] "method path protocol" status bytes referer user_agent
VisiData may open this as a single text column. Parse the fields with regex capture groups:
# Move to the single text column
;
# Enter this regex (captures key fields):
(?P<ip>\S+) \S+ \S+ \[(?P<date>[^\]]+)\] "(?P<method>\S+) (?P<path>\S+) \S+" (?P<status>\d+) (?P<bytes>\d+)
# Press Enter → named columns appear: ip, date, method, path, status, bytes
Type the Columns
# Move to 'status', press # → integer
# Move to 'bytes', press # → integer
# Move to 'date', press @ → date
Analyze Status Codes
# Move to 'status' column
Shift+F
# Frequency table: 200: 85%, 404: 10%, 500: 2%
# Press ] to sort by count descending
# Press Enter on 500 → see all 500 error rows
Find the Busiest IPs
# Move to 'ip' column
Shift+F
# Frequency table: IPs ranked by request count
# Press ] to sort descending → top attacker IPs at top
Export 500 Errors for Incident Report
# Select all 500 status rows
# Move to 'status' column
|
# Enter: ^500$
# Open filtered sheet
"
# Save as CSV
Ctrl+S
# Enter: /tmp/nginx_500_errors.csv
Syslog Analysis
vd /var/log/syslog
# Search for errors
g/
# Enter: error|Error|ERROR
# Select matching rows
g|
# Enter: error|Error|ERROR
# Filter to error-only sheet
"
# Frequency table by hostname (if multiple servers forward to this log)
Shift+F
JSON Application Log Analysis
# JSON Lines format (one JSON object per line)
vd /var/log/app/events.jsonl
# VisiData auto-parses JSONL into columns
# Expand nested fields
# Move to a column containing nested dicts: press (
Access Log Dashboard Workflow
A complete end-to-end workflow:
Practical Command Sequence
# Full nginx log analysis in one session:
vd /var/log/nginx/access.log
# 1. Parse fields
;
# Enter regex: (?P<ip>\S+) \S+ \S+ \[(?P<date>[^\]]+)\] "(?P<method>\S+) (?P<path>\S+) \S+" (?P<status>\d+) (?P<bytes>\d+)
# 2. Type columns
# Move to status → #
# Move to bytes → #
# 3. Status distribution
# Move to status → Shift+F
# 4. Drill into 500s
# Press Enter on '500'
# 5. Find common error paths
# Move to 'path' → Shift+F
# 6. Export
Ctrl+S
# Enter: /tmp/error_paths.csv
# 7. Return to overview
q (from frequency table)
q (from error sheet)
q (from status frequency table)
Troubleshooting Matrix
| Problem | Cause | Fix |
|---|---|---|
| Log opens as single column | Unknown format | Use ; with a regex to parse fields |
| Regex captures nothing | Regex doesn't match log format | Test with / search first |
| Date sort wrong | Column typed as string | Cast to @ (date) |
| Large log takes long to open | Millions of lines | Use --max-rows 100000 to sample |
| Permission denied | Root-owned log | sudo vd /var/log/nginx/access.log or copy first |
Best Practices
- Always copy logs to
/tmp/before editing to avoid modifying production log files. - Save your parsing regex as a macro or CommandLog for reuse across daily log reviews.
- Use
--max-rows 100000for initial exploration of very large logs, then remove the limit once you know what you are looking for.
Hands-On Practice
# Generate a sample nginx-style log
cat > /tmp/access.log << 'EOF'
192.168.1.10 - - [12/May/2025:08:01:00 +0000] "GET /index.html HTTP/1.1" 200 1234 "-" "Mozilla/5.0"
192.168.1.11 - - [12/May/2025:08:01:05 +0000] "POST /api/login HTTP/1.1" 200 432 "-" "curl/7.64"
192.168.1.10 - - [12/May/2025:08:01:10 +0000] "GET /missing.html HTTP/1.1" 404 200 "-" "Mozilla/5.0"
192.168.1.12 - - [12/May/2025:08:01:15 +0000] "GET /admin HTTP/1.1" 500 89 "-" "Go-http-client/1.1"
EOF
vd /tmp/access.log
# Parse with regex, type columns, open frequency table on status