mdbkit 0.2.0__tar.gz → 0.3.0__tar.gz

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.
Files changed (69) hide show
  1. {mdbkit-0.2.0/mdbkit.egg-info → mdbkit-0.3.0}/PKG-INFO +195 -10
  2. mdbkit-0.2.0/PKG-INFO → mdbkit-0.3.0/README.md +738 -577
  3. {mdbkit-0.2.0 → mdbkit-0.3.0}/mdbkit/__init__.py +1 -1
  4. {mdbkit-0.2.0 → mdbkit-0.3.0}/mdbkit/analysis.py +117 -7
  5. mdbkit-0.3.0/mdbkit/build/lib/mdbkit/__init__.py +3 -0
  6. mdbkit-0.3.0/mdbkit/build/lib/mdbkit/advisor.py +319 -0
  7. mdbkit-0.3.0/mdbkit/build/lib/mdbkit/analysis.py +567 -0
  8. {mdbkit-0.2.0 → mdbkit-0.3.0/mdbkit/build/lib}/mdbkit/cli.py +214 -5
  9. mdbkit-0.3.0/mdbkit/build/lib/mdbkit/demo.py +401 -0
  10. {mdbkit-0.2.0 → mdbkit-0.3.0/mdbkit/build/lib}/mdbkit/ftdc.py +263 -75
  11. mdbkit-0.3.0/mdbkit/build/lib/mdbkit/lab.py +385 -0
  12. {mdbkit-0.2.0 → mdbkit-0.3.0/mdbkit/build/lib}/mdbkit/parser.py +7 -0
  13. {mdbkit-0.2.0 → mdbkit-0.3.0/mdbkit/build/lib}/mdbkit/rebuild.py +23 -9
  14. {mdbkit-0.2.0 → mdbkit-0.3.0/mdbkit/build/lib}/mdbkit/render.py +40 -3
  15. {mdbkit-0.2.0 → mdbkit-0.3.0/mdbkit/build/lib}/mdbkit/triage.py +126 -6
  16. mdbkit-0.3.0/mdbkit/cli.py +655 -0
  17. mdbkit-0.3.0/mdbkit/demo.py +401 -0
  18. mdbkit-0.3.0/mdbkit/explain.py +281 -0
  19. mdbkit-0.3.0/mdbkit/filtering.py +93 -0
  20. mdbkit-0.3.0/mdbkit/ftdc.py +654 -0
  21. mdbkit-0.3.0/mdbkit/lab.py +385 -0
  22. mdbkit-0.3.0/mdbkit/mdbkit/__init__.py +3 -0
  23. mdbkit-0.3.0/mdbkit/mdbkit/advisor.py +319 -0
  24. mdbkit-0.3.0/mdbkit/mdbkit/analysis.py +567 -0
  25. mdbkit-0.3.0/mdbkit/mdbkit/cli.py +655 -0
  26. mdbkit-0.3.0/mdbkit/mdbkit/demo.py +401 -0
  27. mdbkit-0.3.0/mdbkit/mdbkit/explain.py +281 -0
  28. mdbkit-0.3.0/mdbkit/mdbkit/filtering.py +93 -0
  29. mdbkit-0.3.0/mdbkit/mdbkit/ftdc.py +654 -0
  30. mdbkit-0.3.0/mdbkit/mdbkit/lab.py +385 -0
  31. mdbkit-0.3.0/mdbkit/mdbkit/parser.py +156 -0
  32. mdbkit-0.3.0/mdbkit/mdbkit/rebuild.py +185 -0
  33. mdbkit-0.3.0/mdbkit/mdbkit/render.py +336 -0
  34. mdbkit-0.3.0/mdbkit/mdbkit/report.py +187 -0
  35. mdbkit-0.3.0/mdbkit/mdbkit/scripts.py +69 -0
  36. mdbkit-0.3.0/mdbkit/mdbkit/triage.py +810 -0
  37. mdbkit-0.3.0/mdbkit/parser.py +156 -0
  38. mdbkit-0.3.0/mdbkit/rebuild.py +185 -0
  39. mdbkit-0.3.0/mdbkit/render.py +336 -0
  40. mdbkit-0.3.0/mdbkit/report.py +187 -0
  41. mdbkit-0.3.0/mdbkit/scripts.py +69 -0
  42. mdbkit-0.3.0/mdbkit/tests/test_demo_lab.py +315 -0
  43. {mdbkit-0.2.0 → mdbkit-0.3.0/mdbkit}/tests/test_ftdc.py +54 -0
  44. {mdbkit-0.2.0 → mdbkit-0.3.0/mdbkit}/tests/test_rebuild_report.py +35 -0
  45. mdbkit-0.3.0/mdbkit/triage.py +810 -0
  46. mdbkit-0.2.0/README.md → mdbkit-0.3.0/mdbkit.egg-info/PKG-INFO +762 -553
  47. mdbkit-0.3.0/mdbkit.egg-info/SOURCES.txt +66 -0
  48. {mdbkit-0.2.0 → mdbkit-0.3.0}/pyproject.toml +1 -1
  49. mdbkit-0.3.0/tests/test_demo_lab.py +315 -0
  50. mdbkit-0.3.0/tests/test_explain.py +119 -0
  51. mdbkit-0.3.0/tests/test_ftdc.py +320 -0
  52. mdbkit-0.3.0/tests/test_mdbkit.py +331 -0
  53. mdbkit-0.3.0/tests/test_rebuild_report.py +160 -0
  54. mdbkit-0.3.0/tests/test_triage.py +173 -0
  55. mdbkit-0.2.0/mdbkit.egg-info/SOURCES.txt +0 -27
  56. {mdbkit-0.2.0 → mdbkit-0.3.0}/LICENSE +0 -0
  57. {mdbkit-0.2.0 → mdbkit-0.3.0}/mdbkit/advisor.py +0 -0
  58. {mdbkit-0.2.0 → mdbkit-0.3.0/mdbkit/build/lib}/mdbkit/explain.py +0 -0
  59. {mdbkit-0.2.0 → mdbkit-0.3.0/mdbkit/build/lib}/mdbkit/filtering.py +0 -0
  60. {mdbkit-0.2.0 → mdbkit-0.3.0/mdbkit/build/lib}/mdbkit/report.py +0 -0
  61. {mdbkit-0.2.0 → mdbkit-0.3.0/mdbkit/build/lib}/mdbkit/scripts.py +0 -0
  62. {mdbkit-0.2.0 → mdbkit-0.3.0/mdbkit}/tests/test_explain.py +0 -0
  63. {mdbkit-0.2.0 → mdbkit-0.3.0/mdbkit}/tests/test_mdbkit.py +0 -0
  64. {mdbkit-0.2.0 → mdbkit-0.3.0/mdbkit}/tests/test_triage.py +0 -0
  65. {mdbkit-0.2.0 → mdbkit-0.3.0}/mdbkit.egg-info/dependency_links.txt +0 -0
  66. {mdbkit-0.2.0 → mdbkit-0.3.0}/mdbkit.egg-info/entry_points.txt +0 -0
  67. {mdbkit-0.2.0 → mdbkit-0.3.0}/mdbkit.egg-info/requires.txt +0 -0
  68. {mdbkit-0.2.0 → mdbkit-0.3.0}/mdbkit.egg-info/top_level.txt +0 -0
  69. {mdbkit-0.2.0 → mdbkit-0.3.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mdbkit
3
- Version: 0.2.0
3
+ Version: 0.3.0
4
4
  Summary: Offline toolkit for MongoDB 4.4+ structured logs: log analysis, slow-query shapes, connection churn, and deterministic index advice. A spiritual successor to mtools' log tools.
5
5
  Author: Saqib Ameen Subhan
6
6
  License: MIT
@@ -36,6 +36,25 @@ MongoDB 4.4 switched to structured JSON logging, and the beloved mtools log comm
36
36
 
37
37
  mdbkit fills that gap: a single, dependency-free CLI that turns structured logs into answers.
38
38
 
39
+ ## Try it in 30 seconds
40
+
41
+ No MongoDB required — `mdbkit demo` writes a realistic log with a real
42
+ incident in it:
43
+
44
+ ```bash
45
+ pip install mdbkit
46
+ mdbkit demo --with-extras -o demo.log
47
+
48
+ mdbkit loginfo demo.log
49
+ mdbkit queries demo.log
50
+ mdbkit triage demo.log --window 0 --no-sysprobe
51
+ mdbkit advise demo.log --indexes indexes.json --schema schema.json
52
+ ```
53
+
54
+ You will see a connection storm, a replica set election, an index build, and
55
+ a collection scan burning 47 million document reads — then the candidate
56
+ index that fixes it.
57
+
39
58
  ## Install
40
59
 
41
60
  **Recommended on any Linux/Mac/Windows:**
@@ -199,6 +218,8 @@ mdbkit triage mongod.log --no-sysprobe # analyzing a log copied off-host
199
218
  **Defaults to the last 60 minutes of log time**, because triage is for
200
219
  incidents happening now or just finished. In one command:
201
220
 
221
+ - **cluster health** — this node's replica set role, every peer's last known
222
+ state, heartbeat failures, and whether the node is still serving
202
223
  - restarts, error clusters, election/stepdown events
203
224
  - connection storms — with the peak minute and the top source IPs
204
225
  - slow-query volume and the peak minute, so you know *when* it hurt
@@ -211,8 +232,15 @@ incidents happening now or just finished. In one command:
211
232
  When run on the database host, mdbkit finds `dbPath` automatically — from the
212
233
  log's startup line, or the running `mongod` process, or `/etc/mongod.conf`,
213
234
  or common defaults — so the disk check works even when the current log has no
214
- startup event. All probing is stdlib-only (`/proc`, `statvfs`); no shell-outs,
215
- nothing leaves the machine.
235
+ startup event. **`diagnostic.data` lives inside the dbPath, so it is picked up
236
+ automatically too**: you only need `--ftdc` to point somewhere else, such as a
237
+ directory copied off another host. All probing is stdlib-only (`/proc`,
238
+ `statvfs`); no shell-outs, nothing leaves the machine.
239
+
240
+ Cluster health is derived entirely from the log — no connection to the
241
+ database. It reports what the node last said about itself and its peers, which
242
+ is the honest limit of an offline tool, and enough to answer "is this node
243
+ serving, and what does it think of the others?"
216
244
 
217
245
  Read-only: it never connects to the database, and every finding ends with a
218
246
  next step for a human to review. Detectors marked beta are pattern-matched and
@@ -295,7 +323,7 @@ query with different parameters is counted once.
295
323
  | `--limit N` | all | Show only the top N shapes |
296
324
  | `--min-ms N` | 0 | Ignore operations faster than N milliseconds |
297
325
  | `--include-system` | off | Include internal `admin`/`config`/`local` namespaces (hidden by default — they are server housekeeping, not your workload) |
298
- | `--report FILE` | | Write a shareable `.md` or `.html` report instead |
326
+ | `--report FILE` | | Write a shareable `.md` or `.html` report instead (see [Shareable reports](#shareable-reports----report-file)) |
299
327
  | `--json` | | Machine-readable output |
300
328
 
301
329
  **Reading the columns:**
@@ -319,13 +347,38 @@ mdbkit queries mongod.log --min-ms 500 --json
319
347
 
320
348
  ### `mdbkit connections <log>`
321
349
 
322
- Connection churn: totals, peak concurrent count, per-source-IP breakdown, and
323
- the client applications and drivers seen.
350
+ Connection churn and **who authenticated**: totals, peak concurrent count,
351
+ per-source-IP breakdown with first/last seen, the client applications and
352
+ drivers, and a per-user table.
324
353
 
325
354
  | Option | Description |
326
355
  |---|---|
327
356
  | `--json` | Machine-readable output |
328
357
 
358
+ ```bash
359
+ mdbkit connections mongod.log
360
+ ```
361
+
362
+ ```
363
+ source ip accepted ended first seen last seen appName
364
+ ---------- -------- ----- ------------------- ------------------- ------------
365
+ 10.20.9.77 220 0 2026-07-01 08:49:30 2026-07-01 08:49:30 checkout-api
366
+ 10.20.4.11 4 1 2026-07-01 08:00:15 2026-07-01 09:29:30 OrderService
367
+
368
+ authenticated users
369
+ user auth db ok failed last authenticated from
370
+ ------------ ------- --- ------ ------------------- -----------
371
+ svc_checkout admin 221 0 2026-07-01 08:49:30 10.20.9.77
372
+ etl_batch admin 0 5 2026-07-01 08:51:54 10.20.11.40
373
+
374
+ etl_batch: 5 failed authentication(s) — last error: AuthenticationFailed
375
+ ```
376
+
377
+ This answers the question that starts most access incidents: *did that
378
+ account connect, from where, and when last?* If the log shows no
379
+ authentication events at all, mdbkit says so — either auth is disabled, or
380
+ the window contains no new logins because clients are reusing connections.
381
+
329
382
  ---
330
383
 
331
384
  ### `mdbkit filter <log>`
@@ -500,11 +553,21 @@ not encrypted; mdbkit decodes it offline.
500
553
 
501
554
  | Option | Default | Description |
502
555
  |---|---|---|
556
+ | `--last DURATION` | `4h` | Analyze only the most recent window — `90m`, `4h`, `2d` |
557
+ | `--all` | off | Analyze the entire history (see the performance note below) |
503
558
  | `--metric LABEL` | all | Restrict to one metric (repeatable), e.g. `--metric conns.current` |
504
559
  | `--step SECONDS` | 60 | Timeline bucket size |
505
- | `--from` / `--to` | | Time bounds (same formats as `filter`) |
560
+ | `--from` / `--to` | | Explicit time bounds (same formats as `filter`) |
506
561
  | `--json` | | Machine-readable output |
507
562
 
563
+ **Performance note.** `diagnostic.data` can hold weeks of per-second samples —
564
+ a few hundred megabytes covering thousands of chunks and several thousand
565
+ metrics each. Decoding all of it is CPU-bound and takes minutes, so these
566
+ commands **default to the last 4 hours** and skip older chunks before
567
+ decompressing them. On a 250 MB directory that is the difference between about
568
+ a second and about a minute. Use `--last`/`--from`/`--to` to move the window,
569
+ and `--all` when you really do want the whole history.
570
+
508
571
  ```bash
509
572
  mdbkit ftdc summary /var/lib/mongodb/diagnostic.data
510
573
  mdbkit ftdc timeline diagnostic.data --metric conns.current --step 300
@@ -521,6 +584,126 @@ contains metrics only, never document contents.
521
584
 
522
585
  ---
523
586
 
587
+ ### Shareable reports — `--report FILE`
588
+
589
+ `triage` and `queries` can write a self-contained report instead of printing to
590
+ the terminal — for a ticket, a handover, or a post-incident review.
591
+
592
+ ```bash
593
+ mdbkit triage mongod.log --report incident.html # styled, self-contained
594
+ mdbkit triage mongod.log --report incident.md # for tickets and PRs
595
+ mdbkit queries mongod.log --limit 20 --report slow-queries.md
596
+ ```
597
+
598
+ The format follows the file extension: `.html` or `.md`.
599
+
600
+ Markdown output looks like this:
601
+
602
+ ```markdown
603
+ # MongoDB incident triage
604
+
605
+ *window 2026-07-01 08:10 -> 09:10 · generated 2026-07-01 09:12*
606
+
607
+ ## Findings
608
+
609
+ - **[CRIT] Replica set instability** — 3 election/stepdown event(s) at 08:41:02, 08:58:14
610
+ - Starting an election, since we've seen no PRIMARY in election timeout period
611
+ - *next:* `Correlate with connection storms and slow checkpoints below`
612
+ - **[WARN] Connection storm** — 2 minute(s) at >= 60 new connections/min; peak 480 at 08:41
613
+ - 10.2.1.7: 312 in the peak minute
614
+ - *next:* `mdbkit connections <log>`
615
+ - **[OK] Errors** — No error/fatal severity lines in window.
616
+ ```
617
+
618
+ The HTML version carries the same content with a dark, print-friendly
619
+ stylesheet. It is **fully self-contained**: inline CSS, no JavaScript, no
620
+ external assets or CDN references, so it opens on an air-gapped machine and
621
+ sends nothing anywhere.
622
+
623
+ Reports contain the same information as the terminal output — query **shapes**
624
+ and metrics, never literal values from your documents.
625
+
626
+ ---
627
+
628
+ ### `mdbkit demo`
629
+
630
+ Generates a realistic MongoDB structured log so you can evaluate mdbkit — or
631
+ run a live demo — without a cluster. Output is deterministic for a given
632
+ seed, so a demo behaves identically every time, including on a projector.
633
+
634
+ | Option | Default | Description |
635
+ |---|---|---|
636
+ | `--scenario` | `mixed` | `healthy`, `incident`, or `mixed` |
637
+ | `--minutes N` | 90 | How much log time to generate |
638
+ | `--seed N` | 7 | Same seed produces byte-identical output |
639
+ | `-o, --out FILE` | stdout | Write to a file |
640
+ | `--with-extras` | off | Also write `indexes.json`, `schema.json` and `explain.json` beside the log |
641
+
642
+ ```bash
643
+ mdbkit demo -o demo.log # 90 minutes, mixed
644
+ mdbkit demo --scenario incident --minutes 30 -o incident.log
645
+ mdbkit demo --scenario healthy -o quiet.log # nothing wrong: the control case
646
+ mdbkit demo | mdbkit queries - # straight down a pipe
647
+ ```
648
+
649
+ The `incident` scenario contains an index build, a connection storm from a
650
+ single client, a replica set election, plan-executor errors, a slow
651
+ WiredTiger checkpoint, and a burst of unindexed queries afterwards — the
652
+ shape of a real bad afternoon.
653
+
654
+ ---
655
+
656
+ ### `mdbkit lab`
657
+
658
+ Starts a **disposable local MongoDB** for testing, reproducing a slow query,
659
+ or rehearsing a demo. This is the only command that starts external
660
+ processes; see [SECURITY.md](SECURITY.md) for exactly how it is bounded.
661
+
662
+ Requires `mongod` on your `PATH` (and `mongosh` to initiate the replica set
663
+ and seed data). Linux and macOS.
664
+
665
+ | Action | What it does |
666
+ |---|---|
667
+ | `start` | Create and start a replica set, print the connection string and log paths |
668
+ | `seed` | Insert sample data and run a workload with deliberately interesting queries |
669
+ | `status` | Show ports, pids and whether each node is running |
670
+ | `logs` | Print the log file paths, ready to pipe into other commands |
671
+ | `stop` | Stop the nodes, keep the data |
672
+ | `destroy` | Stop and delete the lab (requires `--yes`) |
673
+
674
+ | Option | Default | Description |
675
+ |---|---|---|
676
+ | `--dir PATH` | `~/.mdbkit-lab` | Where the lab lives |
677
+ | `--nodes N` | 3 | Replica set size |
678
+ | `--port N` | 28110 | Base port — deliberately far from 27017 |
679
+ | `--slowms N` | 0 | Log every operation, which is what makes the log worth reading |
680
+ | `--standalone` | off | Single node, no replica set |
681
+ | `--docs N` | 50000 | Documents inserted by `seed` |
682
+ | `--yes` | | Confirm `destroy` |
683
+
684
+ **The full loop:**
685
+
686
+ ```bash
687
+ mdbkit lab start # 3-node replica set on 28110-28112
688
+ mdbkit lab seed # sample data + a mixed workload
689
+
690
+ mdbkit queries $(mdbkit lab logs | head -1)
691
+ mdbkit advise $(mdbkit lab logs | head -1)
692
+
693
+ mdbkit lab destroy --yes # remove everything
694
+ ```
695
+
696
+ `seed` runs indexed point lookups alongside deliberately unindexed queries —
697
+ an equality-plus-range-plus-sort with no supporting index, an aggregation
698
+ that scans the collection, and updates whose predicate has no index — so the
699
+ log immediately contains something worth analysing.
700
+
701
+ **Safety.** The lab binds to `127.0.0.1` only, refuses to use or delete any
702
+ directory it did not create, and never touches a MongoDB it did not start.
703
+ It is a laptop and scratch-VM tool, not a deployment tool.
704
+
705
+ ---
706
+
524
707
  ### `mdbkit export-script {schema|indexes}`
525
708
 
526
709
  Prints a small `mongosh` script to stdout. **mdbkit never connects to your
@@ -540,12 +723,14 @@ mdbkit export-script schema > export_schema.js
540
723
  Terminal output is and will remain first-class — this tool is built for the
541
724
  Linux box the database actually runs on.
542
725
 
543
- **Shipped in v0.2:** FTDC decoding, incident triage with system metrics,
544
- query reconstruction (`--as-explain`), and shareable Markdown/HTML reports.
726
+ **Shipped in v0.3:** `demo` log generation and `lab` disposable clusters, on
727
+ top of v0.2's FTDC decoding, incident triage, query reconstruction and
728
+ shareable reports.
545
729
 
546
730
  Next up:
731
+ * `mdbkit compare before.log after.log` — did the index actually help?
732
+ * Multiple log files and globs in one command, for rotated logs.
547
733
  * Per-shape drill-down (`mdbkit queries --shape N` with full detail).
548
- * `serverStatus` snapshot digest for triage (two snapshots for true rates).
549
734
  * Graduating the remaining beta detectors (checkpoints, eviction, flow
550
735
  control) once validated against real incident logs — see
551
736
  `docs/TESTING-PLAYBOOK.md`. Real logs very welcome.