@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 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
- [![npm version](https://badge.fury.io/js/klag.svg)](https://www.npmjs.com/package/klag)
5
+ [![npm version](https://badge.fury.io/js/%40closeup1202%2Fklag.svg)](https://www.npmjs.com/package/@closeup1202/klag)
6
6
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](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 v0.1.0
24
+ ⚡ klag 0.3.0
25
25
 
26
26
  🔍 Consumer Group: my-service
27
27
  Broker: localhost:9092
28
- Collected At: 2026-03-26 17:27:27 (Asia/Seoul)
28
+ Collected At: 2026-03-28 17:27:27 (Asia/Seoul)
29
29
 
30
- Group Status : ⚠️ WARNING Total Lag : 1,234
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
- - [ ] v0.3.0 — multi-group monitoring
160
- - [ ] v0.4.0 — Slack alerts, Prometheus export
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.2.0";
367
- function classifyLag(lag) {
368
- if (lag < 100n) return "OK";
369
- if (lag < 1000n) return "WARN";
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 ? [chalk.bold("Produce Rate"), chalk.bold("Consume Rate")] : []
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
  ] : [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@closeup1202/klag",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Kafka consumer lag root cause analyzer",
5
5
  "type": "module",
6
6
  "bin": {