log4lab 0.0.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.
- log4lab-0.0.2/LICENSE +21 -0
- log4lab-0.0.2/PKG-INFO +222 -0
- log4lab-0.0.2/README.md +163 -0
- log4lab-0.0.2/log4lab/__init__.py +1 -0
- log4lab-0.0.2/log4lab/cli.py +123 -0
- log4lab-0.0.2/log4lab/export.py +744 -0
- log4lab-0.0.2/log4lab/server.py +155 -0
- log4lab-0.0.2/log4lab/tail.py +283 -0
- log4lab-0.0.2/log4lab/templates/index.html +717 -0
- log4lab-0.0.2/log4lab/templates/runs.html +182 -0
- log4lab-0.0.2/log4lab.egg-info/PKG-INFO +222 -0
- log4lab-0.0.2/log4lab.egg-info/SOURCES.txt +18 -0
- log4lab-0.0.2/log4lab.egg-info/dependency_links.txt +1 -0
- log4lab-0.0.2/log4lab.egg-info/entry_points.txt +2 -0
- log4lab-0.0.2/log4lab.egg-info/requires.txt +14 -0
- log4lab-0.0.2/log4lab.egg-info/top_level.txt +4 -0
- log4lab-0.0.2/pyproject.toml +66 -0
- log4lab-0.0.2/setup.cfg +4 -0
- log4lab-0.0.2/tests/__init__.py +1 -0
- log4lab-0.0.2/tests/test_server.py +214 -0
log4lab-0.0.2/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Thibaut Lamadon
|
|
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.
|
log4lab-0.0.2/PKG-INFO
ADDED
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: log4lab
|
|
3
|
+
Version: 0.0.2
|
|
4
|
+
Summary: A lightweight structured log viewer with live streaming and filters.
|
|
5
|
+
Author: Thibaut Lamadon
|
|
6
|
+
License: MIT License
|
|
7
|
+
|
|
8
|
+
Copyright (c) 2025 Thibaut Lamadon
|
|
9
|
+
|
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
11
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
12
|
+
in the Software without restriction, including without limitation the rights
|
|
13
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
14
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
15
|
+
furnished to do so, subject to the following conditions:
|
|
16
|
+
|
|
17
|
+
The above copyright notice and this permission notice shall be included in all
|
|
18
|
+
copies or substantial portions of the Software.
|
|
19
|
+
|
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
21
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
22
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
23
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
24
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
25
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
26
|
+
SOFTWARE.
|
|
27
|
+
|
|
28
|
+
Project-URL: Homepage, https://github.com/tlamadon/log4lab
|
|
29
|
+
Project-URL: Repository, https://github.com/tlamadon/log4lab
|
|
30
|
+
Project-URL: Issues, https://github.com/tlamadon/log4lab/issues
|
|
31
|
+
Keywords: logging,log-viewer,structured-logs,jsonl,monitoring,dashboard,live-streaming
|
|
32
|
+
Classifier: Development Status :: 4 - Beta
|
|
33
|
+
Classifier: Intended Audience :: Developers
|
|
34
|
+
Classifier: Programming Language :: Python :: 3
|
|
35
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
36
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
37
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
38
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
39
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
40
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
41
|
+
Classifier: Topic :: System :: Logging
|
|
42
|
+
Classifier: Topic :: System :: Monitoring
|
|
43
|
+
Requires-Python: >=3.8
|
|
44
|
+
Description-Content-Type: text/markdown
|
|
45
|
+
License-File: LICENSE
|
|
46
|
+
Requires-Dist: fastapi
|
|
47
|
+
Requires-Dist: uvicorn
|
|
48
|
+
Requires-Dist: jinja2
|
|
49
|
+
Requires-Dist: typer
|
|
50
|
+
Requires-Dist: rich>=13.0.0
|
|
51
|
+
Provides-Extra: test
|
|
52
|
+
Requires-Dist: pytest>=7.0; extra == "test"
|
|
53
|
+
Requires-Dist: pytest-asyncio>=0.21; extra == "test"
|
|
54
|
+
Requires-Dist: pytest-cov>=4.0; extra == "test"
|
|
55
|
+
Requires-Dist: httpx>=0.24; extra == "test"
|
|
56
|
+
Provides-Extra: images
|
|
57
|
+
Requires-Dist: term-image>=0.7.0; extra == "images"
|
|
58
|
+
Dynamic: license-file
|
|
59
|
+
|
|
60
|
+
# Log4Lab
|
|
61
|
+
|
|
62
|
+
A lightweight structured log viewer with live streaming, filtering, and rich content rendering.
|
|
63
|
+
|
|
64
|
+
Log4Lab is a web-based dashboard for viewing and analyzing structured JSON logs in real-time. It provides a clean interface for monitoring application logs with live updates, making it easy to track experiments and debug issues.
|
|
65
|
+
|
|
66
|
+
## Installation
|
|
67
|
+
|
|
68
|
+
### Using pipx (Recommended)
|
|
69
|
+
|
|
70
|
+
Install log4lab as an isolated application:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
pipx install log4lab
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Using pip
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
pip install log4lab
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### From source
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
git clone https://github.com/tlamadon/log4lab.git
|
|
86
|
+
cd log4lab
|
|
87
|
+
pip install -e .
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Quick Start
|
|
91
|
+
|
|
92
|
+
1. **Start the web interface:**
|
|
93
|
+
```bash
|
|
94
|
+
log4lab serve logs/app.log
|
|
95
|
+
```
|
|
96
|
+
Then open http://localhost:8000
|
|
97
|
+
|
|
98
|
+
2. **View logs in terminal:**
|
|
99
|
+
```bash
|
|
100
|
+
log4lab tail logs/app.log
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
3. **Export to HTML:**
|
|
104
|
+
```bash
|
|
105
|
+
log4lab export logs/app.log -o report.html
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Log Format
|
|
109
|
+
|
|
110
|
+
Log4Lab expects JSONL format (one JSON object per line):
|
|
111
|
+
|
|
112
|
+
```json
|
|
113
|
+
{"time": "2025-02-03T10:30:00Z", "level": "INFO", "section": "train", "message": "Training started"}
|
|
114
|
+
{"time": "2025-02-03T10:30:05Z", "level": "WARN", "section": "data", "message": "Missing data point"}
|
|
115
|
+
{"time": "2025-02-03T10:30:10Z", "level": "ERROR", "section": "model", "message": "Model failed to converge"}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### With Rich Content
|
|
119
|
+
|
|
120
|
+
Log4Lab can display various file types when you include a `cache_path` field:
|
|
121
|
+
|
|
122
|
+
```json
|
|
123
|
+
{
|
|
124
|
+
"time": "2025-02-03T10:30:00Z",
|
|
125
|
+
"level": "INFO",
|
|
126
|
+
"section": "train",
|
|
127
|
+
"message": "Training complete",
|
|
128
|
+
"cache_path": "artifacts/results.png",
|
|
129
|
+
"run_name": "experiment_1",
|
|
130
|
+
"accuracy": 0.95
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
**Supported content types:**
|
|
135
|
+
- **Images**: PNG, JPG, SVG, etc. (displayed inline)
|
|
136
|
+
- **Code files**: Python, JavaScript, JSON, YAML, etc. (syntax highlighted, collapsible)
|
|
137
|
+
- **Markdown**: .md files (rendered as HTML, collapsible)
|
|
138
|
+
- **PDFs**: Embedded viewer
|
|
139
|
+
- **Other files**: Download links
|
|
140
|
+
|
|
141
|
+
### Core Fields
|
|
142
|
+
|
|
143
|
+
- `time`: Timestamp in ISO 8601 format
|
|
144
|
+
- `level`: Log level (INFO, WARN, ERROR, DEBUG)
|
|
145
|
+
- `section`: Component or module name
|
|
146
|
+
- `message` or `msg`: Main log message
|
|
147
|
+
- `cache_path`: Path to artifact file
|
|
148
|
+
- `run_name`: Name of the run collection
|
|
149
|
+
- `run_id`: Unique run identifier
|
|
150
|
+
|
|
151
|
+
Any additional fields are shown in the expandable JSON view.
|
|
152
|
+
|
|
153
|
+
## Commands
|
|
154
|
+
|
|
155
|
+
### Serve (Web Interface)
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
log4lab serve [LOGFILE]
|
|
159
|
+
|
|
160
|
+
# Options:
|
|
161
|
+
--host 0.0.0.0 # Bind to all interfaces (default: 127.0.0.1)
|
|
162
|
+
--port 3000 # Custom port (default: 8000)
|
|
163
|
+
--reload # Auto-reload for development
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### Tail (Terminal)
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
log4lab tail [LOGFILE]
|
|
170
|
+
|
|
171
|
+
# Options:
|
|
172
|
+
--level ERROR # Filter by log level
|
|
173
|
+
--open-images # Open images automatically
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### Export
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
log4lab export [LOGFILE] -o output.html
|
|
180
|
+
|
|
181
|
+
# Options:
|
|
182
|
+
-o, --output FILE # Output file path
|
|
183
|
+
-t, --title TEXT # Custom page title
|
|
184
|
+
--no-embed-images # Don't embed images (smaller file)
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
## Filtering
|
|
188
|
+
|
|
189
|
+
The web interface supports:
|
|
190
|
+
- **Level filtering**: Dropdown with all levels found in logs
|
|
191
|
+
- **Text filters**: Section, run name, run ID (partial matching)
|
|
192
|
+
- **Time range**: Last 1m, 5m, 30m, 1h, 6h, 24h
|
|
193
|
+
- **URL filters**: Bookmark and share filtered views
|
|
194
|
+
|
|
195
|
+
```
|
|
196
|
+
# Filter by error logs from last hour
|
|
197
|
+
http://localhost:8000/?level=error&time=3600
|
|
198
|
+
|
|
199
|
+
# View specific run
|
|
200
|
+
http://localhost:8000/?run_name=experiment_1
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
## Use Cases
|
|
204
|
+
|
|
205
|
+
**Machine Learning**: Track training runs with metrics and plots
|
|
206
|
+
```json
|
|
207
|
+
{"run_name": "resnet_training", "epoch": 10, "loss": 0.23, "cache_path": "plots/loss.png"}
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
**Debugging**: Monitor distributed systems across components
|
|
211
|
+
```json
|
|
212
|
+
{"section": "api", "level": "ERROR", "message": "Connection timeout", "duration_ms": 5000}
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
**Research**: Document experiments with artifacts
|
|
216
|
+
```json
|
|
217
|
+
{"run_name": "param_sweep", "message": "Testing lr=0.001", "cache_path": "results.pdf"}
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
## License
|
|
221
|
+
|
|
222
|
+
MIT
|
log4lab-0.0.2/README.md
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
# Log4Lab
|
|
2
|
+
|
|
3
|
+
A lightweight structured log viewer with live streaming, filtering, and rich content rendering.
|
|
4
|
+
|
|
5
|
+
Log4Lab is a web-based dashboard for viewing and analyzing structured JSON logs in real-time. It provides a clean interface for monitoring application logs with live updates, making it easy to track experiments and debug issues.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
### Using pipx (Recommended)
|
|
10
|
+
|
|
11
|
+
Install log4lab as an isolated application:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pipx install log4lab
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
### Using pip
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
pip install log4lab
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### From source
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
git clone https://github.com/tlamadon/log4lab.git
|
|
27
|
+
cd log4lab
|
|
28
|
+
pip install -e .
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Quick Start
|
|
32
|
+
|
|
33
|
+
1. **Start the web interface:**
|
|
34
|
+
```bash
|
|
35
|
+
log4lab serve logs/app.log
|
|
36
|
+
```
|
|
37
|
+
Then open http://localhost:8000
|
|
38
|
+
|
|
39
|
+
2. **View logs in terminal:**
|
|
40
|
+
```bash
|
|
41
|
+
log4lab tail logs/app.log
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
3. **Export to HTML:**
|
|
45
|
+
```bash
|
|
46
|
+
log4lab export logs/app.log -o report.html
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Log Format
|
|
50
|
+
|
|
51
|
+
Log4Lab expects JSONL format (one JSON object per line):
|
|
52
|
+
|
|
53
|
+
```json
|
|
54
|
+
{"time": "2025-02-03T10:30:00Z", "level": "INFO", "section": "train", "message": "Training started"}
|
|
55
|
+
{"time": "2025-02-03T10:30:05Z", "level": "WARN", "section": "data", "message": "Missing data point"}
|
|
56
|
+
{"time": "2025-02-03T10:30:10Z", "level": "ERROR", "section": "model", "message": "Model failed to converge"}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### With Rich Content
|
|
60
|
+
|
|
61
|
+
Log4Lab can display various file types when you include a `cache_path` field:
|
|
62
|
+
|
|
63
|
+
```json
|
|
64
|
+
{
|
|
65
|
+
"time": "2025-02-03T10:30:00Z",
|
|
66
|
+
"level": "INFO",
|
|
67
|
+
"section": "train",
|
|
68
|
+
"message": "Training complete",
|
|
69
|
+
"cache_path": "artifacts/results.png",
|
|
70
|
+
"run_name": "experiment_1",
|
|
71
|
+
"accuracy": 0.95
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
**Supported content types:**
|
|
76
|
+
- **Images**: PNG, JPG, SVG, etc. (displayed inline)
|
|
77
|
+
- **Code files**: Python, JavaScript, JSON, YAML, etc. (syntax highlighted, collapsible)
|
|
78
|
+
- **Markdown**: .md files (rendered as HTML, collapsible)
|
|
79
|
+
- **PDFs**: Embedded viewer
|
|
80
|
+
- **Other files**: Download links
|
|
81
|
+
|
|
82
|
+
### Core Fields
|
|
83
|
+
|
|
84
|
+
- `time`: Timestamp in ISO 8601 format
|
|
85
|
+
- `level`: Log level (INFO, WARN, ERROR, DEBUG)
|
|
86
|
+
- `section`: Component or module name
|
|
87
|
+
- `message` or `msg`: Main log message
|
|
88
|
+
- `cache_path`: Path to artifact file
|
|
89
|
+
- `run_name`: Name of the run collection
|
|
90
|
+
- `run_id`: Unique run identifier
|
|
91
|
+
|
|
92
|
+
Any additional fields are shown in the expandable JSON view.
|
|
93
|
+
|
|
94
|
+
## Commands
|
|
95
|
+
|
|
96
|
+
### Serve (Web Interface)
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
log4lab serve [LOGFILE]
|
|
100
|
+
|
|
101
|
+
# Options:
|
|
102
|
+
--host 0.0.0.0 # Bind to all interfaces (default: 127.0.0.1)
|
|
103
|
+
--port 3000 # Custom port (default: 8000)
|
|
104
|
+
--reload # Auto-reload for development
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Tail (Terminal)
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
log4lab tail [LOGFILE]
|
|
111
|
+
|
|
112
|
+
# Options:
|
|
113
|
+
--level ERROR # Filter by log level
|
|
114
|
+
--open-images # Open images automatically
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Export
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
log4lab export [LOGFILE] -o output.html
|
|
121
|
+
|
|
122
|
+
# Options:
|
|
123
|
+
-o, --output FILE # Output file path
|
|
124
|
+
-t, --title TEXT # Custom page title
|
|
125
|
+
--no-embed-images # Don't embed images (smaller file)
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Filtering
|
|
129
|
+
|
|
130
|
+
The web interface supports:
|
|
131
|
+
- **Level filtering**: Dropdown with all levels found in logs
|
|
132
|
+
- **Text filters**: Section, run name, run ID (partial matching)
|
|
133
|
+
- **Time range**: Last 1m, 5m, 30m, 1h, 6h, 24h
|
|
134
|
+
- **URL filters**: Bookmark and share filtered views
|
|
135
|
+
|
|
136
|
+
```
|
|
137
|
+
# Filter by error logs from last hour
|
|
138
|
+
http://localhost:8000/?level=error&time=3600
|
|
139
|
+
|
|
140
|
+
# View specific run
|
|
141
|
+
http://localhost:8000/?run_name=experiment_1
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Use Cases
|
|
145
|
+
|
|
146
|
+
**Machine Learning**: Track training runs with metrics and plots
|
|
147
|
+
```json
|
|
148
|
+
{"run_name": "resnet_training", "epoch": 10, "loss": 0.23, "cache_path": "plots/loss.png"}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
**Debugging**: Monitor distributed systems across components
|
|
152
|
+
```json
|
|
153
|
+
{"section": "api", "level": "ERROR", "message": "Connection timeout", "duration_ms": 5000}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
**Research**: Document experiments with artifacts
|
|
157
|
+
```json
|
|
158
|
+
{"run_name": "param_sweep", "message": "Testing lr=0.001", "cache_path": "results.pdf"}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## License
|
|
162
|
+
|
|
163
|
+
MIT
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.1.0"
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import typer
|
|
2
|
+
import uvicorn
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Optional
|
|
5
|
+
from . import server
|
|
6
|
+
from .tail import LogTailer
|
|
7
|
+
from .export import export_logs_to_html
|
|
8
|
+
|
|
9
|
+
app = typer.Typer(help="Log4Lab — a lightweight structured log dashboard")
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@app.command()
|
|
13
|
+
def serve(
|
|
14
|
+
logfile: Path = typer.Argument(
|
|
15
|
+
"logs/app.log",
|
|
16
|
+
exists=False,
|
|
17
|
+
help="Path to the JSONL log file to stream (default: logs/app.log)"
|
|
18
|
+
),
|
|
19
|
+
host: str = typer.Option("127.0.0.1", help="Host to bind to"),
|
|
20
|
+
port: int = typer.Option(8000, help="Port to listen on"),
|
|
21
|
+
reload: bool = typer.Option(False, help="Enable auto-reload"),
|
|
22
|
+
):
|
|
23
|
+
"""Start the Log4Lab web server."""
|
|
24
|
+
server.set_log_path(logfile)
|
|
25
|
+
try:
|
|
26
|
+
uvicorn.run(
|
|
27
|
+
"log4lab.server:app",
|
|
28
|
+
host=host,
|
|
29
|
+
port=port,
|
|
30
|
+
reload=reload,
|
|
31
|
+
timeout_graceful_shutdown=0 # Exit immediately on CTRL-C
|
|
32
|
+
)
|
|
33
|
+
except KeyboardInterrupt:
|
|
34
|
+
pass # Exit immediately without waiting
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@app.command()
|
|
38
|
+
def tail(
|
|
39
|
+
logfile: Path = typer.Argument(
|
|
40
|
+
"logs/app.log",
|
|
41
|
+
help="Path to the JSONL log file to tail"
|
|
42
|
+
),
|
|
43
|
+
level: Optional[str] = typer.Option(None, "--level", "-l", help="Filter by log level (e.g., INFO, ERROR)"),
|
|
44
|
+
section: Optional[str] = typer.Option(None, "--section", "-s", help="Filter by section name"),
|
|
45
|
+
run_name: Optional[str] = typer.Option(None, "--run-name", "-r", help="Filter by run name"),
|
|
46
|
+
run_id: Optional[str] = typer.Option(None, "--run-id", help="Filter by run ID"),
|
|
47
|
+
group: Optional[str] = typer.Option(None, "--group", "-g", help="Filter by group name"),
|
|
48
|
+
time_range: Optional[int] = typer.Option(None, "--time-range", "-t", help="Only show logs from last N seconds"),
|
|
49
|
+
follow: bool = typer.Option(True, "--follow/--no-follow", "-f", help="Follow log file for new entries"),
|
|
50
|
+
show_images: bool = typer.Option(True, "--images/--no-images", help="Try to show images inline in terminal"),
|
|
51
|
+
open_images: bool = typer.Option(False, "--open-images", help="Open images in system default viewer (Preview, etc.)"),
|
|
52
|
+
):
|
|
53
|
+
"""Tail logs to the terminal with rich formatting and filters."""
|
|
54
|
+
tailer = LogTailer(
|
|
55
|
+
log_path=logfile,
|
|
56
|
+
level=level,
|
|
57
|
+
section=section,
|
|
58
|
+
run_name=run_name,
|
|
59
|
+
run_id=run_id,
|
|
60
|
+
group=group,
|
|
61
|
+
time_range=time_range,
|
|
62
|
+
follow=follow,
|
|
63
|
+
show_images=show_images,
|
|
64
|
+
open_images=open_images,
|
|
65
|
+
)
|
|
66
|
+
try:
|
|
67
|
+
tailer.tail()
|
|
68
|
+
except KeyboardInterrupt:
|
|
69
|
+
pass # Clean exit on Ctrl+C
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
@app.command()
|
|
73
|
+
def export(
|
|
74
|
+
logfile: Path = typer.Argument(
|
|
75
|
+
"logs/app.log",
|
|
76
|
+
help="Path to the JSONL log file to export"
|
|
77
|
+
),
|
|
78
|
+
output: Path = typer.Option(
|
|
79
|
+
"logs-export.html",
|
|
80
|
+
"--output", "-o",
|
|
81
|
+
help="Output HTML file path"
|
|
82
|
+
),
|
|
83
|
+
title: str = typer.Option(
|
|
84
|
+
"Log4Lab Export",
|
|
85
|
+
"--title", "-t",
|
|
86
|
+
help="Title for the HTML page"
|
|
87
|
+
),
|
|
88
|
+
no_embed_images: bool = typer.Option(
|
|
89
|
+
False,
|
|
90
|
+
"--no-embed-images",
|
|
91
|
+
help="Don't embed images as base64 (reduces file size but images won't be included)"
|
|
92
|
+
),
|
|
93
|
+
):
|
|
94
|
+
"""Export logs to a self-contained HTML file with embedded images and working filters."""
|
|
95
|
+
if not logfile.exists():
|
|
96
|
+
typer.echo(f"Error: Log file '{logfile}' does not exist.", err=True)
|
|
97
|
+
raise typer.Exit(1)
|
|
98
|
+
|
|
99
|
+
typer.echo(f"Exporting logs from '{logfile}' to '{output}'...")
|
|
100
|
+
|
|
101
|
+
try:
|
|
102
|
+
export_logs_to_html(
|
|
103
|
+
log_path=logfile,
|
|
104
|
+
output_path=output,
|
|
105
|
+
title=title,
|
|
106
|
+
embed_images=not no_embed_images
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
file_size = output.stat().st_size
|
|
110
|
+
size_mb = file_size / (1024 * 1024)
|
|
111
|
+
|
|
112
|
+
typer.echo(f"✓ Export complete! File size: {size_mb:.2f} MB")
|
|
113
|
+
typer.echo(f"✓ Saved to: {output.absolute()}")
|
|
114
|
+
typer.echo(f"\nOpen the file in your browser to view the logs with working filters.")
|
|
115
|
+
|
|
116
|
+
except Exception as e:
|
|
117
|
+
typer.echo(f"Error during export: {e}", err=True)
|
|
118
|
+
raise typer.Exit(1)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
if __name__ == "__main__":
|
|
122
|
+
app()
|
|
123
|
+
|