Structured, queryable logs your AI agent can actually read — losslessly, at up to 80% less storage.
LogSlim turns unstructured log streams into structured templates + parameters automatically — no regex, no grok, no SDK. That structure is the point: it lets an AI agent investigate a service by reasoning over a few dozen templates and their distributions instead of drowning in (and paying for) millions of raw lines. LogSlim exposes this to agents through a built-in MCP server.
As a side effect of separating templates from parameters, the same data stores in compressed Parquet at up to 80% less space — and every original line is still exactly reconstructable.
Lossless. Sits in front of your existing storage. No agent, no SDK changes, no vendor lock-in.
A busy service emits millions of log lines an hour. You cannot hand those to an LLM agent during an incident:
- it doesn't fit a context window,
- it costs a fortune per glance, and
- the one line that matters is buried among thousands of near-duplicates (the "lost in the middle" problem).
LogSlim collapses that firehose into templates + counts + per-slot distributions, losslessly. An agent then investigates the way a human SRE does — overview → anomaly → drill → raw — and pulls exact log lines only for the narrow window it actually needs.
On the included incident.log (13,728 lines of a real DB-pool-exhaustion cascade), the parser extracts 91 templates, the top 15 of which cover 90% of the volume — with 100% of lines structured. A whole-service overview is a couple thousand tokens instead of ~400k of raw text, and the root-cause chain (deploy → dropped index → SeqScan → pool exhaustion → OOM → circuit breaker) surfaces at the top of the anomaly list rather than as a needle in a haystack.
Raw log line: "2024-01-15 10:23:45 DEBUG 1234 DB SELECT table=sessions req-755556 12ms"
↓ universal masking + learned variables
Template: "{ts} DEBUG {num} DB SELECT table=sessions {var} {size}"
Parameters: ["2024-01-15 10:23:45", "1234", "req-755556", "12ms"]
-
Universal masking (
EntityMasker) replaces dataset-independent syntax — timestamps, IPs, UUIDs, paths, hex, sizes, numbers — in a single deterministic pass over the whole line, including entities glued mid-token (10.0.0.1:50010:Got). The masked shape is the template key: every line gets a template, deterministically, independent of input order. -
Learned variables (
TemplateLearner) discover each dataset's own ID species (session IDs, container names, request tokens) statistically from the corpus: a token position that varies across otherwise-identical shapes becomes a{var}slot only when its cardinality is high and its values look like identifiers — sosucceededvsfailedstays two templates whilereq-755556collapses into one. No per-dataset regexes, no configuration; the template set is a pure function of the data. -
Storage separates templates from parameters. A template seen 5,000 times is stored once; only the variable values per occurrence are stored. DuckDB's columnar encoding + zstd compression handles the rest. Reconstruction is byte-exact — a line that wouldn't round-trip is kept verbatim in
raw_logs(in practice: 0 lines on every validated dataset). -
logslim compactexportslog_entriesandraw_logsto Parquet files and replaces the tables withUNION ALLviews. The database stays queryable after compaction.
LogSlim ships an MCP server (mcp/) that exposes the structured-log API to agents (Claude Code/Desktop, Cursor, …) as atomic, composable tools. There is no opaque "answer" call — the agent composes the investigation, and each step ships structure, not raw text.
| Tool | Cost | Role | Returns |
|---|---|---|---|
list_templates, get_stats |
low | orient | the service's behavior as patterns + counts |
template_counts |
low | detect | per-template counts in a window (baseline=true → window-vs-baseline ratios in one call) |
new_templates |
low | detect | templates first seen (by log-event time) in a window — likely triggers |
template_timeseries |
low | localize | bucketed counts for one template → pinpoint the onset minute |
inspect_template |
low | drill | per-slot distributions + numeric summaries (min/max/avg/p50/p95), no rows pulled |
query_logs |
low–med | drill | occurrences as template once + parameter tuples (projectable to specific columns) |
raw_sample |
med | drill | a sample of unmatched/novel raw lines |
replay |
high | raw | exact original lines for a narrow window — the lossless escape hatch |
Structure over text, by default. query_logs returns the template once plus per-occurrence parameter tuples instead of repeating the static line skeleton on every row, and inspect_template answers "which values / how many / what distribution" from aggregates without pulling any occurrences. Measured on incident.log, 200 matches of one template cost ~3.1k tokens structured (vs ~6.8k as reconstructed text), or ~1k projected to a single field. Server instructions and an investigate_incident prompt steer the agent down this cost ladder — aggregates first, raw lines last.
# 1. start the API the MCP server talks to
logslim serve # → http://localhost:8080
# 2. build the MCP server
cd mcp && npm install && npm run build
# 3. register it with your agent (Claude Code shown)
claude mcp add logslim -- node "$(pwd)/dist/index.js"Configure via LOGSLIM_API_URL (default http://localhost:8080) and optional LOGSLIM_API_KEY. See mcp/README.md for the full tool reference and a no-agent smoke test.
Requirements: Java 17+, Node.js 18+ (dashboard and MCP server only)
# Build from source
git clone https://github.com/mihirrd/logslim
cd logslim
mvn clean package -q
alias logslim="java -jar $(pwd)/target/logslim-1.1.0.jar"By default LogSlim reads and writes logs.duckdb in the current directory. Override with -Dlogslim.db.path=:
java -Dlogslim.db.path=/var/log/myapp.duckdb -jar logslim-1.1.0.jar run --input <file>docker pull mihirrd/logslim:latest # pin a version in production, e.g. :1.3.0LogSlim stores state in a DuckDB file and a Parquet data directory. Mount a host directory so data persists across container restarts, and set up an alias to avoid the docker run boilerplate:
mkdir -p ./data
alias logslim='docker run --rm -v $(pwd)/data:/data mihirrd/logslim:latest'All examples below assume this alias. The database is written to ./data/logs.duckdb on the host.
cp /var/log/app.log ./data/ # the file must be inside the mounted volume
logslim run --input /data/app.loglogslim compact --yesExports all data to /data/logs_data/ as compressed Parquet and shrinks the .duckdb file to metadata only. Run periodically to reclaim disk.
docker run --rm -v $(pwd)/data:/data -p 8080:8080 mihirrd/logslim:latest serveThe dashboard at http://localhost:3000 and the MCP server connect to this.
docker run --rm -v $(pwd)/data:/data --network host mihirrd/logslim:latest \
consume --topic app-logs --bootstrap-servers localhost:9092 \
--batch-size 5000 --flush-interval PT5S --compact-interval PT10MIf Kafka runs in another container, connect them via a shared --network and use the Kafka container name as the bootstrap host.
# Templates — the structured overview
logslim templates --limit 10
logslim templates --search "failed login"
logslim templates --last 1h --limit 5
# Inspect a template: per-slot value stats + recent reconstructed lines
logslim inspect <template-id> --recent 10
# Query by pattern, filter by slot value (indexed lookup, not a text scan)
logslim query "User {id} failed login" --last 24h
logslim query "User {id} failed login" --filter id=456 --last 24h
# Replay exact original lines (lossless)
logslim replay --last 1h
logslim replay --from 2024-01-15T00:00:00Z --to 2024-01-15T23:59:59ZA Next.js dashboard for exploring logs without the CLI.
logslim serve # Terminal 1 → :8080
cd dashboard && npm install && npm run dev # Terminal 2 → :3000It exposes storage stats, template search with hit counts, per-slot inspection, pattern query with "did you mean?" suggestions, anomalies, relative/absolute replay, browser ingest, and compaction — all over the same API the MCP server uses.
The server reads only the compacted Parquet snapshot in logs_data/, so logslim serve can run concurrently with logslim run / logslim consume (the writer owns logs.duckdb). It auto-bootstraps an empty snapshot on first startup and sees new data after each compact.
The same template/parameter separation that makes logs cheap to feed an agent also makes them cheap to store. Real numbers from a single Apple Silicon laptop (Java 17, DuckDB 1.1.3); reproducible via benchmarks/run_all.sh.
| Stage | Size |
|---|---|
Source .log file |
7.30 MB |
After ingest (.duckdb) |
16.26 MB |
After compact (.duckdb + Parquet) |
1.71 MB |
| Reduction vs source | 76.5% |
Compression improves with log repetition — fewer distinct templates compress further as DuckDB's dictionary encoding on template_id gets more efficient.
| Log file | Original | After compact | Reduction |
|---|---|---|---|
app_logs.log (100k lines) |
7.35 MB | 1.49 MB | 80% |
app.log (100k lines) |
9.10 MB | 1.71 MB | 81% |
Structure-first — logs become templates + parameters + distributions, queryable as data and consumable by agents as a few thousand tokens instead of millions of raw lines.
Lossless — every line is exactly reconstructable. Multi-line entries (stack traces, Caused by: chains) are stored and replayed intact.
Zero configuration — no per-dataset regex patterns. Universal syntax is masked deterministically; everything else that varies is learned from the data itself.
Queryable after compression — compact replaces tables with DuckDB views over Parquet. All query commands work without decompressing anything.
Transparent — a preprocessing layer. Feed it logs, query them back. No changes to your application or log format.
Pull requests welcome. Before submitting:
mvn clean test # all tests must passThe suite covers masking and learning correctness (split-vs-merge decisions, byte-exact round-trips through merged templates, determinism), the end-to-end pipeline, lossless reconstruction including multi-line entries, parameter filtering, and temporal ordering. For new features, add integration tests in src/test/java/com/logslim/integration/ that verify the invariants that matter most: losslessness, correct grouping, and temporal order. Open an issue first for large changes.
MIT — see LICENSE.
