debug-log-server 0.1.0__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,402 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: debug-log-server
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Debug log collection and query service based on FastAPI
|
|
5
|
+
Author-email: dengshi <dengshi@mercator.cn>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/dengshihub/debuglogger
|
|
8
|
+
Project-URL: Documentation, https://github.com/dengshihub/debuglogger#readme
|
|
9
|
+
Project-URL: Repository, https://github.com/dengshihub/debuglogger.git
|
|
10
|
+
Project-URL: Issues, https://github.com/dengshihub/debuglogger/issues
|
|
11
|
+
Keywords: debug,log,logging,fastapi,monitoring,error-tracking
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Requires-Python: >=3.8
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
Requires-Dist: fastapi>=0.104.0
|
|
24
|
+
Requires-Dist: uvicorn[standard]>=0.24.0
|
|
25
|
+
Requires-Dist: pydantic>=2.0.0
|
|
26
|
+
Requires-Dist: python-jose[cryptography]>=3.3.0
|
|
27
|
+
Provides-Extra: dev
|
|
28
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
29
|
+
Requires-Dist: httpx>=0.24.0; extra == "dev"
|
|
30
|
+
Requires-Dist: black>=23.0.0; extra == "dev"
|
|
31
|
+
Requires-Dist: flake8>=6.0.0; extra == "dev"
|
|
32
|
+
|
|
33
|
+
# Debug Log Server
|
|
34
|
+
|
|
35
|
+
Debug log collection and query service based on FastAPI.
|
|
36
|
+
|
|
37
|
+
## 📦 Installation
|
|
38
|
+
|
|
39
|
+
### From PyPI
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
pip install debug-log-server
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### From Source
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
git clone https://github.com/dengshihub/debuglogger.git
|
|
49
|
+
cd debuglogger/backend
|
|
50
|
+
pip install -e .
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## 🚀 Quick Start
|
|
54
|
+
|
|
55
|
+
### Start Server
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
# Basic usage
|
|
59
|
+
debug-log-server
|
|
60
|
+
|
|
61
|
+
# With custom log directory
|
|
62
|
+
DEBUG_LOG_DIR=/var/log/debug-logs debug-log-server
|
|
63
|
+
|
|
64
|
+
# With custom port
|
|
65
|
+
debug-log-server --port 9000
|
|
66
|
+
|
|
67
|
+
# With custom host and port
|
|
68
|
+
debug-log-server --host 0.0.0.0 --port 8000
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Environment Variables
|
|
72
|
+
|
|
73
|
+
- `DEBUG_LOG_DIR`: Log storage directory (default: `./debug-logs`)
|
|
74
|
+
- `DEBUG_AUTH`: Enable authentication debug mode (default: `false`)
|
|
75
|
+
|
|
76
|
+
## 📖 API Documentation
|
|
77
|
+
|
|
78
|
+
### POST /api/debug-log
|
|
79
|
+
|
|
80
|
+
Receive debug logs from frontend.
|
|
81
|
+
|
|
82
|
+
**Request Body:**
|
|
83
|
+
```json
|
|
84
|
+
{
|
|
85
|
+
"logs": [
|
|
86
|
+
{
|
|
87
|
+
"sessionId": "session-123",
|
|
88
|
+
"timestamp": 1717564800000,
|
|
89
|
+
"type": "console",
|
|
90
|
+
"level": "log",
|
|
91
|
+
"message": "Hello World",
|
|
92
|
+
"url": "https://example.com"
|
|
93
|
+
}
|
|
94
|
+
],
|
|
95
|
+
"source": "frontend"
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
**Response:**
|
|
100
|
+
```json
|
|
101
|
+
{
|
|
102
|
+
"status": "ok",
|
|
103
|
+
"received": 1
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### GET /api/debug-log/list
|
|
108
|
+
|
|
109
|
+
List all log files.
|
|
110
|
+
|
|
111
|
+
**Query Parameters:**
|
|
112
|
+
- `source`: Filter by source (optional)
|
|
113
|
+
|
|
114
|
+
**Response:**
|
|
115
|
+
```json
|
|
116
|
+
{
|
|
117
|
+
"logs": [
|
|
118
|
+
{
|
|
119
|
+
"name": "frontend.log",
|
|
120
|
+
"path": "/var/log/debug-logs/frontend/frontend.log",
|
|
121
|
+
"size": 1024,
|
|
122
|
+
"modified": "2024-06-05T10:30:00"
|
|
123
|
+
}
|
|
124
|
+
]
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### GET /api/debug-log/{source}
|
|
129
|
+
|
|
130
|
+
Get logs from a specific source.
|
|
131
|
+
|
|
132
|
+
**Query Parameters:**
|
|
133
|
+
- `limit`: Maximum number of entries to return (default: 100)
|
|
134
|
+
|
|
135
|
+
**Response:**
|
|
136
|
+
```json
|
|
137
|
+
{
|
|
138
|
+
"source": "frontend",
|
|
139
|
+
"entries": [
|
|
140
|
+
{
|
|
141
|
+
"sessionId": "session-123",
|
|
142
|
+
"timestamp": 1717564800000,
|
|
143
|
+
"type": "console",
|
|
144
|
+
"level": "log",
|
|
145
|
+
"message": "Hello World"
|
|
146
|
+
}
|
|
147
|
+
]
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## 🐳 Docker
|
|
152
|
+
|
|
153
|
+
### Build Image
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
docker build -t debug-log-server .
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### Run Container
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
docker run -d \
|
|
163
|
+
-p 8000:8000 \
|
|
164
|
+
-v /var/log/debug-logs:/app/debug-logs \
|
|
165
|
+
--name debug-log-server \
|
|
166
|
+
debug-log-server
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Docker Compose
|
|
170
|
+
|
|
171
|
+
```yaml
|
|
172
|
+
version: '3.8'
|
|
173
|
+
|
|
174
|
+
services:
|
|
175
|
+
debug-log-server:
|
|
176
|
+
image: debug-log-server:latest
|
|
177
|
+
ports:
|
|
178
|
+
- "8000:8000"
|
|
179
|
+
volumes:
|
|
180
|
+
- ./debug-logs:/app/debug-logs
|
|
181
|
+
environment:
|
|
182
|
+
- DEBUG_LOG_DIR=/app/debug-logs
|
|
183
|
+
restart: unless-stopped
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
## 🔧 Configuration
|
|
187
|
+
|
|
188
|
+
### Basic Configuration
|
|
189
|
+
|
|
190
|
+
```python
|
|
191
|
+
# config.py
|
|
192
|
+
import os
|
|
193
|
+
|
|
194
|
+
LOG_DIR = os.getenv("DEBUG_LOG_DIR", "./debug-logs")
|
|
195
|
+
HOST = os.getenv("HOST", "0.0.0.0")
|
|
196
|
+
PORT = int(os.getenv("PORT", "8000"))
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### Advanced Configuration
|
|
200
|
+
|
|
201
|
+
```python
|
|
202
|
+
# config.py
|
|
203
|
+
from pydantic import BaseSettings
|
|
204
|
+
|
|
205
|
+
class Settings(BaseSettings):
|
|
206
|
+
log_dir: str = "./debug-logs"
|
|
207
|
+
host: str = "0.0.0.0"
|
|
208
|
+
port: int = 8000
|
|
209
|
+
debug_auth: bool = False
|
|
210
|
+
|
|
211
|
+
class Config:
|
|
212
|
+
env_prefix = "DEBUG_"
|
|
213
|
+
|
|
214
|
+
settings = Settings()
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
## 🔒 Security
|
|
218
|
+
|
|
219
|
+
### Authentication
|
|
220
|
+
|
|
221
|
+
The server supports JWT authentication for protected routes:
|
|
222
|
+
|
|
223
|
+
```python
|
|
224
|
+
from fastapi import Depends
|
|
225
|
+
from debug_log_server import debug_auth
|
|
226
|
+
|
|
227
|
+
@app.get("/api/protected")
|
|
228
|
+
async def protected_route(token: str = Depends(debug_auth)):
|
|
229
|
+
return {"message": "This is a protected route"}
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
### CORS
|
|
233
|
+
|
|
234
|
+
CORS is enabled by default for all origins. For production, configure it properly:
|
|
235
|
+
|
|
236
|
+
```python
|
|
237
|
+
from fastapi.middleware.cors import CORSMiddleware
|
|
238
|
+
|
|
239
|
+
app.add_middleware(
|
|
240
|
+
CORSMiddleware,
|
|
241
|
+
allow_origins=["https://your-domain.com"], # Specify allowed origins
|
|
242
|
+
allow_credentials=True,
|
|
243
|
+
allow_methods=["GET", "POST"],
|
|
244
|
+
allow_headers=["*"],
|
|
245
|
+
)
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
## 📊 Monitoring
|
|
249
|
+
|
|
250
|
+
### Health Check
|
|
251
|
+
|
|
252
|
+
```bash
|
|
253
|
+
curl http://localhost:8000/
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
### Log Statistics
|
|
257
|
+
|
|
258
|
+
```bash
|
|
259
|
+
curl http://localhost:8000/api/debug-log/list
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
## 🧪 Testing
|
|
263
|
+
|
|
264
|
+
### Install Test Dependencies
|
|
265
|
+
|
|
266
|
+
```bash
|
|
267
|
+
pip install debug-log-server[dev]
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
### Run Tests
|
|
271
|
+
|
|
272
|
+
```bash
|
|
273
|
+
pytest tests/
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
### Test with httpx
|
|
277
|
+
|
|
278
|
+
```python
|
|
279
|
+
from fastapi.testclient import TestClient
|
|
280
|
+
from main import app
|
|
281
|
+
|
|
282
|
+
client = TestClient(app)
|
|
283
|
+
|
|
284
|
+
def test_receive_log():
|
|
285
|
+
response = client.post("/api/debug-log", json={
|
|
286
|
+
"logs": [{
|
|
287
|
+
"sessionId": "test",
|
|
288
|
+
"timestamp": 1717564800000,
|
|
289
|
+
"type": "console",
|
|
290
|
+
"message": "test"
|
|
291
|
+
}],
|
|
292
|
+
"source": "test"
|
|
293
|
+
})
|
|
294
|
+
assert response.status_code == 200
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
## 📝 Examples
|
|
298
|
+
|
|
299
|
+
### Python Client
|
|
300
|
+
|
|
301
|
+
```python
|
|
302
|
+
import requests
|
|
303
|
+
|
|
304
|
+
# Send logs
|
|
305
|
+
response = requests.post('http://localhost:8000/api/debug-log', json={
|
|
306
|
+
"logs": [{
|
|
307
|
+
"sessionId": "session-123",
|
|
308
|
+
"timestamp": 1717564800000,
|
|
309
|
+
"type": "console",
|
|
310
|
+
"level": "log",
|
|
311
|
+
"message": "Hello from Python"
|
|
312
|
+
}],
|
|
313
|
+
"source": "python-client"
|
|
314
|
+
})
|
|
315
|
+
|
|
316
|
+
print(response.json())
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
### JavaScript Client
|
|
320
|
+
|
|
321
|
+
```javascript
|
|
322
|
+
// Using fetch
|
|
323
|
+
fetch('http://localhost:8000/api/debug-log', {
|
|
324
|
+
method: 'POST',
|
|
325
|
+
headers: { 'Content-Type': 'application/json' },
|
|
326
|
+
body: JSON.stringify({
|
|
327
|
+
logs: [{
|
|
328
|
+
sessionId: 'session-123',
|
|
329
|
+
timestamp: Date.now(),
|
|
330
|
+
type: 'console',
|
|
331
|
+
level: 'log',
|
|
332
|
+
message: 'Hello from JavaScript'
|
|
333
|
+
}],
|
|
334
|
+
source: 'javascript-client'
|
|
335
|
+
})
|
|
336
|
+
})
|
|
337
|
+
.then(response => response.json())
|
|
338
|
+
.then(data => console.log(data));
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
## 🚀 Deployment
|
|
342
|
+
|
|
343
|
+
### Systemd Service
|
|
344
|
+
|
|
345
|
+
```ini
|
|
346
|
+
# /etc/systemd/system/debug-log-server.service
|
|
347
|
+
[Unit]
|
|
348
|
+
Description=Debug Log Server
|
|
349
|
+
After=network.target
|
|
350
|
+
|
|
351
|
+
[Service]
|
|
352
|
+
Type=simple
|
|
353
|
+
User=www-data
|
|
354
|
+
WorkingDirectory=/opt/debug-log-server
|
|
355
|
+
Environment="DEBUG_LOG_DIR=/var/log/debug-logs"
|
|
356
|
+
ExecStart=/usr/local/bin/debug-log-server
|
|
357
|
+
Restart=always
|
|
358
|
+
|
|
359
|
+
[Install]
|
|
360
|
+
WantedBy=multi-user.target
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
```bash
|
|
364
|
+
sudo systemctl enable debug-log-server
|
|
365
|
+
sudo systemctl start debug-log-server
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
### Nginx Reverse Proxy
|
|
369
|
+
|
|
370
|
+
```nginx
|
|
371
|
+
server {
|
|
372
|
+
listen 80;
|
|
373
|
+
server_name logs.your-domain.com;
|
|
374
|
+
|
|
375
|
+
location / {
|
|
376
|
+
proxy_pass http://127.0.0.1:8000;
|
|
377
|
+
proxy_set_header Host $host;
|
|
378
|
+
proxy_set_header X-Real-IP $remote_addr;
|
|
379
|
+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
380
|
+
proxy_set_header X-Forwarded-Proto $scheme;
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
## 📚 Related Projects
|
|
386
|
+
|
|
387
|
+
- **@dengshi/debug-logger**: Frontend log collection library (npm)
|
|
388
|
+
- **playwright-collector**: Playwright-based log collector
|
|
389
|
+
|
|
390
|
+
## 📄 License
|
|
391
|
+
|
|
392
|
+
MIT License - see [LICENSE](LICENSE) file for details.
|
|
393
|
+
|
|
394
|
+
## 🤝 Contributing
|
|
395
|
+
|
|
396
|
+
Contributions are welcome! Please read our [Contributing Guide](../CONTRIBUTING.md) for details.
|
|
397
|
+
|
|
398
|
+
## 📞 Support
|
|
399
|
+
|
|
400
|
+
- 📖 [Documentation](https://github.com/dengshihub/debuglogger#readme)
|
|
401
|
+
- 🐛 [Issue Tracker](https://github.com/dengshihub/debuglogger/issues)
|
|
402
|
+
- 💬 [Discussions](https://github.com/dengshihub/debuglogger/discussions)
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
static/index.html,sha256=ThsBvwX4abtz0yoqbQtch3c0AROeMP8gWHGj87TjUAM,13983
|
|
2
|
+
debug_log_server-0.1.0.dist-info/METADATA,sha256=0mwA6JFdYvKSeN2g1d4ddTPu2UzVtJjvCbfOvmG3FiY,8388
|
|
3
|
+
debug_log_server-0.1.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
4
|
+
debug_log_server-0.1.0.dist-info/entry_points.txt,sha256=XdEvilgx2UAIVk-6Tkk3W-K9s0ksVWM-CuVAc_KinKs,47
|
|
5
|
+
debug_log_server-0.1.0.dist-info/top_level.txt,sha256=ZSyr8N5s1w9m9ysX1kCSA7hJCb6YZCYf62FJQ_LmzGI,7
|
|
6
|
+
debug_log_server-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
static
|
static/index.html
ADDED
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="zh-CN">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>调试系统 - 前端演示</title>
|
|
7
|
+
<style>
|
|
8
|
+
body {
|
|
9
|
+
font-family: Arial, sans-serif;
|
|
10
|
+
max-width: 800px;
|
|
11
|
+
margin: 50px auto;
|
|
12
|
+
padding: 20px;
|
|
13
|
+
background-color: #f5f5f5;
|
|
14
|
+
}
|
|
15
|
+
.container {
|
|
16
|
+
background: white;
|
|
17
|
+
padding: 30px;
|
|
18
|
+
border-radius: 8px;
|
|
19
|
+
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
|
20
|
+
}
|
|
21
|
+
h1 { color: #333; }
|
|
22
|
+
.section {
|
|
23
|
+
margin: 20px 0;
|
|
24
|
+
padding: 15px;
|
|
25
|
+
border: 1px solid #ddd;
|
|
26
|
+
border-radius: 5px;
|
|
27
|
+
}
|
|
28
|
+
button {
|
|
29
|
+
background: #007bff;
|
|
30
|
+
color: white;
|
|
31
|
+
border: none;
|
|
32
|
+
padding: 10px 20px;
|
|
33
|
+
border-radius: 5px;
|
|
34
|
+
cursor: pointer;
|
|
35
|
+
margin: 5px;
|
|
36
|
+
}
|
|
37
|
+
button:hover { background: #0056b3; }
|
|
38
|
+
button.error { background: #dc3545; }
|
|
39
|
+
button.error:hover { background: #c82333; }
|
|
40
|
+
button.success { background: #28a745; }
|
|
41
|
+
button.success:hover { background: #218838; }
|
|
42
|
+
.log-output {
|
|
43
|
+
background: #f8f9fa;
|
|
44
|
+
padding: 15px;
|
|
45
|
+
border-radius: 5px;
|
|
46
|
+
margin-top: 10px;
|
|
47
|
+
max-height: 200px;
|
|
48
|
+
overflow-y: auto;
|
|
49
|
+
font-family: monospace;
|
|
50
|
+
font-size: 12px;
|
|
51
|
+
}
|
|
52
|
+
.status {
|
|
53
|
+
padding: 10px;
|
|
54
|
+
border-radius: 5px;
|
|
55
|
+
margin: 10px 0;
|
|
56
|
+
}
|
|
57
|
+
.status.success { background: #d4edda; color: #155724; }
|
|
58
|
+
.status.error { background: #f8d7da; color: #721c24; }
|
|
59
|
+
.status.info { background: #d1ecf1; color: #0c5460; }
|
|
60
|
+
</style>
|
|
61
|
+
</head>
|
|
62
|
+
<body>
|
|
63
|
+
<div class="container">
|
|
64
|
+
<h1>🔧 调试系统 - 前端演示</h1>
|
|
65
|
+
|
|
66
|
+
<div class="section">
|
|
67
|
+
<h2>📡 服务状态</h2>
|
|
68
|
+
<button onclick="checkService()">检查服务状态</button>
|
|
69
|
+
<div id="serviceStatus" class="status info">等待检查...</div>
|
|
70
|
+
</div>
|
|
71
|
+
|
|
72
|
+
<div class="section">
|
|
73
|
+
<h2>📝 基础日志测试</h2>
|
|
74
|
+
<button onclick="sendInfoLog()">发送 Info 日志</button>
|
|
75
|
+
<button onclick="sendWarnLog()">发送 Warn 日志</button>
|
|
76
|
+
<button class="error" onclick="sendErrorLog()">发送 Error 日志</button>
|
|
77
|
+
<button class="success" onclick="sendSuccessLog()">发送 Success 日志</button>
|
|
78
|
+
</div>
|
|
79
|
+
|
|
80
|
+
<div class="section">
|
|
81
|
+
<h2>🌐 网络请求测试</h2>
|
|
82
|
+
<button onclick="testApiCall()">测试 API 调用</button>
|
|
83
|
+
<button onclick="testFailedApi()">测试失败 API</button>
|
|
84
|
+
</div>
|
|
85
|
+
|
|
86
|
+
<div class="section">
|
|
87
|
+
<h2>🎯 用户行为测试</h2>
|
|
88
|
+
<button onclick="simulateUserClick()">模拟用户点击</button>
|
|
89
|
+
<button onclick="simulateFormSubmit()">模拟表单提交</button>
|
|
90
|
+
<button onclick="simulateNavigation()">模拟页面跳转</button>
|
|
91
|
+
</div>
|
|
92
|
+
|
|
93
|
+
<div class="section">
|
|
94
|
+
<h2>📊 查看日志</h2>
|
|
95
|
+
<button onclick="viewLogs()">查看所有日志</button>
|
|
96
|
+
<button onclick="viewRecentLogs()">查看最近日志</button>
|
|
97
|
+
<div id="logOutput" class="log-output">日志输出区域...</div>
|
|
98
|
+
</div>
|
|
99
|
+
|
|
100
|
+
<div class="section">
|
|
101
|
+
<h2>🔍 性能监控</h2>
|
|
102
|
+
<button onclick="measurePerformance()">测量页面性能</button>
|
|
103
|
+
<button onclick="measureApiPerformance()">测量 API 性能</button>
|
|
104
|
+
</div>
|
|
105
|
+
</div>
|
|
106
|
+
|
|
107
|
+
<script>
|
|
108
|
+
// 配置
|
|
109
|
+
const DEBUG_ENDPOINT = '/api/debug-log';
|
|
110
|
+
const sessionId = 'session-' + Date.now() + '-' + Math.random().toString(36).substr(2, 9);
|
|
111
|
+
|
|
112
|
+
// 基础日志发送函数
|
|
113
|
+
async function sendLog(type, level, message, extra = {}) {
|
|
114
|
+
const logData = {
|
|
115
|
+
sessionId: sessionId,
|
|
116
|
+
timestamp: Date.now(),
|
|
117
|
+
type: type,
|
|
118
|
+
level: level,
|
|
119
|
+
message: message,
|
|
120
|
+
url: window.location.href,
|
|
121
|
+
...extra
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
try {
|
|
125
|
+
const response = await fetch(DEBUG_ENDPOINT, {
|
|
126
|
+
method: 'POST',
|
|
127
|
+
headers: { 'Content-Type': 'application/json' },
|
|
128
|
+
body: JSON.stringify({ logs: [logData], source: 'example' })
|
|
129
|
+
});
|
|
130
|
+
const result = await response.json();
|
|
131
|
+
console.log('日志发送成功:', result);
|
|
132
|
+
return result;
|
|
133
|
+
} catch (error) {
|
|
134
|
+
console.error('日志发送失败:', error);
|
|
135
|
+
throw error;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// 检查服务状态
|
|
140
|
+
async function checkService() {
|
|
141
|
+
const statusDiv = document.getElementById('serviceStatus');
|
|
142
|
+
try {
|
|
143
|
+
const response = await fetch('/api/debug-log/list');
|
|
144
|
+
if (response.ok) {
|
|
145
|
+
statusDiv.className = 'status success';
|
|
146
|
+
statusDiv.textContent = '✅ 调试服务运行正常';
|
|
147
|
+
sendLog('system', 'info', '调试服务检查通过');
|
|
148
|
+
} else {
|
|
149
|
+
throw new Error('服务响应异常');
|
|
150
|
+
}
|
|
151
|
+
} catch (error) {
|
|
152
|
+
statusDiv.className = 'status error';
|
|
153
|
+
statusDiv.textContent = '❌ 调试服务无法连接: ' + error.message;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// 发送不同级别的日志
|
|
158
|
+
function sendInfoLog() {
|
|
159
|
+
sendLog('console', 'info', '这是一条信息日志', {
|
|
160
|
+
userId: 'user-123',
|
|
161
|
+
action: 'info_test'
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function sendWarnLog() {
|
|
166
|
+
sendLog('console', 'warn', '这是一条警告日志', {
|
|
167
|
+
warningType: 'deprecated_api',
|
|
168
|
+
apiVersion: 'v1'
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function sendErrorLog() {
|
|
173
|
+
sendLog('console', 'error', '这是一条错误日志', {
|
|
174
|
+
errorCode: 'ERR_001',
|
|
175
|
+
errorMessage: '模拟的错误信息'
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function sendSuccessLog() {
|
|
180
|
+
sendLog('console', 'info', '操作成功完成', {
|
|
181
|
+
action: 'user_action',
|
|
182
|
+
status: 'success'
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// 测试 API 调用
|
|
187
|
+
async function testApiCall() {
|
|
188
|
+
const startTime = Date.now();
|
|
189
|
+
try {
|
|
190
|
+
const response = await fetch('/api/debug-log/list');
|
|
191
|
+
const duration = Date.now() - startTime;
|
|
192
|
+
|
|
193
|
+
await sendLog('network-request', 'info', 'API 调用成功', {
|
|
194
|
+
url: '/api/debug-log/list',
|
|
195
|
+
method: 'GET',
|
|
196
|
+
status: response.status,
|
|
197
|
+
duration: duration
|
|
198
|
+
});
|
|
199
|
+
} catch (error) {
|
|
200
|
+
const duration = Date.now() - startTime;
|
|
201
|
+
await sendLog('network-request', 'error', 'API 调用失败', {
|
|
202
|
+
url: '/api/debug-log/list',
|
|
203
|
+
method: 'GET',
|
|
204
|
+
error: error.message,
|
|
205
|
+
duration: duration
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
async function testFailedApi() {
|
|
211
|
+
const startTime = Date.now();
|
|
212
|
+
try {
|
|
213
|
+
const response = await fetch('/api/nonexistent');
|
|
214
|
+
const duration = Date.now() - startTime;
|
|
215
|
+
|
|
216
|
+
await sendLog('network-request', 'error', '预期中的 API 失败', {
|
|
217
|
+
url: '/api/nonexistent',
|
|
218
|
+
method: 'GET',
|
|
219
|
+
status: response.status,
|
|
220
|
+
duration: duration
|
|
221
|
+
});
|
|
222
|
+
} catch (error) {
|
|
223
|
+
const duration = Date.now() - startTime;
|
|
224
|
+
await sendLog('network-request', 'error', 'API 调用异常', {
|
|
225
|
+
url: '/api/nonexistent',
|
|
226
|
+
method: 'GET',
|
|
227
|
+
error: error.message,
|
|
228
|
+
duration: duration
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// 模拟用户行为
|
|
234
|
+
function simulateUserClick() {
|
|
235
|
+
sendLog('user-action', 'info', '用户点击了按钮', {
|
|
236
|
+
elementId: 'test-button',
|
|
237
|
+
elementType: 'button',
|
|
238
|
+
timestamp: Date.now()
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
function simulateFormSubmit() {
|
|
243
|
+
sendLog('user-action', 'info', '用户提交了表单', {
|
|
244
|
+
formId: 'test-form',
|
|
245
|
+
formData: {
|
|
246
|
+
username: 'test_user',
|
|
247
|
+
email: 'test@example.com'
|
|
248
|
+
}
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
function simulateNavigation() {
|
|
253
|
+
sendLog('navigation', 'info', '用户导航到新页面', {
|
|
254
|
+
from: window.location.href,
|
|
255
|
+
to: '/new-page',
|
|
256
|
+
method: 'push'
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// 查看日志
|
|
261
|
+
async function viewLogs() {
|
|
262
|
+
try {
|
|
263
|
+
const response = await fetch('/api/debug-log/example');
|
|
264
|
+
const data = await response.json();
|
|
265
|
+
const outputDiv = document.getElementById('logOutput');
|
|
266
|
+
|
|
267
|
+
if (data.entries && data.entries.length > 0) {
|
|
268
|
+
outputDiv.innerHTML = data.entries.map(entry =>
|
|
269
|
+
`<div style="margin: 5px 0; padding: 5px; background: #fff; border-left: 3px solid ${getLevelColor(entry.level)};">
|
|
270
|
+
<strong>[${entry.level}]</strong> ${entry.message} <br>
|
|
271
|
+
<small>${new Date(entry.timestamp).toLocaleString()}</small>
|
|
272
|
+
</div>`
|
|
273
|
+
).join('');
|
|
274
|
+
} else {
|
|
275
|
+
outputDiv.innerHTML = '暂无日志数据';
|
|
276
|
+
}
|
|
277
|
+
} catch (error) {
|
|
278
|
+
document.getElementById('logOutput').innerHTML = '获取日志失败: ' + error.message;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
async function viewRecentLogs() {
|
|
283
|
+
try {
|
|
284
|
+
const response = await fetch('/api/debug-log/example?limit=5');
|
|
285
|
+
const data = await response.json();
|
|
286
|
+
const outputDiv = document.getElementById('logOutput');
|
|
287
|
+
|
|
288
|
+
if (data.entries && data.entries.length > 0) {
|
|
289
|
+
outputDiv.innerHTML = `<h3>最近 ${data.entries.length} 条日志:</h3>` +
|
|
290
|
+
data.entries.map(entry =>
|
|
291
|
+
`<div style="margin: 5px 0; padding: 5px; background: #fff; border-left: 3px solid ${getLevelColor(entry.level)};">
|
|
292
|
+
<strong>[${entry.level}]</strong> ${entry.message} <br>
|
|
293
|
+
<small>${new Date(entry.timestamp).toLocaleString()}</small>
|
|
294
|
+
</div>`
|
|
295
|
+
).join('');
|
|
296
|
+
} else {
|
|
297
|
+
outputDiv.innerHTML = '暂无日志数据';
|
|
298
|
+
}
|
|
299
|
+
} catch (error) {
|
|
300
|
+
document.getElementById('logOutput').innerHTML = '获取日志失败: ' + error.message;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
function getLevelColor(level) {
|
|
305
|
+
const colors = {
|
|
306
|
+
'error': '#dc3545',
|
|
307
|
+
'warn': '#ffc107',
|
|
308
|
+
'info': '#007bff',
|
|
309
|
+
'success': '#28a745'
|
|
310
|
+
};
|
|
311
|
+
return colors[level] || '#6c757d';
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// 性能监控
|
|
315
|
+
function measurePerformance() {
|
|
316
|
+
const perfData = performance.getEntriesByType('navigation')[0];
|
|
317
|
+
sendLog('performance', 'info', '页面性能数据', {
|
|
318
|
+
domContentLoaded: perfData.domContentLoadedEventEnd - perfData.domContentLoadedEventStart,
|
|
319
|
+
loadComplete: perfData.loadEventEnd - perfData.loadEventStart,
|
|
320
|
+
totalLoadTime: perfData.loadEventEnd - perfData.fetchStart,
|
|
321
|
+
domInteractive: perfData.domInteractive - perfData.fetchStart
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
async function measureApiPerformance() {
|
|
326
|
+
const startTime = performance.now();
|
|
327
|
+
try {
|
|
328
|
+
await fetch('/api/debug-log/list');
|
|
329
|
+
const duration = performance.now() - startTime;
|
|
330
|
+
|
|
331
|
+
sendLog('performance', 'info', 'API 性能测试', {
|
|
332
|
+
endpoint: '/api/debug-log/list',
|
|
333
|
+
responseTime: duration.toFixed(2) + 'ms',
|
|
334
|
+
status: 'success'
|
|
335
|
+
});
|
|
336
|
+
} catch (error) {
|
|
337
|
+
const duration = performance.now() - startTime;
|
|
338
|
+
sendLog('performance', 'error', 'API 性能测试失败', {
|
|
339
|
+
endpoint: '/api/debug-log/list',
|
|
340
|
+
responseTime: duration.toFixed(2) + 'ms',
|
|
341
|
+
error: error.message
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// 页面加载时自动发送日志
|
|
347
|
+
window.addEventListener('load', () => {
|
|
348
|
+
sendLog('page', 'info', '页面加载完成', {
|
|
349
|
+
userAgent: navigator.userAgent,
|
|
350
|
+
screen: `${window.screen.width}x${window.screen.height}`,
|
|
351
|
+
language: navigator.language
|
|
352
|
+
});
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
// 页面卸载前发送日志
|
|
356
|
+
window.addEventListener('beforeunload', () => {
|
|
357
|
+
navigator.sendBeacon(DEBUG_ENDPOINT, JSON.stringify({
|
|
358
|
+
logs: [{
|
|
359
|
+
sessionId: sessionId,
|
|
360
|
+
timestamp: Date.now(),
|
|
361
|
+
type: 'page',
|
|
362
|
+
level: 'info',
|
|
363
|
+
message: '页面即将卸载',
|
|
364
|
+
url: window.location.href
|
|
365
|
+
}],
|
|
366
|
+
source: 'example'
|
|
367
|
+
}));
|
|
368
|
+
});
|
|
369
|
+
</script>
|
|
370
|
+
</body>
|
|
371
|
+
</html>
|