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.
@@ -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
+ [![PyPI version](https://badge.fury.io/py/logtap.svg)](https://badge.fury.io/py/logtap)
47
+ [![Tests](https://github.com/cainky/logtap/actions/workflows/tests.yml/badge.svg)](https://github.com/cainky/logtap/actions/workflows/tests.yml)
48
+ [![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)
49
+ [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](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=QGputf7HjtjMkCnDHsPNBBqZ27DYZ-7a2_biF5Rvk6c,179
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=BBphxLKvk7yIiLyQ3tdiagqtjc7xFixd_FUzNFjlC0c,1176
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=Ak-z2ChqZZ7FgHdu1JDo3v5aDBPR3VIICyXTLDBf75E,462
9
- logtap/api/routes/logs.py,sha256=XpRAd4fZmVyylz6bHCHm4y0Y2GofSquH6j5WJP3Jyao,8467
10
- logtap/api/routes/parsed.py,sha256=XVvkKBE_hQvfJyrDBBPR_PpVxvof-y4B77xKe9Rr0Qk,3367
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=fWSuQdin9G-RC7Oqzesfp93WZI1-v7227P-WWTsxtIQ,1045
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=dwPRXub1dcRwKulDt_qNa2waQm1YPOxIg0QokAK6Gyw,3648
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=Nk86jHqEfI4H96fk-1rjbC5sBwfzls43hyOhnRV6rxI,1359
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=45J-Xw1Gb35uP5188wxy2QZlyy3Fh18fpAFizZnpi3A,1850
32
- logtap-0.3.0.dist-info/METADATA,sha256=O1zoWpiUNGvVxjSLfAGPfZKnxgy4CFZV1p3_olMe7rM,7466
33
- logtap-0.3.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
34
- logtap-0.3.0.dist-info/entry_points.txt,sha256=tuAit8kt97yjtACQKvN35wWozp4KhSju_gfDhSS1IrM,47
35
- logtap-0.3.0.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
36
- logtap-0.3.0.dist-info/RECORD,,
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,,
@@ -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
- [![PyPI version](https://badge.fury.io/py/logtap.svg)](https://badge.fury.io/py/logtap)
47
- [![Tests](https://github.com/cainky/logtap/actions/workflows/tests.yml/badge.svg)](https://github.com/cainky/logtap/actions/workflows/tests.yml)
48
- [![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)
49
- [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](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