logwhisperer 0.2.2__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 (40) hide show
  1. logwhisperer-0.2.2/LICENSE +21 -0
  2. logwhisperer-0.2.2/PKG-INFO +647 -0
  3. logwhisperer-0.2.2/README.md +614 -0
  4. logwhisperer-0.2.2/man/log-whisperer.1 +581 -0
  5. logwhisperer-0.2.2/pyproject.toml +51 -0
  6. logwhisperer-0.2.2/setup.cfg +4 -0
  7. logwhisperer-0.2.2/src/logwhisperer/__init__.py +3 -0
  8. logwhisperer-0.2.2/src/logwhisperer/cli.py +130 -0
  9. logwhisperer-0.2.2/src/logwhisperer/core.py +199 -0
  10. logwhisperer-0.2.2/src/logwhisperer/normalize.py +59 -0
  11. logwhisperer-0.2.2/src/logwhisperer/notify/__init__.py +1 -0
  12. logwhisperer-0.2.2/src/logwhisperer/notify/dispatch.py +67 -0
  13. logwhisperer-0.2.2/src/logwhisperer/notify/email_smtp.py +36 -0
  14. logwhisperer-0.2.2/src/logwhisperer/notify/ntfy.py +17 -0
  15. logwhisperer-0.2.2/src/logwhisperer/notify/telegram.py +13 -0
  16. logwhisperer-0.2.2/src/logwhisperer/paths.py +19 -0
  17. logwhisperer-0.2.2/src/logwhisperer/report.py +72 -0
  18. logwhisperer-0.2.2/src/logwhisperer/severity.py +42 -0
  19. logwhisperer-0.2.2/src/logwhisperer/sources/__init__.py +39 -0
  20. logwhisperer-0.2.2/src/logwhisperer/sources/_subprocess.py +25 -0
  21. logwhisperer-0.2.2/src/logwhisperer/sources/compose.py +19 -0
  22. logwhisperer-0.2.2/src/logwhisperer/sources/docker.py +13 -0
  23. logwhisperer-0.2.2/src/logwhisperer/sources/file.py +18 -0
  24. logwhisperer-0.2.2/src/logwhisperer/sources/journal.py +16 -0
  25. logwhisperer-0.2.2/src/logwhisperer/state.py +188 -0
  26. logwhisperer-0.2.2/src/logwhisperer.egg-info/PKG-INFO +647 -0
  27. logwhisperer-0.2.2/src/logwhisperer.egg-info/SOURCES.txt +38 -0
  28. logwhisperer-0.2.2/src/logwhisperer.egg-info/dependency_links.txt +1 -0
  29. logwhisperer-0.2.2/src/logwhisperer.egg-info/entry_points.txt +2 -0
  30. logwhisperer-0.2.2/src/logwhisperer.egg-info/requires.txt +4 -0
  31. logwhisperer-0.2.2/src/logwhisperer.egg-info/top_level.txt +1 -0
  32. logwhisperer-0.2.2/tests/test_cli.py +143 -0
  33. logwhisperer-0.2.2/tests/test_core.py +227 -0
  34. logwhisperer-0.2.2/tests/test_dispatch.py +94 -0
  35. logwhisperer-0.2.2/tests/test_normalize.py +119 -0
  36. logwhisperer-0.2.2/tests/test_report.py +111 -0
  37. logwhisperer-0.2.2/tests/test_severity.py +53 -0
  38. logwhisperer-0.2.2/tests/test_sources.py +121 -0
  39. logwhisperer-0.2.2/tests/test_state.py +239 -0
  40. logwhisperer-0.2.2/tests/test_subprocess.py +38 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Nao Intelligence GmbH
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,647 @@
1
+ Metadata-Version: 2.4
2
+ Name: logwhisperer
3
+ Version: 0.2.2
4
+ Summary: CLI log pattern analysis and anomaly detection tool
5
+ Author-email: Nao Intelligence GmbH <pranto@naointelligence.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/naointelligence/logwhisperer
8
+ Project-URL: Repository, https://github.com/naointelligence/logwhisperer
9
+ Project-URL: Issues, https://github.com/naointelligence/logwhisperer/issues
10
+ Keywords: logging,log-analysis,pattern-detection,anomaly-detection,cli,devops,monitoring
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Environment :: Console
13
+ Classifier: Intended Audience :: System Administrators
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Operating System :: POSIX :: Linux
16
+ Classifier: Operating System :: MacOS
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.9
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Programming Language :: Python :: 3.13
23
+ Classifier: Topic :: System :: Logging
24
+ Classifier: Topic :: System :: Monitoring
25
+ Classifier: Topic :: System :: Systems Administration
26
+ Requires-Python: >=3.9
27
+ Description-Content-Type: text/markdown
28
+ License-File: LICENSE
29
+ Requires-Dist: requests
30
+ Provides-Extra: dev
31
+ Requires-Dist: pytest; extra == "dev"
32
+ Dynamic: license-file
33
+
34
+ <h1 align="center">
35
+ <br>
36
+ LogWhisperer
37
+ <br>
38
+ </h1>
39
+
40
+ <p align="center">
41
+ <b>CLI log pattern analysis &amp; anomaly detection tool</b><br>
42
+ <a href="https://pypi.org/project/logwhisperer/"><img src="https://img.shields.io/pypi/v/logwhisperer" alt="PyPI"></a>
43
+ <a href="https://pypi.org/project/logwhisperer/"><img src="https://img.shields.io/pypi/pyversions/logwhisperer" alt="Python versions"></a>
44
+ <a href="https://github.com/naointelligence/logwhisperer/blob/main/LICENSE"><img src="https://img.shields.io/pypi/l/logwhisperer" alt="License"></a>
45
+ </p>
46
+
47
+ <p align="center">
48
+ <a href="#installation">Installation</a> &bull;
49
+ <a href="#quick-start">Quick Start</a> &bull;
50
+ <a href="#sources">Sources</a> &bull;
51
+ <a href="#pattern-engine">Pattern Engine</a> &bull;
52
+ <a href="#baseline-learning">Baseline Learning</a> &bull;
53
+ <a href="#notifications">Notifications</a> &bull;
54
+ <a href="#recipes">Recipes</a>
55
+ </p>
56
+
57
+ ---
58
+
59
+ ## What Is LogWhisperer?
60
+
61
+ LogWhisperer reads logs from **multiple sources** (Docker containers, Compose
62
+ services, systemd journals, plain files), **normalizes** variable data out of
63
+ each line (IPs, UUIDs, timestamps, paths, numbers), and **clusters** them into
64
+ patterns. It remembers every pattern it has ever seen and tells you which ones
65
+ are **new**.
66
+
67
+ ```
68
+ ┌───────────┐
69
+ docker logs ──>│ │ normalize cluster diff
70
+ compose ──>│ Sources │──> "ERROR <IP>" ──> pattern A ──> [NEW]
71
+ journalctl ──>│ │ "INFO <UUID>" pattern B [seen]
72
+ plain file ──>└───────────┘ pattern C [NEW]
73
+
74
+ ┌───────────────┼───────────────┐
75
+ ▼ ▼ ▼
76
+ patterns.db text/JSON alerts
77
+ (JSON-lines) report (ntfy/tg/email)
78
+ ```
79
+
80
+ ---
81
+
82
+ ## Installation
83
+
84
+ ### From PyPI (recommended)
85
+
86
+ ```bash
87
+ pip install logwhisperer
88
+ ```
89
+
90
+ Or with [pipx](https://pipx.pypa.io/) for an isolated install:
91
+
92
+ ```bash
93
+ pipx install logwhisperer
94
+ ```
95
+
96
+ ### From source (development)
97
+
98
+ ```bash
99
+ git clone https://github.com/naointelligence/logwhisperer.git
100
+ cd logwhisperer
101
+ python3 -m venv .venv
102
+ source .venv/bin/activate
103
+ pip install -e ".[dev]"
104
+ ```
105
+
106
+ ### Verify
107
+
108
+ ```bash
109
+ log-whisperer --help
110
+ ```
111
+
112
+ ---
113
+
114
+ ## Quick Start
115
+
116
+ **1. Analyse a log file**
117
+
118
+ ```bash
119
+ log-whisperer --file /var/log/syslog --show-samples
120
+ ```
121
+
122
+ **2. See only never-before-seen patterns**
123
+
124
+ ```bash
125
+ log-whisperer --file /var/log/syslog --show-new
126
+ ```
127
+
128
+ **3. First run shows `[NEW]`, second run shows `[seen]`**
129
+
130
+ ```bash
131
+ $ log-whisperer --file app.log
132
+ [NEW][ERROR] x12 total=12 ERROR something failed with code <N>
133
+ [NEW][INFO] x841 total=841 INFO request from <IP> processed in <N>ms
134
+
135
+ $ log-whisperer --file app.log
136
+ [seen][ERROR] x12 total=24 ERROR something failed with code <N>
137
+ [seen][INFO] x841 total=1682 INFO request from <IP> processed in <N>ms
138
+ ```
139
+
140
+ ---
141
+
142
+ ## Usage
143
+
144
+ ```
145
+ log-whisperer [SOURCE] [OPTIONS]
146
+ ```
147
+
148
+ ### Source Flags
149
+
150
+ > Exactly **one** source must be specified (unless using `--reset`).
151
+
152
+ | Flag | Description | Example |
153
+ |:-----|:------------|:--------|
154
+ | `--file PATH` | Read from a plain text file | `--file /var/log/app.log` |
155
+ | `--docker NAME` | Read from a Docker container | `--docker my-api` |
156
+ | `--compose SERVICE` | Read from a Compose service | `--compose web` |
157
+ | `--compose-all` | Read from **all** Compose services | `--compose-all` |
158
+ | `--service UNIT` | Read from a systemd journal unit | `--service nginx` |
159
+
160
+ ### Display Options
161
+
162
+ | Flag | Default | Description |
163
+ |:-----|:--------|:------------|
164
+ | `--show-samples` | off | Print one raw sample line per pattern |
165
+ | `--show-new` | off | Only display patterns never seen before |
166
+ | `--min-severity` | `INFO` | Filter output: `INFO`, `WARN`, or `ERROR` |
167
+ | `--json` | off | Output the full report as JSON |
168
+
169
+ ### Window Options
170
+
171
+ | Flag | Default | Description |
172
+ |:-----|:--------|:------------|
173
+ | `--since` | `1h` | Time window passed to docker/journalctl (e.g. `10m`, `1h`, `today`) |
174
+ | `--lines` | `5000` | Maximum number of lines to process |
175
+
176
+ ### State Management
177
+
178
+ | Flag | Default | Description |
179
+ |:-----|:--------|:------------|
180
+ | `--state-db PATH` | `~/.local/state/logwhisperer/patterns.db` | Pattern database file |
181
+ | `--baseline-state PATH` | `~/.local/state/logwhisperer/baseline.json` | Baseline state file |
182
+ | `--baseline-learn DURATION` | — | Enter baseline learning mode (e.g. `24h`, `30m`) |
183
+ | `--reset` | off | Delete the pattern DB and baseline state |
184
+
185
+ ### Notification Flags
186
+
187
+ | Flag | Env Variable | Description |
188
+ |:-----|:-------------|:------------|
189
+ | `--notify-ntfy-topic` | `LOGWHISPERER_NTFY_TOPIC` | ntfy topic name |
190
+ | `--notify-ntfy-server` | `LOGWHISPERER_NTFY_SERVER` | ntfy server URL (default: `https://ntfy.sh`) |
191
+ | `--notify-telegram-token` | `LOGWHISPERER_TELEGRAM_TOKEN` | Telegram bot token |
192
+ | `--notify-telegram-chat-id` | `LOGWHISPERER_TELEGRAM_CHAT_ID` | Telegram chat ID |
193
+ | `--notify-email-host` | `LOGWHISPERER_SMTP_HOST` | SMTP server hostname |
194
+ | `--notify-email-port` | `LOGWHISPERER_SMTP_PORT` | SMTP port (default: `587`) |
195
+ | `--notify-email-user` | `LOGWHISPERER_SMTP_USER` | SMTP username |
196
+ | `--notify-email-pass` | `LOGWHISPERER_SMTP_PASS` | SMTP password |
197
+ | `--notify-email-from` | `LOGWHISPERER_EMAIL_FROM` | Sender email address |
198
+ | `--notify-email-to` | `LOGWHISPERER_EMAIL_TO` | Recipient email address |
199
+ | `--notify-email-no-tls` | — | Disable STARTTLS |
200
+
201
+ ---
202
+
203
+ ## Sources
204
+
205
+ ### Plain File
206
+
207
+ Reads the last `--lines` lines from any text file. No external tools required.
208
+
209
+ ```bash
210
+ log-whisperer --file /var/log/nginx/error.log --since 1h
211
+ ```
212
+
213
+ > **Note:** `--since` is passed to docker/journalctl only. For files, all
214
+ > lines are read and the last `--lines` are analysed.
215
+
216
+ ### Docker Container
217
+
218
+ Fetches logs from a running (or stopped) container via `docker logs`.
219
+
220
+ ```bash
221
+ log-whisperer --docker my-api --since 30m --show-new
222
+ ```
223
+
224
+ ### Docker Compose
225
+
226
+ Fetches logs from one service or all services in the current project.
227
+
228
+ ```bash
229
+ # Single service
230
+ log-whisperer --compose web --since 1h
231
+
232
+ # All services
233
+ log-whisperer --compose-all --since 1h
234
+ ```
235
+
236
+ ### systemd Journal
237
+
238
+ Reads from `journalctl` for a specific unit, with bare message output (`-o cat`).
239
+
240
+ ```bash
241
+ log-whisperer --service nginx --since today
242
+ ```
243
+
244
+ ---
245
+
246
+ ## Pattern Engine
247
+
248
+ ### How Normalisation Works
249
+
250
+ LogWhisperer transforms each raw log line into a **pattern** by replacing
251
+ variable data with fixed placeholders:
252
+
253
+ ```
254
+ Raw: 2024-03-15T10:23:45 ERROR Connection to 192.168.1.42 failed (attempt 3)
255
+ Pattern: ERROR Connection to <IP> failed (attempt <N>)
256
+ ```
257
+
258
+ The following substitutions are applied **in order**:
259
+
260
+ | Data Type | Example | Placeholder |
261
+ |:----------|:--------|:------------|
262
+ | Timestamp prefix | `2024-03-15T10:23:45` | *(stripped)* |
263
+ | Syslog prefix | `Mar 15 10:23:45 host app:` | *(stripped)* |
264
+ | UUID | `550e8400-e29b-41d4-a716-446655440000` | `<UUID>` |
265
+ | Hash (32-64 hex chars) | `d41d8cd98f00b204e9800998ecf8427e` | `<HASH>` |
266
+ | IPv4 address | `192.168.1.42` | `<IP>` |
267
+ | MAC address | `aa:bb:cc:dd:ee:ff` | `<MAC>` |
268
+ | Hex literal | `0x1A2F` | `<HEX>` |
269
+ | File path | `/usr/local/bin/app` | `<PATH>` |
270
+ | Number | `42`, `3000` | `<N>` |
271
+
272
+ > **Order matters.** UUIDs and long hashes are matched *before* the generic
273
+ > number pattern to prevent partial replacement (e.g. a UUID being half-replaced
274
+ > with `<N>`).
275
+
276
+ ### Pattern Hashing
277
+
278
+ Each normalized pattern is hashed with **SHA-1** to produce a stable identifier.
279
+ Two log lines that normalize to the same string will always share the same hash
280
+ and be counted together.
281
+
282
+ ### Severity Classification
283
+
284
+ Patterns are classified by scanning for keywords (case-insensitive):
285
+
286
+ | Severity | Keywords |
287
+ |:---------|:---------|
288
+ | **ERROR** | `error`, `fatal`, `exception`, `traceback`, `panic`, `segfault`, `failed`, `failure`, `critical` |
289
+ | **WARN** | `warn`, `warning`, `timeout`, `timed out`, `retry`, `throttle`, `rate limit`, `deprecated`, `slow`, `unavailable` |
290
+ | **INFO** | Everything else |
291
+
292
+ Use `--min-severity` to filter the report output:
293
+
294
+ ```bash
295
+ # Only show warnings and errors
296
+ log-whisperer --file app.log --min-severity WARN
297
+
298
+ # Only show errors
299
+ log-whisperer --file app.log --min-severity ERROR
300
+ ```
301
+
302
+ > **Note:** Patterns below the severity threshold are still recorded in the
303
+ > database — they just don't appear in the report.
304
+
305
+ ---
306
+
307
+ ## Baseline Learning
308
+
309
+ When you first deploy LogWhisperer against a service, *every* pattern is new.
310
+ **Baseline learning** lets the tool silently learn existing patterns for a
311
+ set period before it starts alerting.
312
+
313
+ ```bash
314
+ # Learn patterns for the next 24 hours — no alerts during this time
315
+ log-whisperer --file app.log --baseline-learn 24h
316
+ ```
317
+
318
+ ```
319
+ Baseline learning enabled until 2024-03-16 10:30:00. (No alerts during this period)
320
+ ```
321
+
322
+ During baseline learning:
323
+ - All patterns are recorded in the database as usual.
324
+ - The report still shows `[NEW]` / `[seen]` tags.
325
+ - **No alert notifications are dispatched.**
326
+
327
+ Once the learning window expires, any genuinely new pattern will trigger alerts.
328
+
329
+ ### Duration Format
330
+
331
+ | Example | Meaning |
332
+ |:--------|:--------|
333
+ | `30s` | 30 seconds |
334
+ | `10m` | 10 minutes |
335
+ | `2h` | 2 hours |
336
+ | `1d` | 1 day |
337
+
338
+ ---
339
+
340
+ ## State & Database
341
+
342
+ ### Pattern Database
343
+
344
+ The pattern DB is a **JSON-lines** file (one JSON object per line):
345
+
346
+ ```bash
347
+ $ cat ~/.local/state/logwhisperer/patterns.db
348
+ {"h":"d72cb82a...","first_seen":1710500000,"last_seen":1710503600,"total_seen":47,"severity":"ERROR","pattern":"ERROR something failed","sample":"2024-03-15 ERROR something failed"}
349
+ {"h":"8c39b05a...","first_seen":1710500000,"last_seen":1710503600,"total_seen":1203,"severity":"INFO","pattern":"INFO all good","sample":"2024-03-15 INFO all good"}
350
+ ```
351
+
352
+ You can inspect it with standard tools:
353
+
354
+ ```bash
355
+ # Pretty-print all entries
356
+ cat ~/.local/state/logwhisperer/patterns.db | jq .
357
+
358
+ # Count total patterns
359
+ wc -l ~/.local/state/logwhisperer/patterns.db
360
+
361
+ # Find ERROR patterns
362
+ grep '"severity":"ERROR"' ~/.local/state/logwhisperer/patterns.db | jq .
363
+ ```
364
+
365
+ ### Concurrency Safety
366
+
367
+ The database uses **file-level locking** (`fcntl.flock`):
368
+
369
+ - **Readers** acquire a shared lock — multiple concurrent reads are safe.
370
+ - **Writers** acquire an exclusive lock — writes are serialized.
371
+
372
+ This prevents corruption when two cron jobs overlap.
373
+
374
+ ### Reset
375
+
376
+ To clear all learned patterns and start fresh:
377
+
378
+ ```bash
379
+ log-whisperer --reset --file /dev/null
380
+ ```
381
+
382
+ ### Custom State Location
383
+
384
+ ```bash
385
+ log-whisperer --file app.log \
386
+ --state-db /opt/logwhisperer/myapp.db \
387
+ --baseline-state /opt/logwhisperer/myapp-baseline.json
388
+ ```
389
+
390
+ ---
391
+
392
+ ## Notifications
393
+
394
+ LogWhisperer can alert you when **new patterns appear** (outside baseline
395
+ learning mode). Configure one or more channels:
396
+
397
+ ### ntfy
398
+
399
+ [ntfy](https://ntfy.sh) is a simple pub/sub notification service.
400
+
401
+ ```bash
402
+ log-whisperer --file app.log \
403
+ --notify-ntfy-topic my-alerts \
404
+ --notify-ntfy-server https://ntfy.sh
405
+ ```
406
+
407
+ Or via environment variables:
408
+
409
+ ```bash
410
+ export LOGWHISPERER_NTFY_TOPIC=my-alerts
411
+ log-whisperer --file app.log
412
+ ```
413
+
414
+ ### Telegram
415
+
416
+ ```bash
417
+ log-whisperer --file app.log \
418
+ --notify-telegram-token "123456:ABC-DEF..." \
419
+ --notify-telegram-chat-id "-100123456789"
420
+ ```
421
+
422
+ Or via environment:
423
+
424
+ ```bash
425
+ export LOGWHISPERER_TELEGRAM_TOKEN="123456:ABC-DEF..."
426
+ export LOGWHISPERER_TELEGRAM_CHAT_ID="-100123456789"
427
+ ```
428
+
429
+ ### Email (SMTP)
430
+
431
+ ```bash
432
+ log-whisperer --file app.log \
433
+ --notify-email-host smtp.gmail.com \
434
+ --notify-email-port 587 \
435
+ --notify-email-user me@gmail.com \
436
+ --notify-email-pass "app-password" \
437
+ --notify-email-from me@gmail.com \
438
+ --notify-email-to ops-team@company.com
439
+ ```
440
+
441
+ Use `--notify-email-no-tls` to disable STARTTLS for local/internal relays.
442
+
443
+ ### Alert Format
444
+
445
+ When new patterns are detected, all configured channels receive a message like:
446
+
447
+ ```
448
+ Log Whisperer ALERT (2 new patterns)
449
+ Source: file:/var/log/app.log | since=1h
450
+
451
+ [NEW][ERROR] x5 ERROR connection to <IP> refused
452
+ sample: 2024-03-15 10:23:45 ERROR connection to 10.0.0.3 refused
453
+
454
+ [NEW][WARN] x12 WARN request latency <N>ms exceeds threshold
455
+ sample: 2024-03-15 10:24:01 WARN request latency 3502ms exceeds threshold
456
+ ```
457
+
458
+ > **Tip:** Multiple channels can be configured simultaneously. A failure in
459
+ > one channel does not block delivery to the others.
460
+
461
+ ---
462
+
463
+ ## JSON Output
464
+
465
+ Use `--json` for machine-readable output (piping, dashboards, further processing):
466
+
467
+ ```bash
468
+ log-whisperer --file app.log --json | python3 -m json.tool
469
+ ```
470
+
471
+ ```json
472
+ {
473
+ "source": "file:app.log",
474
+ "since": "1h",
475
+ "lines_limit": 5000,
476
+ "state_db": "/home/user/.local/state/logwhisperer/patterns.db",
477
+ "baseline_active": false,
478
+ "baseline_until": 0,
479
+ "generated_at": 1710503600,
480
+ "items": [
481
+ {
482
+ "tag": "NEW",
483
+ "count_window": 5,
484
+ "total_seen": 5,
485
+ "severity": "ERROR",
486
+ "pattern": "ERROR connection to <IP> refused",
487
+ "sample": "2024-03-15 10:23:45 ERROR connection to 10.0.0.3 refused",
488
+ "hash": "a1b2c3d4..."
489
+ }
490
+ ]
491
+ }
492
+ ```
493
+
494
+ ### Useful jq Queries
495
+
496
+ ```bash
497
+ # Count of new patterns only
498
+ log-whisperer --file app.log --json | jq '[.items[] | select(.tag == "NEW")] | length'
499
+
500
+ # List ERROR patterns with their counts
501
+ log-whisperer --file app.log --json | jq '.items[] | select(.severity == "ERROR") | {pattern, count_window}'
502
+
503
+ # Exit non-zero if any new ERROR patterns found
504
+ log-whisperer --file app.log --json | jq -e '[.items[] | select(.tag == "NEW" and .severity == "ERROR")] | length > 0'
505
+ ```
506
+
507
+ ---
508
+
509
+ ## Recipes
510
+
511
+ ### Cron Job — Check Every 5 Minutes, Alert on New Patterns
512
+
513
+ ```cron
514
+ */5 * * * * /usr/local/bin/log-whisperer --file /var/log/app.log --since 5m --notify-ntfy-topic my-alerts 2>>/var/log/logwhisperer-errors.log
515
+ ```
516
+
517
+ ### First-Time Setup with Baseline Learning
518
+
519
+ ```bash
520
+ # Step 1: Learn existing patterns for 24 hours
521
+ log-whisperer --file /var/log/app.log --baseline-learn 24h
522
+
523
+ # Step 2: Set up the cron job (alerts will start after 24h)
524
+ crontab -e
525
+ # */5 * * * * log-whisperer --file /var/log/app.log --since 5m --notify-ntfy-topic my-alerts
526
+ ```
527
+
528
+ ### Monitor Multiple Services
529
+
530
+ ```bash
531
+ #!/bin/bash
532
+ # monitor-all.sh — run from cron
533
+
534
+ log-whisperer --docker api-server --since 5m --state-db /var/lib/lw/api.db
535
+ log-whisperer --docker worker --since 5m --state-db /var/lib/lw/worker.db
536
+ log-whisperer --service nginx --since 5m --state-db /var/lib/lw/nginx.db
537
+ log-whisperer --file /var/log/app.log --since 5m --state-db /var/lib/lw/app.db
538
+ ```
539
+
540
+ > Use separate `--state-db` paths to keep pattern histories isolated per service.
541
+
542
+ ### Errors-Only Daily Digest
543
+
544
+ ```bash
545
+ log-whisperer --file /var/log/app.log \
546
+ --since 24h \
547
+ --min-severity ERROR \
548
+ --show-samples \
549
+ --notify-email-host smtp.company.com \
550
+ --notify-email-from lw@company.com \
551
+ --notify-email-to oncall@company.com
552
+ ```
553
+
554
+ ### CI / Smoke Test — Fail If New Error Patterns Appear
555
+
556
+ ```bash
557
+ log-whisperer --file test-output.log --json \
558
+ | jq -e '[.items[] | select(.tag == "NEW" and .severity == "ERROR")] | length == 0' \
559
+ || { echo "New error patterns detected!"; exit 1; }
560
+ ```
561
+
562
+ ### Pipe Logs Directly (via process substitution)
563
+
564
+ ```bash
565
+ log-whisperer --file <(kubectl logs deploy/my-app --since=1h) --show-new
566
+ ```
567
+
568
+ ---
569
+
570
+ ## Environment Variables
571
+
572
+ All notification settings can be set via environment variables so they don't
573
+ appear in process listings or shell history:
574
+
575
+ | Variable | Maps To |
576
+ |:---------|:--------|
577
+ | `LOGWHISPERER_NTFY_TOPIC` | `--notify-ntfy-topic` |
578
+ | `LOGWHISPERER_NTFY_SERVER` | `--notify-ntfy-server` |
579
+ | `LOGWHISPERER_TELEGRAM_TOKEN` | `--notify-telegram-token` |
580
+ | `LOGWHISPERER_TELEGRAM_CHAT_ID` | `--notify-telegram-chat-id` |
581
+ | `LOGWHISPERER_SMTP_HOST` | `--notify-email-host` |
582
+ | `LOGWHISPERER_SMTP_PORT` | `--notify-email-port` |
583
+ | `LOGWHISPERER_SMTP_USER` | `--notify-email-user` |
584
+ | `LOGWHISPERER_SMTP_PASS` | `--notify-email-pass` |
585
+ | `LOGWHISPERER_EMAIL_FROM` | `--notify-email-from` |
586
+ | `LOGWHISPERER_EMAIL_TO` | `--notify-email-to` |
587
+ | `XDG_STATE_HOME` | Base directory for state files (default: `~/.local/state`) |
588
+
589
+ ---
590
+
591
+ ## Troubleshooting
592
+
593
+ ### "Command not found: docker"
594
+
595
+ The Docker or journalctl binary isn't on `$PATH`. Make sure the relevant tool
596
+ is installed and accessible to the user running LogWhisperer.
597
+
598
+ ### "Choose exactly one source"
599
+
600
+ You must specify exactly one of `--file`, `--docker`, `--compose`, `--compose-all`,
601
+ or `--service`. You cannot combine sources in a single run.
602
+
603
+ ### Every run shows `[NEW]`
604
+
605
+ Your state DB is being reset or isn't persisting. Check:
606
+
607
+ ```bash
608
+ ls -la ~/.local/state/logwhisperer/patterns.db
609
+ ```
610
+
611
+ If the file doesn't exist between runs, verify that `--state-db` points to a
612
+ writable, persistent location.
613
+
614
+ ### No alerts firing
615
+
616
+ - Check that baseline learning has expired: look for `Baseline: ACTIVE` in the
617
+ text report output.
618
+ - Alerts only fire for `[NEW]` patterns — if all patterns are `[seen]`, no alert
619
+ is sent.
620
+ - Verify your notification credentials are correct by checking stderr for
621
+ `"Notification failures:"` messages.
622
+
623
+ ### Database looks corrupted
624
+
625
+ The DB is JSON-lines. Validate it:
626
+
627
+ ```bash
628
+ python3 -c "
629
+ import json, sys
630
+ for i, line in enumerate(open(sys.argv[1]), 1):
631
+ try: json.loads(line)
632
+ except: print(f'Bad line {i}: {line.rstrip()}')
633
+ " ~/.local/state/logwhisperer/patterns.db
634
+ ```
635
+
636
+ If corruption occurred, reset and re-learn:
637
+
638
+ ```bash
639
+ log-whisperer --reset --file /dev/null
640
+ log-whisperer --file /var/log/app.log --baseline-learn 1h
641
+ ```
642
+
643
+ ---
644
+
645
+ <p align="center">
646
+ <sub>LogWhisperer &mdash; Built with Python 3.9+</sub>
647
+ </p>