tracetui 2.0.0__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.
- tracetui-2.0.0/LICENSE +21 -0
- tracetui-2.0.0/PKG-INFO +162 -0
- tracetui-2.0.0/README.md +148 -0
- tracetui-2.0.0/pyproject.toml +26 -0
- tracetui-2.0.0/setup.cfg +4 -0
- tracetui-2.0.0/tests/test_anomaly_detection.py +127 -0
- tracetui-2.0.0/tests/test_detail_panel.py +98 -0
- tracetui-2.0.0/tests/test_filter_integration.py +112 -0
- tracetui-2.0.0/tests/test_filter_modal.py +17 -0
- tracetui-2.0.0/tests/test_live_monitor.py +92 -0
- tracetui-2.0.0/tests/test_models.py +90 -0
- tracetui-2.0.0/tests/test_parsers.py +251 -0
- tracetui-2.0.0/tests/test_widgets.py +404 -0
- tracetui-2.0.0/tracetui/__init__.py +3 -0
- tracetui-2.0.0/tracetui/__main__.py +6 -0
- tracetui-2.0.0/tracetui/app.py +403 -0
- tracetui-2.0.0/tracetui/cli.py +56 -0
- tracetui-2.0.0/tracetui/models.py +103 -0
- tracetui-2.0.0/tracetui/parsers/__init__.py +1 -0
- tracetui-2.0.0/tracetui/parsers/log_parser.py +467 -0
- tracetui-2.0.0/tracetui/widgets/__init__.py +1 -0
- tracetui-2.0.0/tracetui/widgets/detail_panel.py +106 -0
- tracetui-2.0.0/tracetui/widgets/filter_input.py +96 -0
- tracetui-2.0.0/tracetui/widgets/live_monitor.py +230 -0
- tracetui-2.0.0/tracetui/widgets/log_list.py +601 -0
- tracetui-2.0.0/tracetui/widgets/source_panel.py +137 -0
- tracetui-2.0.0/tracetui.egg-info/PKG-INFO +162 -0
- tracetui-2.0.0/tracetui.egg-info/SOURCES.txt +30 -0
- tracetui-2.0.0/tracetui.egg-info/dependency_links.txt +1 -0
- tracetui-2.0.0/tracetui.egg-info/entry_points.txt +2 -0
- tracetui-2.0.0/tracetui.egg-info/requires.txt +5 -0
- tracetui-2.0.0/tracetui.egg-info/top_level.txt +1 -0
tracetui-2.0.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 TraceTUI Contributors
|
|
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.
|
tracetui-2.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: tracetui
|
|
3
|
+
Version: 2.0.0
|
|
4
|
+
Summary: Interactive TUI debugger for logs with real-time parsing and filtering
|
|
5
|
+
License: MIT
|
|
6
|
+
Requires-Python: >=3.10
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Requires-Dist: textual>=1.0.0
|
|
10
|
+
Requires-Dist: rich>=13.0.0
|
|
11
|
+
Provides-Extra: dev
|
|
12
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
13
|
+
Dynamic: license-file
|
|
14
|
+
|
|
15
|
+
# TraceTUI
|
|
16
|
+
|
|
17
|
+
<p align="left">
|
|
18
|
+
<a href="./README.md">🇺🇸 English</a> •
|
|
19
|
+
<a href="./README.ru.md">🇷🇺 Русский</a>
|
|
20
|
+
</p>
|
|
21
|
+
|
|
22
|
+
**Interactive TUI debugger for logs** — a beautiful console interface built on [Textual](https://textual.io/) that parses logs (`loguru`, `logging`) in real-time, highlights errors, and lets you filter them with arrow keys.
|
|
23
|
+
|
|
24
|
+

|
|
25
|
+

|
|
26
|
+

|
|
27
|
+

|
|
28
|
+
|
|
29
|
+

|
|
30
|
+
|
|
31
|
+
## ✨ Features
|
|
32
|
+
|
|
33
|
+
### Three-panel interface
|
|
34
|
+
- **Left panel** — list of log sources/files with activity indicators
|
|
35
|
+
- **Center panel** — log stream with color-coded severity levels
|
|
36
|
+
- **Right panel** — selected entry details (traceback, metadata, tags)
|
|
37
|
+
|
|
38
|
+
### Log formats
|
|
39
|
+
- **loguru** — `2024-01-15 10:30:45 | ERROR | module.py:42 - message`
|
|
40
|
+
- **logging** — `2024-01-15 10:30:45,123 - module.name - ERROR - message`
|
|
41
|
+
- **Simple** — `ERROR - message`
|
|
42
|
+
- **Auto-detect** — automatic format detection
|
|
43
|
+
|
|
44
|
+
### Navigation & Control
|
|
45
|
+
|
|
46
|
+
#### Log navigation
|
|
47
|
+
- `↑` / `↓` — navigate through logs (by line)
|
|
48
|
+
- Error blocks are highlighted on hover
|
|
49
|
+
|
|
50
|
+
#### Source switching
|
|
51
|
+
- `←` / `→` — switch between sources/log files
|
|
52
|
+
- Active source is marked with `▸` in the left panel
|
|
53
|
+
|
|
54
|
+
#### Keyboard shortcuts
|
|
55
|
+
| Key | Action | Description |
|
|
56
|
+
|-----|--------|-------------|
|
|
57
|
+
| `F` | Filter | Filter mode — type text to search |
|
|
58
|
+
| `E` | Next Error | Jump to next error (skip INFO/DEBUG) |
|
|
59
|
+
| `T` | Theme | Switch theme (light/dark) |
|
|
60
|
+
| `Q` | Quit | Exit the application |
|
|
61
|
+
|
|
62
|
+
#### Additional capabilities
|
|
63
|
+
- Copy traceback to clipboard
|
|
64
|
+
- Add custom tags (labels)
|
|
65
|
+
- Filter by level and text
|
|
66
|
+
- Multiple log file support
|
|
67
|
+
- **Timeline Merge Mode** (`m`) — cross-file event timeline with color-coded source tags
|
|
68
|
+
|
|
69
|
+
## 📦 Installation
|
|
70
|
+
|
|
71
|
+
### Via pip
|
|
72
|
+
```bash
|
|
73
|
+
pip install tracetui
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### From source
|
|
77
|
+
```bash
|
|
78
|
+
git clone <repository-url>
|
|
79
|
+
cd TraceTUI
|
|
80
|
+
pip install -e .
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Dependencies
|
|
84
|
+
- Python >= 3.10
|
|
85
|
+
- textual >= 1.0.0
|
|
86
|
+
- rich >= 13.0.0
|
|
87
|
+
|
|
88
|
+
## 🚀 Usage
|
|
89
|
+
|
|
90
|
+
### Quick start
|
|
91
|
+
```bash
|
|
92
|
+
# Run with sample logs
|
|
93
|
+
tracetui sample_logs/app_standard.log
|
|
94
|
+
tracetui sample_logs/app_loguru.log
|
|
95
|
+
|
|
96
|
+
# Multiple files
|
|
97
|
+
tracetui log1.log log2.log log3.log
|
|
98
|
+
|
|
99
|
+
# With format specification
|
|
100
|
+
tracetui -f loguru my_app.log
|
|
101
|
+
tracetui -f logging server.log
|
|
102
|
+
|
|
103
|
+
# Multiple files (timeline mode — press m in UI)
|
|
104
|
+
tracetui app.log worker.log scheduler.log
|
|
105
|
+
|
|
106
|
+
# Filter by level
|
|
107
|
+
tracetui -l ERROR app.log
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Programmatic usage
|
|
111
|
+
```python
|
|
112
|
+
from tracetui.app import TraceTUIApp
|
|
113
|
+
from tracetui.parsers.log_parser import load_log_source
|
|
114
|
+
from pathlib import Path
|
|
115
|
+
|
|
116
|
+
app = TraceTUIApp()
|
|
117
|
+
|
|
118
|
+
# Load a file
|
|
119
|
+
source = load_log_source(Path("app.log"))
|
|
120
|
+
app.add_log_source(source)
|
|
121
|
+
|
|
122
|
+
# Run
|
|
123
|
+
app.run()
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## 🧪 Testing
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
# Run all tests
|
|
130
|
+
pytest
|
|
131
|
+
|
|
132
|
+
# With verbose output
|
|
133
|
+
pytest -v
|
|
134
|
+
|
|
135
|
+
# With coverage
|
|
136
|
+
pytest --cov=tracetui --cov-report=html
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## 📝 Usage examples
|
|
140
|
+
|
|
141
|
+
### Debugging an app with many errors
|
|
142
|
+
```bash
|
|
143
|
+
# Open a log with auto-detect format
|
|
144
|
+
tracetui /var/log/myapp.log
|
|
145
|
+
|
|
146
|
+
# Navigate with arrows to ERROR
|
|
147
|
+
# Press Enter to view details
|
|
148
|
+
# Check the traceback in the right panel
|
|
149
|
+
# Press 'd' to mark as "investigate"
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### Monitoring multiple files
|
|
153
|
+
```bash
|
|
154
|
+
# Open several logs simultaneously
|
|
155
|
+
tracetui app.log worker.log scheduler.log
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## 📄 License
|
|
159
|
+
|
|
160
|
+
MIT
|
|
161
|
+
|
|
162
|
+
---
|
tracetui-2.0.0/README.md
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# TraceTUI
|
|
2
|
+
|
|
3
|
+
<p align="left">
|
|
4
|
+
<a href="./README.md">🇺🇸 English</a> •
|
|
5
|
+
<a href="./README.ru.md">🇷🇺 Русский</a>
|
|
6
|
+
</p>
|
|
7
|
+
|
|
8
|
+
**Interactive TUI debugger for logs** — a beautiful console interface built on [Textual](https://textual.io/) that parses logs (`loguru`, `logging`) in real-time, highlights errors, and lets you filter them with arrow keys.
|
|
9
|
+
|
|
10
|
+

|
|
11
|
+

|
|
12
|
+

|
|
13
|
+

|
|
14
|
+
|
|
15
|
+

|
|
16
|
+
|
|
17
|
+
## ✨ Features
|
|
18
|
+
|
|
19
|
+
### Three-panel interface
|
|
20
|
+
- **Left panel** — list of log sources/files with activity indicators
|
|
21
|
+
- **Center panel** — log stream with color-coded severity levels
|
|
22
|
+
- **Right panel** — selected entry details (traceback, metadata, tags)
|
|
23
|
+
|
|
24
|
+
### Log formats
|
|
25
|
+
- **loguru** — `2024-01-15 10:30:45 | ERROR | module.py:42 - message`
|
|
26
|
+
- **logging** — `2024-01-15 10:30:45,123 - module.name - ERROR - message`
|
|
27
|
+
- **Simple** — `ERROR - message`
|
|
28
|
+
- **Auto-detect** — automatic format detection
|
|
29
|
+
|
|
30
|
+
### Navigation & Control
|
|
31
|
+
|
|
32
|
+
#### Log navigation
|
|
33
|
+
- `↑` / `↓` — navigate through logs (by line)
|
|
34
|
+
- Error blocks are highlighted on hover
|
|
35
|
+
|
|
36
|
+
#### Source switching
|
|
37
|
+
- `←` / `→` — switch between sources/log files
|
|
38
|
+
- Active source is marked with `▸` in the left panel
|
|
39
|
+
|
|
40
|
+
#### Keyboard shortcuts
|
|
41
|
+
| Key | Action | Description |
|
|
42
|
+
|-----|--------|-------------|
|
|
43
|
+
| `F` | Filter | Filter mode — type text to search |
|
|
44
|
+
| `E` | Next Error | Jump to next error (skip INFO/DEBUG) |
|
|
45
|
+
| `T` | Theme | Switch theme (light/dark) |
|
|
46
|
+
| `Q` | Quit | Exit the application |
|
|
47
|
+
|
|
48
|
+
#### Additional capabilities
|
|
49
|
+
- Copy traceback to clipboard
|
|
50
|
+
- Add custom tags (labels)
|
|
51
|
+
- Filter by level and text
|
|
52
|
+
- Multiple log file support
|
|
53
|
+
- **Timeline Merge Mode** (`m`) — cross-file event timeline with color-coded source tags
|
|
54
|
+
|
|
55
|
+
## 📦 Installation
|
|
56
|
+
|
|
57
|
+
### Via pip
|
|
58
|
+
```bash
|
|
59
|
+
pip install tracetui
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### From source
|
|
63
|
+
```bash
|
|
64
|
+
git clone <repository-url>
|
|
65
|
+
cd TraceTUI
|
|
66
|
+
pip install -e .
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Dependencies
|
|
70
|
+
- Python >= 3.10
|
|
71
|
+
- textual >= 1.0.0
|
|
72
|
+
- rich >= 13.0.0
|
|
73
|
+
|
|
74
|
+
## 🚀 Usage
|
|
75
|
+
|
|
76
|
+
### Quick start
|
|
77
|
+
```bash
|
|
78
|
+
# Run with sample logs
|
|
79
|
+
tracetui sample_logs/app_standard.log
|
|
80
|
+
tracetui sample_logs/app_loguru.log
|
|
81
|
+
|
|
82
|
+
# Multiple files
|
|
83
|
+
tracetui log1.log log2.log log3.log
|
|
84
|
+
|
|
85
|
+
# With format specification
|
|
86
|
+
tracetui -f loguru my_app.log
|
|
87
|
+
tracetui -f logging server.log
|
|
88
|
+
|
|
89
|
+
# Multiple files (timeline mode — press m in UI)
|
|
90
|
+
tracetui app.log worker.log scheduler.log
|
|
91
|
+
|
|
92
|
+
# Filter by level
|
|
93
|
+
tracetui -l ERROR app.log
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Programmatic usage
|
|
97
|
+
```python
|
|
98
|
+
from tracetui.app import TraceTUIApp
|
|
99
|
+
from tracetui.parsers.log_parser import load_log_source
|
|
100
|
+
from pathlib import Path
|
|
101
|
+
|
|
102
|
+
app = TraceTUIApp()
|
|
103
|
+
|
|
104
|
+
# Load a file
|
|
105
|
+
source = load_log_source(Path("app.log"))
|
|
106
|
+
app.add_log_source(source)
|
|
107
|
+
|
|
108
|
+
# Run
|
|
109
|
+
app.run()
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## 🧪 Testing
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
# Run all tests
|
|
116
|
+
pytest
|
|
117
|
+
|
|
118
|
+
# With verbose output
|
|
119
|
+
pytest -v
|
|
120
|
+
|
|
121
|
+
# With coverage
|
|
122
|
+
pytest --cov=tracetui --cov-report=html
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## 📝 Usage examples
|
|
126
|
+
|
|
127
|
+
### Debugging an app with many errors
|
|
128
|
+
```bash
|
|
129
|
+
# Open a log with auto-detect format
|
|
130
|
+
tracetui /var/log/myapp.log
|
|
131
|
+
|
|
132
|
+
# Navigate with arrows to ERROR
|
|
133
|
+
# Press Enter to view details
|
|
134
|
+
# Check the traceback in the right panel
|
|
135
|
+
# Press 'd' to mark as "investigate"
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Monitoring multiple files
|
|
139
|
+
```bash
|
|
140
|
+
# Open several logs simultaneously
|
|
141
|
+
tracetui app.log worker.log scheduler.log
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## 📄 License
|
|
145
|
+
|
|
146
|
+
MIT
|
|
147
|
+
|
|
148
|
+
---
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "tracetui"
|
|
7
|
+
version = "2.0.0"
|
|
8
|
+
description = "Interactive TUI debugger for logs with real-time parsing and filtering"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = {text = "MIT"}
|
|
11
|
+
requires-python = ">=3.10"
|
|
12
|
+
dependencies = [
|
|
13
|
+
"textual>=1.0.0",
|
|
14
|
+
"rich>=13.0.0",
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
[project.optional-dependencies]
|
|
18
|
+
dev = [
|
|
19
|
+
"pytest>=7.0",
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
[project.scripts]
|
|
23
|
+
tracetui = "tracetui.cli:main"
|
|
24
|
+
|
|
25
|
+
[tool.setuptools.packages.find]
|
|
26
|
+
include = ["tracetui*"]
|
tracetui-2.0.0/setup.cfg
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"""Тесты для автоматического обнаружения аномалий."""
|
|
2
|
+
|
|
3
|
+
from tracetui.models import LogLevel, LogEntry
|
|
4
|
+
from tracetui.widgets.log_list import LogList
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class TestAnomalyDetection:
|
|
8
|
+
"""Тесты автоматического выделения аномалий."""
|
|
9
|
+
|
|
10
|
+
def test_error_level_is_anomaly(self):
|
|
11
|
+
"""Уровень ERROR автоматически аномальный."""
|
|
12
|
+
log_list = LogList()
|
|
13
|
+
entry = LogEntry(level=LogLevel.ERROR, message="some info")
|
|
14
|
+
assert log_list._is_anomaly(entry) is True
|
|
15
|
+
|
|
16
|
+
def test_critical_level_is_anomaly(self):
|
|
17
|
+
"""Уровень CRITICAL автоматически аномальный."""
|
|
18
|
+
log_list = LogList()
|
|
19
|
+
entry = LogEntry(level=LogLevel.CRITICAL, message="some info")
|
|
20
|
+
assert log_list._is_anomaly(entry) is True
|
|
21
|
+
|
|
22
|
+
def test_timeout_keyword_detected(self):
|
|
23
|
+
"""Ключевое слово Timeout обнаруживается."""
|
|
24
|
+
log_list = LogList()
|
|
25
|
+
entry = LogEntry(level=LogLevel.WARNING, message="Request timeout after 30s")
|
|
26
|
+
assert log_list._is_anomaly(entry) is True
|
|
27
|
+
|
|
28
|
+
def test_connection_error_keyword_detected(self):
|
|
29
|
+
"""Ключевое слово ConnectionError обнаруживается."""
|
|
30
|
+
log_list = LogList()
|
|
31
|
+
entry = LogEntry(level=LogLevel.ERROR, message="ConnectionError: host unreachable")
|
|
32
|
+
assert log_list._is_anomaly(entry) is True
|
|
33
|
+
|
|
34
|
+
def test_exception_keyword_detected(self):
|
|
35
|
+
"""Ключевое слово Exception обнаруживается."""
|
|
36
|
+
log_list = LogList()
|
|
37
|
+
entry = LogEntry(level=LogLevel.ERROR, message="Unhandled exception in worker")
|
|
38
|
+
assert log_list._is_anomaly(entry) is True
|
|
39
|
+
|
|
40
|
+
def test_failure_keyword_detected(self):
|
|
41
|
+
"""Ключевое слово Failure обнаруживается."""
|
|
42
|
+
log_list = LogList()
|
|
43
|
+
entry = LogEntry(level=LogLevel.WARNING, message="Task failure after 3 retries")
|
|
44
|
+
assert log_list._is_anomaly(entry) is True
|
|
45
|
+
|
|
46
|
+
def test_fatal_keyword_detected(self):
|
|
47
|
+
"""Ключевое слово Fatal обнаруживается."""
|
|
48
|
+
log_list = LogList()
|
|
49
|
+
entry = LogEntry(level=LogLevel.ERROR, message="Fatal error in main loop")
|
|
50
|
+
assert log_list._is_anomaly(entry) is True
|
|
51
|
+
|
|
52
|
+
def test_status_code_500_detected(self):
|
|
53
|
+
"""HTTP 500 обнаруживается."""
|
|
54
|
+
log_list = LogList()
|
|
55
|
+
entry = LogEntry(level=LogLevel.WARNING, message="Server responded with 500")
|
|
56
|
+
assert log_list._is_anomaly(entry) is True
|
|
57
|
+
|
|
58
|
+
def test_status_code_503_detected(self):
|
|
59
|
+
"""HTTP 503 обнаруживается."""
|
|
60
|
+
log_list = LogList()
|
|
61
|
+
entry = LogEntry(level=LogLevel.WARNING, message="Service 503 unavailable")
|
|
62
|
+
assert log_list._is_anomaly(entry) is True
|
|
63
|
+
|
|
64
|
+
def test_timed_out_keyword_detected(self):
|
|
65
|
+
"""Фраза timed out обнаруживается."""
|
|
66
|
+
log_list = LogList()
|
|
67
|
+
entry = LogEntry(level=LogLevel.WARNING, message="Database connection timed out")
|
|
68
|
+
assert log_list._is_anomaly(entry) is True
|
|
69
|
+
|
|
70
|
+
def test_broken_pipe_detected(self):
|
|
71
|
+
"""Фраза broken pipe обнаруживается."""
|
|
72
|
+
log_list = LogList()
|
|
73
|
+
entry = LogEntry(level=LogLevel.ERROR, message="Write failed: broken pipe")
|
|
74
|
+
assert log_list._is_anomaly(entry) is True
|
|
75
|
+
|
|
76
|
+
def test_normal_info_is_not_anomaly(self):
|
|
77
|
+
"""Обычный INFO не является аномалией."""
|
|
78
|
+
log_list = LogList()
|
|
79
|
+
entry = LogEntry(level=LogLevel.INFO, message="Application started successfully")
|
|
80
|
+
assert log_list._is_anomaly(entry) is False
|
|
81
|
+
|
|
82
|
+
def test_normal_debug_is_not_anomaly(self):
|
|
83
|
+
"""Обычный DEBUG не является аномалией."""
|
|
84
|
+
log_list = LogList()
|
|
85
|
+
entry = LogEntry(level=LogLevel.DEBUG, message="Processing request payload")
|
|
86
|
+
assert log_list._is_anomaly(entry) is False
|
|
87
|
+
|
|
88
|
+
def test_normal_warning_is_not_anomaly(self):
|
|
89
|
+
"""Обычный WARNING без ключевых слов не аномалия."""
|
|
90
|
+
log_list = LogList()
|
|
91
|
+
entry = LogEntry(level=LogLevel.WARNING, message="Configuration file is outdated")
|
|
92
|
+
assert log_list._is_anomaly(entry) is False
|
|
93
|
+
|
|
94
|
+
def test_case_insensitive_detection(self):
|
|
95
|
+
"""Проверка без учёта регистра."""
|
|
96
|
+
log_list = LogList()
|
|
97
|
+
entry = LogEntry(level=LogLevel.WARNING, message="TIMEOUT detected in module")
|
|
98
|
+
assert log_list._is_anomaly(entry) is True
|
|
99
|
+
|
|
100
|
+
entry2 = LogEntry(level=LogLevel.WARNING, message="Timeout in module")
|
|
101
|
+
assert log_list._is_anomaly(entry2) is True
|
|
102
|
+
|
|
103
|
+
entry3 = LogEntry(level=LogLevel.WARNING, message="tImeOuT occurred")
|
|
104
|
+
assert log_list._is_anomaly(entry3) is True
|
|
105
|
+
|
|
106
|
+
def test_anomaly_count_in_header(self):
|
|
107
|
+
"""Подсчёт аномалий в заголовке."""
|
|
108
|
+
log_list = LogList()
|
|
109
|
+
entries = [
|
|
110
|
+
LogEntry(level=LogLevel.INFO, message="normal"),
|
|
111
|
+
LogEntry(level=LogLevel.ERROR, message="error"),
|
|
112
|
+
LogEntry(level=LogLevel.WARNING, message="timeout occurred"),
|
|
113
|
+
LogEntry(level=LogLevel.DEBUG, message="debug info"),
|
|
114
|
+
]
|
|
115
|
+
log_list._entries = entries
|
|
116
|
+
# DataTable использует _build_line() вместо _build_lines()
|
|
117
|
+
# Проверяем аномалии через get_filtered_entries
|
|
118
|
+
filtered = log_list._get_filtered_entries()
|
|
119
|
+
anomaly_count = sum(1 for e in filtered if log_list._is_anomaly(e))
|
|
120
|
+
assert anomaly_count == 2
|
|
121
|
+
|
|
122
|
+
def test_custom_anomaly_keywords(self):
|
|
123
|
+
"""Проверка что список ключевых слов не пустой."""
|
|
124
|
+
log_list = LogList()
|
|
125
|
+
assert len(log_list._anomaly_keywords) > 0
|
|
126
|
+
assert "timeout" in log_list._anomaly_keywords
|
|
127
|
+
assert "connectionerror" in log_list._anomaly_keywords
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"""Тесты для DetailPanel виджета."""
|
|
2
|
+
|
|
3
|
+
from tracetui.models import LogLevel, LogEntry
|
|
4
|
+
from tracetui.widgets.detail_panel import DetailPanel
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class TestDetailPanel:
|
|
8
|
+
"""Тесты панели деталей."""
|
|
9
|
+
|
|
10
|
+
def test_initial_display(self):
|
|
11
|
+
"""Начальное состояние — подсказка."""
|
|
12
|
+
panel = DetailPanel()
|
|
13
|
+
panel.on_mount()
|
|
14
|
+
assert "Select a log entry" in panel.text
|
|
15
|
+
|
|
16
|
+
def test_update_with_entry(self):
|
|
17
|
+
"""Обновление деталями записи."""
|
|
18
|
+
panel = DetailPanel()
|
|
19
|
+
entry = LogEntry(
|
|
20
|
+
level=LogLevel.ERROR,
|
|
21
|
+
message="Something broke",
|
|
22
|
+
timestamp="2024-01-01 12:00:00",
|
|
23
|
+
logger_name="app.main",
|
|
24
|
+
)
|
|
25
|
+
panel.update_detail(entry)
|
|
26
|
+
assert "ERROR" in panel.text
|
|
27
|
+
assert "Something broke" in panel.text
|
|
28
|
+
assert "2024-01-01 12:00:00" in panel.text
|
|
29
|
+
|
|
30
|
+
def test_update_with_traceback(self):
|
|
31
|
+
"""Отображение traceback."""
|
|
32
|
+
panel = DetailPanel()
|
|
33
|
+
entry = LogEntry(
|
|
34
|
+
level=LogLevel.ERROR,
|
|
35
|
+
message="Crash",
|
|
36
|
+
traceback="Traceback (most recent call last):\n File 'app.py', line 10",
|
|
37
|
+
)
|
|
38
|
+
panel.update_detail(entry)
|
|
39
|
+
assert "Traceback" in panel.text
|
|
40
|
+
assert "app.py" in panel.text
|
|
41
|
+
|
|
42
|
+
def test_toggle_traceback_visible(self):
|
|
43
|
+
"""Traceback виден."""
|
|
44
|
+
panel = DetailPanel()
|
|
45
|
+
panel._show_traceback = True
|
|
46
|
+
assert panel._show_traceback is True
|
|
47
|
+
|
|
48
|
+
def test_toggle_traceback_hidden(self):
|
|
49
|
+
"""Traceback скрыт."""
|
|
50
|
+
panel = DetailPanel()
|
|
51
|
+
panel._show_traceback = False
|
|
52
|
+
assert panel._show_traceback is False
|
|
53
|
+
|
|
54
|
+
def test_toggle_traceback_action(self):
|
|
55
|
+
"""Переключение через action."""
|
|
56
|
+
panel = DetailPanel()
|
|
57
|
+
initial = panel._show_traceback
|
|
58
|
+
panel.action_toggle_traceback()
|
|
59
|
+
assert panel._show_traceback == (not initial)
|
|
60
|
+
|
|
61
|
+
def test_update_none_entry(self):
|
|
62
|
+
"""Обновление None — возврат к подсказке."""
|
|
63
|
+
panel = DetailPanel()
|
|
64
|
+
panel.update_detail(None)
|
|
65
|
+
assert "Select a log entry" in panel.text
|
|
66
|
+
|
|
67
|
+
entry = LogEntry(level=LogLevel.ERROR, message="test")
|
|
68
|
+
panel.update_detail(entry)
|
|
69
|
+
assert "test" in panel.text
|
|
70
|
+
|
|
71
|
+
def test_current_entry_property(self):
|
|
72
|
+
"""Свойство current_entry."""
|
|
73
|
+
panel = DetailPanel()
|
|
74
|
+
assert panel.current_entry is None
|
|
75
|
+
|
|
76
|
+
entry = LogEntry(level=LogLevel.INFO, message="test")
|
|
77
|
+
panel.update_detail(entry)
|
|
78
|
+
assert panel.current_entry is entry
|
|
79
|
+
|
|
80
|
+
def test_detail_with_tags(self):
|
|
81
|
+
"""Отображение тегов."""
|
|
82
|
+
panel = DetailPanel()
|
|
83
|
+
entry = LogEntry(
|
|
84
|
+
level=LogLevel.ERROR,
|
|
85
|
+
message="error",
|
|
86
|
+
tags=["исследовать", "решено"],
|
|
87
|
+
)
|
|
88
|
+
panel.update_detail(entry)
|
|
89
|
+
assert "исследовать" in panel.text
|
|
90
|
+
assert "решено" in panel.text
|
|
91
|
+
|
|
92
|
+
def test_detail_without_optional_fields(self):
|
|
93
|
+
"""Минимальная запись без timestamp/logger."""
|
|
94
|
+
panel = DetailPanel()
|
|
95
|
+
entry = LogEntry(level=LogLevel.INFO, message="simple")
|
|
96
|
+
panel.update_detail(entry)
|
|
97
|
+
assert "INFO" in panel.text
|
|
98
|
+
assert "simple" in panel.text
|