@closeup1202/klag 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -13
- package/dist/cli/index.js +46 -14
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> Know **why** your Kafka consumer lag is growing — in 5 seconds from the terminal
|
|
4
4
|
|
|
5
|
-
[](https://www.npmjs.com/package/@closeup1202/klag)
|
|
6
6
|
[](https://opensource.org/licenses/MIT)
|
|
7
7
|
|
|
8
8
|
## Compared to existing tools
|
|
@@ -21,21 +21,21 @@ npx @closeup1202/klag --broker localhost:9092 --group my-service
|
|
|
21
21
|
|
|
22
22
|
## Output example
|
|
23
23
|
```
|
|
24
|
-
⚡ klag
|
|
24
|
+
⚡ klag 0.3.0
|
|
25
25
|
|
|
26
26
|
🔍 Consumer Group: my-service
|
|
27
27
|
Broker: localhost:9092
|
|
28
|
-
Collected At: 2026-03-
|
|
28
|
+
Collected At: 2026-03-28 17:27:27 (Asia/Seoul)
|
|
29
29
|
|
|
30
|
-
Group Status :
|
|
30
|
+
Group Status : 🚨 CRITICAL Total Lag : 1,234 Drain : ∞
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
│ Topic │ Partition │ Committed Offset │ Log-End Offset │ Lag │ Status │ Produce Rate │ Consume Rate │
|
|
34
|
-
|
|
35
|
-
│ orders │ 0 │ 8,796 │ 10,000 │ 1,204 │ 🔴 HIGH │ 40.0 msg/s │ 0.0 msg/s │
|
|
36
|
-
│ orders │ 1 │ 9,988 │ 10,000 │ 12 │ 🟢 OK │ 0.0 msg/s │ 0.0 msg/s │
|
|
37
|
-
│ orders │ 2 │ 9,982 │ 10,000 │ 18 │ 🟢 OK │ 0.0 msg/s │ 0.0 msg/s │
|
|
38
|
-
|
|
32
|
+
┌────────┬───────────┬──────────────────┬────────────────┬───────┬─────────┬──────┬──────────────┬──────────────┐
|
|
33
|
+
│ Topic │ Partition │ Committed Offset │ Log-End Offset │ Lag │ Status │ Drain│ Produce Rate │ Consume Rate │
|
|
34
|
+
├────────┼───────────┼──────────────────┼────────────────┼───────┼─────────┼──────┼──────────────┼──────────────┤
|
|
35
|
+
│ orders │ 0 │ 8,796 │ 10,000 │ 1,204 │ 🔴 HIGH │ ∞ │ 40.0 msg/s │ 0.0 msg/s │
|
|
36
|
+
│ orders │ 1 │ 9,988 │ 10,000 │ 12 │ 🟢 OK │ — │ 0.0 msg/s │ 0.0 msg/s │
|
|
37
|
+
│ orders │ 2 │ 9,982 │ 10,000 │ 18 │ 🟢 OK │ — │ 0.0 msg/s │ 0.0 msg/s │
|
|
38
|
+
└────────┴───────────┴──────────────────┴────────────────┴───────┴─────────┴──────┴──────────────┴──────────────┘
|
|
39
39
|
|
|
40
40
|
🔎 Root Cause Analysis
|
|
41
41
|
[PRODUCER_BURST] orders
|
|
@@ -156,8 +156,9 @@ All consumption pauses during rebalancing, which can cause a temporary lag spike
|
|
|
156
156
|
|
|
157
157
|
- [x] v0.1.0 — lag collection, hot partition, producer burst, slow consumer, rebalancing detection, watch mode with lag trend (▲▼)
|
|
158
158
|
- [x] v0.2.0 — SSL/SASL authentication, `.klagrc` config file support
|
|
159
|
-
- [
|
|
160
|
-
- [ ] v0.4.0 —
|
|
159
|
+
- [x] v0.3.0 — time-to-drain severity classification, Drain column per partition
|
|
160
|
+
- [ ] v0.4.0 — multi-group monitoring
|
|
161
|
+
- [ ] v0.5.0 — Slack alerts, Prometheus export
|
|
161
162
|
|
|
162
163
|
## License
|
|
163
164
|
|
package/dist/cli/index.js
CHANGED
|
@@ -363,12 +363,30 @@ import chalk from "chalk";
|
|
|
363
363
|
import Table from "cli-table3";
|
|
364
364
|
|
|
365
365
|
// src/types/index.ts
|
|
366
|
-
var VERSION = "0.
|
|
367
|
-
function classifyLag(lag) {
|
|
368
|
-
if (lag
|
|
369
|
-
if (
|
|
366
|
+
var VERSION = "0.3.0";
|
|
367
|
+
function classifyLag(lag, consumeRate) {
|
|
368
|
+
if (lag === 0n) return "OK";
|
|
369
|
+
if (consumeRate !== void 0) {
|
|
370
|
+
if (consumeRate === 0) return "HIGH";
|
|
371
|
+
const drainSec = Number(lag) / consumeRate;
|
|
372
|
+
if (drainSec < 60) return "OK";
|
|
373
|
+
if (drainSec < 300) return "WARN";
|
|
374
|
+
return "HIGH";
|
|
375
|
+
}
|
|
376
|
+
if (lag < 10000n) return "OK";
|
|
377
|
+
if (lag < 100000n) return "WARN";
|
|
370
378
|
return "HIGH";
|
|
371
379
|
}
|
|
380
|
+
function formatDrainTime(lag, consumeRate) {
|
|
381
|
+
if (lag === 0n) return "\u2014";
|
|
382
|
+
if (consumeRate === 0) return "\u221E";
|
|
383
|
+
const sec = Math.ceil(Number(lag) / consumeRate);
|
|
384
|
+
if (sec < 60) return `${sec}s`;
|
|
385
|
+
const m = Math.floor(sec / 60);
|
|
386
|
+
const s = sec % 60;
|
|
387
|
+
if (sec < 3600) return s > 0 ? `${m}m${s}s` : `${m}m`;
|
|
388
|
+
return `>${Math.floor(sec / 3600)}h`;
|
|
389
|
+
}
|
|
372
390
|
|
|
373
391
|
// src/reporter/tableReporter.ts
|
|
374
392
|
var LEVEL_ICON = {
|
|
@@ -388,8 +406,8 @@ function formatTrend(lagDiff) {
|
|
|
388
406
|
if (lagDiff > 0n) return chalk.red(`\u25B2 +${lagDiff.toLocaleString()}`);
|
|
389
407
|
return chalk.green(`\u25BC ${lagDiff.toLocaleString()}`);
|
|
390
408
|
}
|
|
391
|
-
function groupStatus(totalLag) {
|
|
392
|
-
const level = classifyLag(totalLag);
|
|
409
|
+
function groupStatus(totalLag, totalConsumeRate) {
|
|
410
|
+
const level = classifyLag(totalLag, totalConsumeRate);
|
|
393
411
|
if (level === "OK") return chalk.green("\u2705 OK");
|
|
394
412
|
if (level === "WARN") return chalk.yellow("\u26A0\uFE0F WARNING");
|
|
395
413
|
return chalk.red("\u{1F6A8} CRITICAL");
|
|
@@ -418,18 +436,25 @@ function printLagTable(snapshot, rcaResults = [], rateSnapshot, watchMode = fals
|
|
|
418
436
|
chalk.bold(" Collected At: ") + chalk.gray(`${localTime} (${tz})`)
|
|
419
437
|
);
|
|
420
438
|
console.log("");
|
|
421
|
-
const status = groupStatus(totalLag);
|
|
422
|
-
const totalStr = chalk.bold(formatLag(totalLag));
|
|
423
|
-
console.log(` Group Status : ${status} Total Lag : ${totalStr}`);
|
|
424
|
-
console.log("");
|
|
425
439
|
const hasRate = !!rateSnapshot && rateSnapshot.partitions.length > 0;
|
|
426
440
|
const hasTrend = watchMode;
|
|
427
441
|
const rateMap = /* @__PURE__ */ new Map();
|
|
442
|
+
let totalConsumeRate;
|
|
428
443
|
if (hasRate && rateSnapshot) {
|
|
444
|
+
let sum = 0;
|
|
429
445
|
for (const r of rateSnapshot.partitions) {
|
|
430
446
|
rateMap.set(`${r.topic}-${r.partition}`, r);
|
|
447
|
+
sum += r.consumeRate;
|
|
431
448
|
}
|
|
449
|
+
totalConsumeRate = sum;
|
|
432
450
|
}
|
|
451
|
+
const status = groupStatus(totalLag, totalConsumeRate);
|
|
452
|
+
const totalStr = chalk.bold(formatLag(totalLag));
|
|
453
|
+
const drainStr = totalConsumeRate !== void 0 ? ` Drain : ${chalk.cyan(formatDrainTime(totalLag, totalConsumeRate))}` : "";
|
|
454
|
+
console.log(
|
|
455
|
+
` Group Status : ${status} Total Lag : ${totalStr}${drainStr}`
|
|
456
|
+
);
|
|
457
|
+
console.log("");
|
|
433
458
|
const head = [
|
|
434
459
|
chalk.bold("Topic"),
|
|
435
460
|
chalk.bold("Partition"),
|
|
@@ -438,7 +463,11 @@ function printLagTable(snapshot, rcaResults = [], rateSnapshot, watchMode = fals
|
|
|
438
463
|
chalk.bold("Lag"),
|
|
439
464
|
...hasTrend ? [chalk.bold("Trend")] : [],
|
|
440
465
|
chalk.bold("Status"),
|
|
441
|
-
...hasRate ? [
|
|
466
|
+
...hasRate ? [
|
|
467
|
+
chalk.bold("Drain"),
|
|
468
|
+
chalk.bold("Produce Rate"),
|
|
469
|
+
chalk.bold("Consume Rate")
|
|
470
|
+
] : []
|
|
442
471
|
];
|
|
443
472
|
const table = new Table({
|
|
444
473
|
head,
|
|
@@ -450,16 +479,19 @@ function printLagTable(snapshot, rcaResults = [], rateSnapshot, watchMode = fals
|
|
|
450
479
|
"right",
|
|
451
480
|
...hasTrend ? ["right"] : [],
|
|
452
481
|
"center",
|
|
453
|
-
...hasRate ? ["right", "right"] : []
|
|
482
|
+
...hasRate ? ["right", "right", "right"] : []
|
|
454
483
|
],
|
|
455
484
|
style: { head: [], border: ["grey"] }
|
|
456
485
|
});
|
|
457
486
|
let lastTopic = "";
|
|
458
487
|
for (const p of partitions) {
|
|
459
|
-
const level = classifyLag(p.lag);
|
|
460
|
-
const lagStr = level === "HIGH" ? chalk.red(formatLag(p.lag)) : level === "WARN" ? chalk.yellow(formatLag(p.lag)) : chalk.green(formatLag(p.lag));
|
|
461
488
|
const rateEntry = rateMap.get(`${p.topic}-${p.partition}`);
|
|
489
|
+
const level = classifyLag(p.lag, rateEntry?.consumeRate);
|
|
490
|
+
const lagStr = level === "HIGH" ? chalk.red(formatLag(p.lag)) : level === "WARN" ? chalk.yellow(formatLag(p.lag)) : chalk.green(formatLag(p.lag));
|
|
462
491
|
const rateColumns = hasRate ? [
|
|
492
|
+
chalk.cyan(
|
|
493
|
+
rateEntry !== void 0 ? formatDrainTime(p.lag, rateEntry.consumeRate) : "\u2014"
|
|
494
|
+
),
|
|
463
495
|
chalk.yellow(formatRate(rateEntry?.produceRate ?? 0)),
|
|
464
496
|
chalk.cyan(formatRate(rateEntry?.consumeRate ?? 0))
|
|
465
497
|
] : [];
|