logtap 0.3.0__py3-none-any.whl → 0.4.1__py3-none-any.whl
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.
- logtap/__init__.py +1 -1
- logtap/api/app.py +69 -3
- logtap/api/routes/health.py +26 -4
- logtap/api/routes/logs.py +26 -31
- logtap/api/routes/parsed.py +8 -7
- logtap/api/routes/runs.py +330 -0
- logtap/cli/commands/collect.py +107 -0
- logtap/cli/commands/doctor.py +127 -0
- logtap/cli/commands/ingest.py +123 -0
- logtap/cli/commands/runs.py +116 -0
- logtap/cli/commands/tail.py +220 -23
- logtap/cli/main.py +12 -5
- logtap/core/runs.py +433 -0
- logtap/core/validation.py +132 -0
- logtap/models/responses.py +54 -1
- logtap-0.4.1.dist-info/METADATA +304 -0
- {logtap-0.3.0.dist-info → logtap-0.4.1.dist-info}/RECORD +20 -14
- logtap-0.3.0.dist-info/METADATA +0 -319
- {logtap-0.3.0.dist-info → logtap-0.4.1.dist-info}/WHEEL +0 -0
- {logtap-0.3.0.dist-info → logtap-0.4.1.dist-info}/entry_points.txt +0 -0
- {logtap-0.3.0.dist-info → logtap-0.4.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: logtap
|
|
3
|
+
Version: 0.4.1
|
|
4
|
+
Summary: A CLI-first log access tool for Unix systems. Remote log file access without SSH.
|
|
5
|
+
Project-URL: Homepage, https://github.com/cainky/logtap
|
|
6
|
+
Project-URL: Repository, https://github.com/cainky/logtap
|
|
7
|
+
Author-email: cainky <kylecain.me@gmail.com>
|
|
8
|
+
License: GPL-3.0-or-later
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Keywords: cli,devops,logs,monitoring,sysadmin
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Intended Audience :: System Administrators
|
|
15
|
+
Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
|
|
16
|
+
Classifier: Operating System :: MacOS
|
|
17
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Topic :: System :: Logging
|
|
22
|
+
Classifier: Topic :: System :: Monitoring
|
|
23
|
+
Classifier: Topic :: System :: Systems Administration
|
|
24
|
+
Requires-Python: >=3.10
|
|
25
|
+
Requires-Dist: aiofiles>=23.2.1
|
|
26
|
+
Requires-Dist: fastapi>=0.109.0
|
|
27
|
+
Requires-Dist: google-re2>=1.1
|
|
28
|
+
Requires-Dist: httpx>=0.26.0
|
|
29
|
+
Requires-Dist: pydantic-settings>=2.1.0
|
|
30
|
+
Requires-Dist: pydantic>=2.5.0
|
|
31
|
+
Requires-Dist: python-dotenv>=1.0.1
|
|
32
|
+
Requires-Dist: rich>=13.7.0
|
|
33
|
+
Requires-Dist: typer>=0.9.0
|
|
34
|
+
Requires-Dist: uvicorn[standard]>=0.27.0
|
|
35
|
+
Requires-Dist: websockets>=12.0
|
|
36
|
+
Provides-Extra: dev
|
|
37
|
+
Requires-Dist: pre-commit>=4.5.1; extra == 'dev'
|
|
38
|
+
Requires-Dist: pytest-asyncio>=0.23.0; extra == 'dev'
|
|
39
|
+
Requires-Dist: pytest-cov>=4.1.0; extra == 'dev'
|
|
40
|
+
Requires-Dist: pytest>=7.4.0; extra == 'dev'
|
|
41
|
+
Requires-Dist: ruff>=0.1.0; extra == 'dev'
|
|
42
|
+
Description-Content-Type: text/markdown
|
|
43
|
+
|
|
44
|
+
# logtap
|
|
45
|
+
|
|
46
|
+
[](https://badge.fury.io/py/logtap)
|
|
47
|
+
[](https://github.com/cainky/logtap/actions/workflows/tests.yml)
|
|
48
|
+
[](https://www.gnu.org/licenses/gpl-3.0)
|
|
49
|
+
[](https://www.python.org/downloads/)
|
|
50
|
+
|
|
51
|
+
**`tail -f` for GPU clouds. Survives disconnects, aggregates multi-node.**
|
|
52
|
+
|
|
53
|
+
> Stop losing your training logs when SSH drops. Watch from anywhere, reconnect seamlessly.
|
|
54
|
+
|
|
55
|
+
## The Problem
|
|
56
|
+
|
|
57
|
+
You're training a model on RunPod, Vast.ai, or Lambda. You SSH in, start training, and:
|
|
58
|
+
|
|
59
|
+
- Your terminal disconnects after an hour
|
|
60
|
+
- You lose visibility into what's happening
|
|
61
|
+
- You resort to tmux hacks just to keep logs alive
|
|
62
|
+
- Multi-node training means logs scattered across machines
|
|
63
|
+
|
|
64
|
+
## The Solution
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
# On your GPU instance
|
|
68
|
+
pip install logtap
|
|
69
|
+
logtap collect &
|
|
70
|
+
python train.py 2>&1 | logtap ingest run1
|
|
71
|
+
|
|
72
|
+
# From your laptop (or phone)
|
|
73
|
+
logtap tail run1 --follow
|
|
74
|
+
|
|
75
|
+
# Connection drops... reconnects automatically
|
|
76
|
+
# "reconnected (missed 0 lines)"
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Quickstart: RunPod / Vast.ai
|
|
80
|
+
|
|
81
|
+
On the GPU instance:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
pip install logtap
|
|
85
|
+
export LOGTAP_API_KEY=secret
|
|
86
|
+
logtap collect --port 8000
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Start training and stream logs:
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
python train.py 2>&1 | logtap ingest run1 --tag node=$(hostname)
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
From your laptop:
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
export LOGTAP_SERVER=http://<gpu-ip>:8000
|
|
99
|
+
export LOGTAP_API_KEY=secret
|
|
100
|
+
logtap tail run1 --follow
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Disconnect, close your terminal, or switch networks.
|
|
104
|
+
Re-run `logtap tail` anytime to resume where you left off.
|
|
105
|
+
|
|
106
|
+
Works the same on RunPod, Vast.ai, Lambda, and any ephemeral GPU cloud.
|
|
107
|
+
|
|
108
|
+
## Features
|
|
109
|
+
|
|
110
|
+
- **Survives Disconnects** - Resume from where you left off with cursor-based streaming
|
|
111
|
+
- **Pipe-Friendly** - Works with any training script via stdin
|
|
112
|
+
- **Multi-Node Ready** - Tag runs with `node=gpu1` and filter/aggregate
|
|
113
|
+
- **Zero Infra** - No database, no complex setup, just pip install
|
|
114
|
+
- **Lightweight** - <50MB memory, append-only file storage
|
|
115
|
+
|
|
116
|
+
## Why not tmux / mosh?
|
|
117
|
+
|
|
118
|
+
tmux and mosh help keep SSH sessions alive.
|
|
119
|
+
logtap solves a different problem.
|
|
120
|
+
|
|
121
|
+
- SSH can still drop (web terminals, proxies, idle timeouts)
|
|
122
|
+
- tmux doesn't aggregate logs across machines
|
|
123
|
+
- tmux can't be viewed from another device without SSH
|
|
124
|
+
- tmux sessions die when ephemeral instances stop
|
|
125
|
+
|
|
126
|
+
logtap streams logs over HTTP:
|
|
127
|
+
- survives disconnects
|
|
128
|
+
- resumes without gaps
|
|
129
|
+
- aggregates multi-node training via tags
|
|
130
|
+
- works from anywhere (no SSH required)
|
|
131
|
+
|
|
132
|
+
You can still use tmux. You just don't have to rely on it.
|
|
133
|
+
|
|
134
|
+
## Quick Start
|
|
135
|
+
|
|
136
|
+
### 1. Install
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
pip install logtap
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### 2. Start Collector (on GPU instance)
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
logtap collect --api-key secret
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### 3. Pipe Your Training Logs
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
python train.py 2>&1 | logtap ingest run1 --api-key secret
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### 4. Tail From Anywhere
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
export LOGTAP_SERVER=http://your-gpu-ip:8000
|
|
158
|
+
export LOGTAP_API_KEY=secret
|
|
159
|
+
|
|
160
|
+
logtap tail run1 --follow
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## CLI Commands
|
|
164
|
+
|
|
165
|
+
| Command | Description |
|
|
166
|
+
|---------|-------------|
|
|
167
|
+
| `logtap collect` | Start collector server (accepts ingested runs) |
|
|
168
|
+
| `logtap ingest <run>` | Pipe stdin to collector |
|
|
169
|
+
| `logtap tail <run>` | Tail a run with `--follow` for streaming |
|
|
170
|
+
| `logtap runs` | List active runs |
|
|
171
|
+
| `logtap doctor` | Check server connectivity and diagnose issues |
|
|
172
|
+
|
|
173
|
+
### Ingest Options
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
# Auto-generate run name
|
|
177
|
+
python train.py | logtap ingest
|
|
178
|
+
|
|
179
|
+
# Add tags for multi-node
|
|
180
|
+
python train.py | logtap ingest run1 --tag node=gpu1 --tag rank=0
|
|
181
|
+
|
|
182
|
+
# Quiet mode (no status messages)
|
|
183
|
+
python train.py | logtap ingest run1 --quiet
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### Tail Options
|
|
187
|
+
|
|
188
|
+
```bash
|
|
189
|
+
# Follow mode (like tail -f)
|
|
190
|
+
logtap tail run1 --follow
|
|
191
|
+
|
|
192
|
+
# Resume from specific cursor (survives disconnects!)
|
|
193
|
+
logtap tail run1 --follow --since 5000
|
|
194
|
+
|
|
195
|
+
# Filter by tag
|
|
196
|
+
logtap tail run1 --tag node=gpu1
|
|
197
|
+
|
|
198
|
+
# Output formats
|
|
199
|
+
logtap tail run1 --output jsonl | jq '.line'
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### Collector Options
|
|
203
|
+
|
|
204
|
+
```bash
|
|
205
|
+
logtap collect \
|
|
206
|
+
--port 8000 \
|
|
207
|
+
--api-key secret \
|
|
208
|
+
--data-dir ~/.logtap/runs \
|
|
209
|
+
--max-disk-mb 5000 \
|
|
210
|
+
--retention-hours 72
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
## Multi-Node Training
|
|
214
|
+
|
|
215
|
+
Tag each node and aggregate:
|
|
216
|
+
|
|
217
|
+
```bash
|
|
218
|
+
# Node 1
|
|
219
|
+
python train.py | logtap ingest run1 --tag node=gpu1
|
|
220
|
+
|
|
221
|
+
# Node 2
|
|
222
|
+
python train.py | logtap ingest run1 --tag node=gpu2
|
|
223
|
+
|
|
224
|
+
# Watch all nodes
|
|
225
|
+
logtap tail run1 --follow
|
|
226
|
+
|
|
227
|
+
# Watch specific node
|
|
228
|
+
logtap tail run1 --follow --tag node=gpu1
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
## Environment Variables
|
|
232
|
+
|
|
233
|
+
| Variable | Default | Description |
|
|
234
|
+
|----------|---------|-------------|
|
|
235
|
+
| `LOGTAP_SERVER` | `http://localhost:8000` | Collector URL |
|
|
236
|
+
| `LOGTAP_API_KEY` | - | API key for auth |
|
|
237
|
+
|
|
238
|
+
Set these to avoid typing `--server` and `--api-key` every time.
|
|
239
|
+
|
|
240
|
+
## How It Works
|
|
241
|
+
|
|
242
|
+
1. **Collector** writes logs to append-only files with cursor tracking
|
|
243
|
+
2. **Ingest** streams stdin over HTTP chunked POST
|
|
244
|
+
3. **Tail** uses SSE (Server-Sent Events) with resume support
|
|
245
|
+
4. **Reconnect** passes `?since=<cursor>` to continue without gaps
|
|
246
|
+
|
|
247
|
+
No database. No message queue. Just files and HTTP.
|
|
248
|
+
|
|
249
|
+
## API Endpoints
|
|
250
|
+
|
|
251
|
+
For scripting or custom integrations:
|
|
252
|
+
|
|
253
|
+
| Endpoint | Description |
|
|
254
|
+
|----------|-------------|
|
|
255
|
+
| `POST /runs/{id}/ingest` | Stream lines (chunked POST) |
|
|
256
|
+
| `GET /runs/{id}/stream` | SSE stream with `?since=&follow=` |
|
|
257
|
+
| `GET /runs/{id}/query` | Query with `?from=&to=&search=` |
|
|
258
|
+
| `GET /runs` | List runs |
|
|
259
|
+
| `GET /health` | Health check with capabilities |
|
|
260
|
+
|
|
261
|
+
## Legacy: Static File Mode
|
|
262
|
+
|
|
263
|
+
logtap also works as a simple remote log viewer (the original use case):
|
|
264
|
+
|
|
265
|
+
```bash
|
|
266
|
+
# On server with log files
|
|
267
|
+
logtap serve --log-dir /var/log
|
|
268
|
+
|
|
269
|
+
# From client
|
|
270
|
+
logtap tail syslog --server http://myserver:8000 --follow
|
|
271
|
+
logtap query auth.log --regex "Failed password"
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
## Security
|
|
275
|
+
|
|
276
|
+
- **API Key Auth** - Optional but recommended for production
|
|
277
|
+
- **Path Traversal Protection** - Comprehensive defense with symlink-safe containment checks (see [SECURITY.md](SECURITY.md))
|
|
278
|
+
- **ReDoS Protection** - Uses google-re2 for guaranteed linear-time regex matching
|
|
279
|
+
- **Read-Only by Default** - Collector only writes to its data directory
|
|
280
|
+
- **Input Validation** - Rejects control characters, NUL bytes, and malicious path patterns
|
|
281
|
+
|
|
282
|
+
## Development
|
|
283
|
+
|
|
284
|
+
```bash
|
|
285
|
+
git clone https://github.com/cainky/logtap.git
|
|
286
|
+
cd logtap
|
|
287
|
+
|
|
288
|
+
# Install with uv
|
|
289
|
+
uv sync --extra dev
|
|
290
|
+
|
|
291
|
+
# Run tests
|
|
292
|
+
uv run pytest
|
|
293
|
+
|
|
294
|
+
# Run collector in dev mode
|
|
295
|
+
uv run logtap collect --reload
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
## License
|
|
299
|
+
|
|
300
|
+
GPL v3 - see [LICENSE](LICENSE)
|
|
301
|
+
|
|
302
|
+
## Author
|
|
303
|
+
|
|
304
|
+
Kyle Cain - [@cainky](https://github.com/cainky)
|
|
@@ -1,24 +1,30 @@
|
|
|
1
|
-
logtap/__init__.py,sha256=
|
|
1
|
+
logtap/__init__.py,sha256=kPYm2mOUmJEBnDtZ78K-fva7PcnK1J7yjQToY6bEqyc,179
|
|
2
2
|
logtap/__main__.py,sha256=vqJPz3Zf-ICn_4P3B1o7U4NxcCo0qWgfAGEE_j13t-c,138
|
|
3
3
|
logtap/api/__init__.py,sha256=80bP-eIxtAzidgv5nzcfyCEdl8EI2QjVY_eyxjvvhA0,98
|
|
4
|
-
logtap/api/app.py,sha256=
|
|
4
|
+
logtap/api/app.py,sha256=6TIwQFjwyW0VYYo3ayRiUO2IuHnPKsK5kAIPO9xzPvo,3094
|
|
5
5
|
logtap/api/dependencies.py,sha256=1cx1qrp0O6v1fHXA2JdEhC8P4caG2oUSCfMk2-8zmGs,1620
|
|
6
6
|
logtap/api/routes/__init__.py,sha256=XYvFyTP4zKywRZH0v97k0EZCYgxdL2PSUaNet20znPE,29
|
|
7
7
|
logtap/api/routes/files.py,sha256=bqZYrX6jrF5-7GzBpUIXXoPVdxUwm6o0LTcJBLtaJUE,991
|
|
8
|
-
logtap/api/routes/health.py,sha256=
|
|
9
|
-
logtap/api/routes/logs.py,sha256=
|
|
10
|
-
logtap/api/routes/parsed.py,sha256=
|
|
8
|
+
logtap/api/routes/health.py,sha256=s117Hr1E8OcBGPOWq2WwHLZSq35hS7wmLPk6BYq3dq4,1112
|
|
9
|
+
logtap/api/routes/logs.py,sha256=J_Sp9-Mx2g-tVdpVJMzMz1ytymtCCMJvDJ74uLeiKw0,8294
|
|
10
|
+
logtap/api/routes/parsed.py,sha256=dXIKy0JMxlvoHAbCCk4zhagHUPaWclaUBPcHXG2ew1I,3442
|
|
11
|
+
logtap/api/routes/runs.py,sha256=njyPZ90eXeAvnDWVq-q3x0slDCjUJ8FotOnl1muCKgI,10571
|
|
11
12
|
logtap/cli/__init__.py,sha256=U4zaUJ1rm0qHXqeArpzC45S5N-5SBdd8K6foe513msk,31
|
|
12
|
-
logtap/cli/main.py,sha256=
|
|
13
|
+
logtap/cli/main.py,sha256=7XSeZrvCfiCcMA_sJLIIaT21ORAsYKiD_gKCSLxGmbc,1267
|
|
13
14
|
logtap/cli/commands/__init__.py,sha256=U4zaUJ1rm0qHXqeArpzC45S5N-5SBdd8K6foe513msk,31
|
|
15
|
+
logtap/cli/commands/collect.py,sha256=8x6LyMrzI79wYtfLZcbQdgpy5nxPZuQOEillE9IfwwE,3002
|
|
16
|
+
logtap/cli/commands/doctor.py,sha256=fVL6ZhD52zQBqGQRwi0J_m3MaN-0SbwhxM8UZC4nG-Y,4218
|
|
14
17
|
logtap/cli/commands/files.py,sha256=WFr8kA0SdgQHz3ZyONTaljxHMcD-nQlndp3UIOwZATc,2455
|
|
18
|
+
logtap/cli/commands/ingest.py,sha256=JaItHHYV3fBmPkseYpubyHryNbuEuxyjRBk-EiiEwyU,4054
|
|
15
19
|
logtap/cli/commands/query.py,sha256=uD9nH5E-7EqJryLf3hHkDbJSQo4kWFGmzzHgTfAKFwk,3418
|
|
20
|
+
logtap/cli/commands/runs.py,sha256=Dweswku19Dj2KOFhT0kaega9KSKmUrvya3eLn0-5lXo,3632
|
|
16
21
|
logtap/cli/commands/serve.py,sha256=9OvfII21q6cel3zZfSsAsiERKwKFt0ZFTXmUd2Psthg,1910
|
|
17
|
-
logtap/cli/commands/tail.py,sha256=
|
|
22
|
+
logtap/cli/commands/tail.py,sha256=mOYGwXaXeM_PHI27BAm61CR25YAh9dpLOlq8oy0ciLM,10427
|
|
18
23
|
logtap/core/__init__.py,sha256=tsoL0XuDrPd5xHEu975WqFHoA7EQgloxrum7CjsWHuk,450
|
|
19
24
|
logtap/core/reader.py,sha256=BuBrEAbS2naCBTtuBNc0Un6thbekzabaHTBzYE1SwKg,5277
|
|
25
|
+
logtap/core/runs.py,sha256=lYOWAWp6RrcaMLkWxqhD0MC5alQe7rv07GtEazL5WWE,14956
|
|
20
26
|
logtap/core/search.py,sha256=rtq8WP96RYUvRkX_R5x_mdD_dw1syDuNkHx3uP_diOg,4574
|
|
21
|
-
logtap/core/validation.py,sha256=
|
|
27
|
+
logtap/core/validation.py,sha256=kRKRuhSaORslgatIQ2nW3ufDOn0mp_mB38CguhizRBY,6071
|
|
22
28
|
logtap/core/parsers/__init__.py,sha256=5f3hFxf_DgNScRDchRT8ocFVgi7Md4xuMN-ShvlssBo,575
|
|
23
29
|
logtap/core/parsers/apache.py,sha256=JjuQ4v-b7HJvTCcjbOMgv5_dSdiNVPX_EUyplc3f5Qw,5332
|
|
24
30
|
logtap/core/parsers/auto.py,sha256=OLLuX7XIxS0Upnv9FQ-_B0sGAyZmfNxjnMDGdZtUIO4,3565
|
|
@@ -28,9 +34,9 @@ logtap/core/parsers/nginx.py,sha256=j_oILELOM0azDPLc41wXrLu5o_LhnPs9fT0_iaOqqAQ,
|
|
|
28
34
|
logtap/core/parsers/syslog.py,sha256=gBNQ39QXsigOpfnq3cEdmvFa8NLp_wmiSMDlTt0SIbs,2430
|
|
29
35
|
logtap/models/__init__.py,sha256=tce3Q0QjPhnlAYG8IcwxPedyh1ibBlKIF3CjXe5wwgo,280
|
|
30
36
|
logtap/models/config.py,sha256=8x6OR_y2ZB8SSoQWQGwDB7DXH30UyMNXUcRWOctjUn8,927
|
|
31
|
-
logtap/models/responses.py,sha256=
|
|
32
|
-
logtap-0.
|
|
33
|
-
logtap-0.
|
|
34
|
-
logtap-0.
|
|
35
|
-
logtap-0.
|
|
36
|
-
logtap-0.
|
|
37
|
+
logtap/models/responses.py,sha256=xKdKdS85soxMYGNad3WfF0pOG0Pb5Z7XwVrwK-TCnHs,4084
|
|
38
|
+
logtap-0.4.1.dist-info/METADATA,sha256=Mz7i6uIVOv9mHtQ2oMgKSOVH5oVtpjO7s3N9u7ZSUP8,8203
|
|
39
|
+
logtap-0.4.1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
40
|
+
logtap-0.4.1.dist-info/entry_points.txt,sha256=tuAit8kt97yjtACQKvN35wWozp4KhSju_gfDhSS1IrM,47
|
|
41
|
+
logtap-0.4.1.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
42
|
+
logtap-0.4.1.dist-info/RECORD,,
|
logtap-0.3.0.dist-info/METADATA
DELETED
|
@@ -1,319 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: logtap
|
|
3
|
-
Version: 0.3.0
|
|
4
|
-
Summary: A CLI-first log access tool for Unix systems. Remote log file access without SSH.
|
|
5
|
-
Project-URL: Homepage, https://github.com/cainky/logtap
|
|
6
|
-
Project-URL: Repository, https://github.com/cainky/logtap
|
|
7
|
-
Author-email: cainky <kylecain.me@gmail.com>
|
|
8
|
-
License: GPL-3.0-or-later
|
|
9
|
-
License-File: LICENSE
|
|
10
|
-
Keywords: cli,devops,logs,monitoring,sysadmin
|
|
11
|
-
Classifier: Development Status :: 4 - Beta
|
|
12
|
-
Classifier: Environment :: Console
|
|
13
|
-
Classifier: Intended Audience :: Developers
|
|
14
|
-
Classifier: Intended Audience :: System Administrators
|
|
15
|
-
Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
|
|
16
|
-
Classifier: Operating System :: MacOS
|
|
17
|
-
Classifier: Operating System :: POSIX :: Linux
|
|
18
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
-
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
-
Classifier: Topic :: System :: Logging
|
|
22
|
-
Classifier: Topic :: System :: Monitoring
|
|
23
|
-
Classifier: Topic :: System :: Systems Administration
|
|
24
|
-
Requires-Python: >=3.10
|
|
25
|
-
Requires-Dist: aiofiles>=23.2.1
|
|
26
|
-
Requires-Dist: fastapi>=0.109.0
|
|
27
|
-
Requires-Dist: google-re2>=1.1
|
|
28
|
-
Requires-Dist: httpx>=0.26.0
|
|
29
|
-
Requires-Dist: pydantic-settings>=2.1.0
|
|
30
|
-
Requires-Dist: pydantic>=2.5.0
|
|
31
|
-
Requires-Dist: python-dotenv>=1.0.1
|
|
32
|
-
Requires-Dist: rich>=13.7.0
|
|
33
|
-
Requires-Dist: typer>=0.9.0
|
|
34
|
-
Requires-Dist: uvicorn[standard]>=0.27.0
|
|
35
|
-
Requires-Dist: websockets>=12.0
|
|
36
|
-
Provides-Extra: dev
|
|
37
|
-
Requires-Dist: pre-commit>=4.5.1; extra == 'dev'
|
|
38
|
-
Requires-Dist: pytest-asyncio>=0.23.0; extra == 'dev'
|
|
39
|
-
Requires-Dist: pytest-cov>=4.1.0; extra == 'dev'
|
|
40
|
-
Requires-Dist: pytest>=7.4.0; extra == 'dev'
|
|
41
|
-
Requires-Dist: ruff>=0.1.0; extra == 'dev'
|
|
42
|
-
Description-Content-Type: text/markdown
|
|
43
|
-
|
|
44
|
-
# logtap
|
|
45
|
-
|
|
46
|
-
[](https://badge.fury.io/py/logtap)
|
|
47
|
-
[](https://github.com/cainky/logtap/actions/workflows/tests.yml)
|
|
48
|
-
[](https://www.gnu.org/licenses/gpl-3.0)
|
|
49
|
-
[](https://www.python.org/downloads/)
|
|
50
|
-
|
|
51
|
-
**A CLI-first log access tool for Unix systems. Remote log file access without SSH.**
|
|
52
|
-
|
|
53
|
-
> The simplest way to access log files remotely. No database. No complex setup.
|
|
54
|
-
|
|
55
|
-
## Features
|
|
56
|
-
|
|
57
|
-
- **Remote Log Access** - Query log files via REST API without SSH
|
|
58
|
-
- **Beautiful CLI** - Colored output with rich formatting
|
|
59
|
-
- **Regex Search** - Powerful filtering with regex patterns
|
|
60
|
-
- **Real-time Streaming** - Follow logs like `tail -f` (WebSocket)
|
|
61
|
-
- **Lightweight** - No database required, minimal dependencies
|
|
62
|
-
- **Secure** - Optional API key authentication
|
|
63
|
-
- **Docker Ready** - One-command deployment
|
|
64
|
-
|
|
65
|
-
## Quick Start
|
|
66
|
-
|
|
67
|
-
### Installation
|
|
68
|
-
|
|
69
|
-
```bash
|
|
70
|
-
pip install logtap
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
Or with Docker:
|
|
74
|
-
|
|
75
|
-
```bash
|
|
76
|
-
docker pull cainky/logtap
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
### Start the Server
|
|
80
|
-
|
|
81
|
-
On the machine with log files:
|
|
82
|
-
|
|
83
|
-
```bash
|
|
84
|
-
logtap serve
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
With authentication:
|
|
88
|
-
|
|
89
|
-
```bash
|
|
90
|
-
logtap serve --api-key your-secret-key
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
### Query Logs
|
|
94
|
-
|
|
95
|
-
From anywhere:
|
|
96
|
-
|
|
97
|
-
```bash
|
|
98
|
-
# Basic query
|
|
99
|
-
logtap query syslog
|
|
100
|
-
|
|
101
|
-
# Search for errors
|
|
102
|
-
logtap query syslog --term "error"
|
|
103
|
-
|
|
104
|
-
# Regex search
|
|
105
|
-
logtap query auth.log --regex "Failed password.*root"
|
|
106
|
-
|
|
107
|
-
# Last 100 lines
|
|
108
|
-
logtap query syslog --limit 100
|
|
109
|
-
|
|
110
|
-
# From a remote server
|
|
111
|
-
logtap query syslog --server http://myserver:8000 --api-key secret
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
### List Available Files
|
|
115
|
-
|
|
116
|
-
```bash
|
|
117
|
-
logtap files
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
### Real-time Streaming
|
|
121
|
-
|
|
122
|
-
```bash
|
|
123
|
-
logtap tail syslog --follow
|
|
124
|
-
```
|
|
125
|
-
|
|
126
|
-
## CLI Commands
|
|
127
|
-
|
|
128
|
-
| Command | Description |
|
|
129
|
-
|---------|-------------|
|
|
130
|
-
| `logtap serve` | Start the API server |
|
|
131
|
-
| `logtap query <file>` | Query log files |
|
|
132
|
-
| `logtap tail <file>` | Tail logs (with `--follow` for streaming) |
|
|
133
|
-
| `logtap files` | List available log files |
|
|
134
|
-
|
|
135
|
-
### Common Options
|
|
136
|
-
|
|
137
|
-
```bash
|
|
138
|
-
# Server options
|
|
139
|
-
logtap serve --host 0.0.0.0 --port 8000
|
|
140
|
-
logtap serve --api-key mysecret --log-dir /var/log
|
|
141
|
-
|
|
142
|
-
# Client options
|
|
143
|
-
logtap query syslog --server http://host:8000 --api-key mysecret
|
|
144
|
-
logtap query syslog --term "error" --limit 50
|
|
145
|
-
logtap query syslog --regex "pattern" --ignore-case
|
|
146
|
-
logtap query syslog --output json # json, plain, pretty
|
|
147
|
-
```
|
|
148
|
-
|
|
149
|
-
## API Reference
|
|
150
|
-
|
|
151
|
-
### GET /logs
|
|
152
|
-
|
|
153
|
-
Query log file contents.
|
|
154
|
-
|
|
155
|
-
**Parameters:**
|
|
156
|
-
| Parameter | Type | Default | Description |
|
|
157
|
-
|-----------|------|---------|-------------|
|
|
158
|
-
| `filename` | string | `syslog` | Log file name |
|
|
159
|
-
| `term` | string | - | Substring to search for |
|
|
160
|
-
| `regex` | string | - | Regex pattern to match |
|
|
161
|
-
| `limit` | int | `50` | Number of lines (1-1000) |
|
|
162
|
-
| `case_sensitive` | bool | `true` | Case-sensitive search |
|
|
163
|
-
|
|
164
|
-
**Example:**
|
|
165
|
-
```bash
|
|
166
|
-
curl "http://localhost:8000/logs?filename=syslog&term=error&limit=10"
|
|
167
|
-
```
|
|
168
|
-
|
|
169
|
-
**Response:**
|
|
170
|
-
```json
|
|
171
|
-
{
|
|
172
|
-
"lines": ["Jan 8 10:23:45 server error: connection failed", "..."],
|
|
173
|
-
"count": 10,
|
|
174
|
-
"filename": "syslog"
|
|
175
|
-
}
|
|
176
|
-
```
|
|
177
|
-
|
|
178
|
-
### GET /files
|
|
179
|
-
|
|
180
|
-
List available log files.
|
|
181
|
-
|
|
182
|
-
```bash
|
|
183
|
-
curl "http://localhost:8000/files"
|
|
184
|
-
```
|
|
185
|
-
|
|
186
|
-
### GET /health
|
|
187
|
-
|
|
188
|
-
Health check endpoint.
|
|
189
|
-
|
|
190
|
-
```bash
|
|
191
|
-
curl "http://localhost:8000/health"
|
|
192
|
-
```
|
|
193
|
-
|
|
194
|
-
### Authentication
|
|
195
|
-
|
|
196
|
-
If `LOGTAP_API_KEY` is set, all requests require the `X-API-Key` header:
|
|
197
|
-
|
|
198
|
-
```bash
|
|
199
|
-
curl -H "X-API-Key: your-secret" "http://localhost:8000/logs"
|
|
200
|
-
```
|
|
201
|
-
|
|
202
|
-
## Configuration
|
|
203
|
-
|
|
204
|
-
### Environment Variables
|
|
205
|
-
|
|
206
|
-
| Variable | Default | Description |
|
|
207
|
-
|----------|---------|-------------|
|
|
208
|
-
| `LOGTAP_HOST` | `0.0.0.0` | Server bind host |
|
|
209
|
-
| `LOGTAP_PORT` | `8000` | Server bind port |
|
|
210
|
-
| `LOGTAP_LOG_DIRECTORY` | `/var/log` | Log files directory |
|
|
211
|
-
| `LOGTAP_API_KEY` | - | API key (optional) |
|
|
212
|
-
|
|
213
|
-
### Using .env File
|
|
214
|
-
|
|
215
|
-
Create a `.env` file:
|
|
216
|
-
|
|
217
|
-
```env
|
|
218
|
-
LOGTAP_LOG_DIRECTORY=/var/log
|
|
219
|
-
LOGTAP_API_KEY=your-secret-key
|
|
220
|
-
```
|
|
221
|
-
|
|
222
|
-
## Docker Deployment
|
|
223
|
-
|
|
224
|
-
### Using Docker Compose
|
|
225
|
-
|
|
226
|
-
```yaml
|
|
227
|
-
version: "3.8"
|
|
228
|
-
|
|
229
|
-
services:
|
|
230
|
-
logtap:
|
|
231
|
-
image: cainky/logtap
|
|
232
|
-
ports:
|
|
233
|
-
- "8000:8000"
|
|
234
|
-
volumes:
|
|
235
|
-
- /var/log:/var/log:ro
|
|
236
|
-
environment:
|
|
237
|
-
- LOGTAP_API_KEY=your-secret-key
|
|
238
|
-
```
|
|
239
|
-
|
|
240
|
-
```bash
|
|
241
|
-
docker-compose up -d
|
|
242
|
-
```
|
|
243
|
-
|
|
244
|
-
### Using Docker Directly
|
|
245
|
-
|
|
246
|
-
```bash
|
|
247
|
-
docker run -d \
|
|
248
|
-
-p 8000:8000 \
|
|
249
|
-
-v /var/log:/var/log:ro \
|
|
250
|
-
-e LOGTAP_API_KEY=your-secret \
|
|
251
|
-
cainky/logtap
|
|
252
|
-
```
|
|
253
|
-
|
|
254
|
-
## Development
|
|
255
|
-
|
|
256
|
-
### Setup
|
|
257
|
-
|
|
258
|
-
```bash
|
|
259
|
-
# Clone the repository
|
|
260
|
-
git clone https://github.com/cainky/logtap.git
|
|
261
|
-
cd logtap
|
|
262
|
-
|
|
263
|
-
# Install dependencies
|
|
264
|
-
poetry install
|
|
265
|
-
|
|
266
|
-
# Run tests
|
|
267
|
-
poetry run pytest
|
|
268
|
-
|
|
269
|
-
# Run the server in development mode
|
|
270
|
-
poetry run logtap serve --reload
|
|
271
|
-
```
|
|
272
|
-
|
|
273
|
-
### Project Structure
|
|
274
|
-
|
|
275
|
-
```
|
|
276
|
-
logtap/
|
|
277
|
-
├── src/logtap/
|
|
278
|
-
│ ├── api/ # FastAPI server
|
|
279
|
-
│ ├── cli/ # Typer CLI commands
|
|
280
|
-
│ ├── core/ # Core business logic
|
|
281
|
-
│ └── models/ # Pydantic models
|
|
282
|
-
├── tests/
|
|
283
|
-
│ ├── unit/ # Unit tests
|
|
284
|
-
│ └── integration/ # API tests
|
|
285
|
-
├── Dockerfile
|
|
286
|
-
└── docker-compose.yml
|
|
287
|
-
```
|
|
288
|
-
|
|
289
|
-
### Running Tests
|
|
290
|
-
|
|
291
|
-
```bash
|
|
292
|
-
# All tests
|
|
293
|
-
poetry run pytest
|
|
294
|
-
|
|
295
|
-
# With coverage
|
|
296
|
-
poetry run pytest --cov=logtap
|
|
297
|
-
|
|
298
|
-
# Specific test file
|
|
299
|
-
poetry run pytest tests/unit/test_reader.py
|
|
300
|
-
```
|
|
301
|
-
|
|
302
|
-
## Security Considerations
|
|
303
|
-
|
|
304
|
-
- **Path Traversal Protection**: Filenames are validated to prevent `../` attacks
|
|
305
|
-
- **Input Validation**: Search terms limited to 100 chars, limits capped at 1000
|
|
306
|
-
- **Read-Only**: Log directory is mounted read-only in Docker
|
|
307
|
-
- **API Authentication**: Optional API key for production use
|
|
308
|
-
|
|
309
|
-
## License
|
|
310
|
-
|
|
311
|
-
GPL v3 License - see [LICENSE](LICENSE) for details.
|
|
312
|
-
|
|
313
|
-
## Contributing
|
|
314
|
-
|
|
315
|
-
Contributions are welcome! Please open an issue to discuss potential changes before submitting a pull request.
|
|
316
|
-
|
|
317
|
-
## Author
|
|
318
|
-
|
|
319
|
-
Kyle Cain - [@cainky](https://github.com/cainky)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|