Production-ready TypeScript/Node.js streaming log template miner — a 1:1 port of the official Python Drain3 algorithm with zero runtime dependencies and cross-runtime support.
npm install @agentix-e/drain-ts
pnpm add @agentix-e/drain-ts
Node.js ≥ 22
drain-ts turns unstructured log messages into structured templates — in real time, with zero training. Given a stream of logs like "connection from 192.168.1.1 port 8080", it automatically identifies that the IP and port are variables, producing the template "connection from <IP> port <NUM>".
It implements the Drain algorithm (He et al., ICWS 2017) — the best-performing rule-based log parser in the LogPAI benchmark suite — using a fixed-depth prefix tree for efficient O((d+cm)n) online clustering.
This is the only TypeScript implementation with full Drain3 v0.9.11 feature parity: tree search, fast match, LRU eviction, inference mode, parameter extraction, and framework-agnostic persistence.
Process logs one at a time. No batching, no pre-training, no warm-up. Templates evolve automatically as new patterns appear.
treeSearch, fastMatch, addSeqToPrefixTree, LRU eviction, match() inference, extractParameters() — all 1:1 with the Python reference.
No runtime dependencies. No mandatory peer deps. Just TypeScript and your platform's standard library.
Node.js ≥22, Deno ≥2, Bun ≥1, and modern browsers (ES2020+). Same API everywhere.
Full TypeScript with strict mode, exactOptionalPropertyTypes, and noUncheckedIndexedAccess. Zero any types in the entire codebase.
Save/load model state to File, Memory, Redis, Kafka, S3 — or implement the 2-method interface for any backend.
import { TemplateMiner, TemplateMinerConfig, DEFAULT_MASKING_INSTRUCTIONS } from "@agentix-e/drain-ts";
const miner = new TemplateMiner({
config: TemplateMinerConfig.from({ maskingInstructions: DEFAULT_MASKING_INSTRUCTIONS }),
});
miner.addLogMessage("connection from 192.168.1.1 port 8080");
miner.addLogMessage("connection from 10.0.0.1 port 443");
// Both mapped to: "connection from <IP> port <NUM>"
// Extract the actual values:
const params = miner.extractParameters(
"connection from <IP> port <NUM>",
"connection from 192.168.1.1 port 8080"
);
// [{ value: "192.168.1.1", maskName: "IP" }, { value: "8080", maskName: "NUM" }]
import { TemplateMiner, FilePersistence } from "@agentix-e/drain-ts";
const miner = new TemplateMiner({
persistenceHandler: new FilePersistence("./state.json"),
});
// State auto-saves on every template change
// On restart, model loads from state.json automatically
| Feature | Drain3 (Python) | drain-ts (This Project) |
|---|---|---|
| Fixed-depth prefix tree | ✅ | ✅ 1:1 port |
| Similarity-based clustering | ✅ | ✅ Same formula |
| Template incremental update | ✅ | ✅ Same algorithm |
| LRU eviction (maxClusters) | ✅ | ✅ Same policy |
| match() inference mode | ✅ 3 strategies | ✅ 3 strategies |
| extractParameters() | ✅ | ✅ Exact + inexact |
| LogMasker | ✅ | ✅ Sequential replacement |
| Persistence (File/Redis/Kafka) | ✅ kafka-python, redis | ✅ framework-agnostic interface |
| Profiler | ✅ | ✅ Same section names |
| simTh validation | ❌ | ✅ 0–1 range check |
| Async factory | ❌ | ✅ TemplateMiner.create() |
| compactTree() | ❌ | ✅ Cleanup after LRU eviction |
| Zero runtime deps | ❌ pip install drain3 | ✅ npm install (no deps) |
| Type safety | ❌ Dynamic | ✅ Strict TypeScript |
| Metric | Value |
|---|---|
| Tests | 548 (unit + integration + benchmark) |
| Coverage | View full report → |
| Benchmark | Loghub 2k — 16 datasets → |
| TypeScript | Strict mode, zero errors |
| Throughput | ~420,000 logs/sec (Node 22) |