logscope-cli 0.4.6__tar.gz → 0.4.7__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.
- {logscope_cli-0.4.6 → logscope_cli-0.4.7}/PKG-INFO +7 -2
- {logscope_cli-0.4.6 → logscope_cli-0.4.7}/README.md +6 -2
- {logscope_cli-0.4.6 → logscope_cli-0.4.7}/logscope/parser.py +40 -6
- {logscope_cli-0.4.6 → logscope_cli-0.4.7}/pyproject.toml +1 -1
- {logscope_cli-0.4.6 → logscope_cli-0.4.7}/LICENSE +0 -0
- {logscope_cli-0.4.6 → logscope_cli-0.4.7}/logscope/__init__.py +0 -0
- {logscope_cli-0.4.6 → logscope_cli-0.4.7}/logscope/cli.py +0 -0
- {logscope_cli-0.4.6 → logscope_cli-0.4.7}/logscope/themes.py +0 -0
- {logscope_cli-0.4.6 → logscope_cli-0.4.7}/logscope/viewer.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: logscope-cli
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.7
|
|
4
4
|
Summary: LogScope — Beautiful log viewer for the terminal
|
|
5
5
|
License-File: LICENSE
|
|
6
6
|
Author: vinnytherobot
|
|
@@ -40,7 +40,7 @@ Description-Content-Type: text/markdown
|
|
|
40
40
|
|
|
41
41
|
* **Fast & Lightweight**: Tail files natively or stream huge data directly via pipes (`cat server.log | logscope`).
|
|
42
42
|
* **Colored & Structured Logs**: Automatically identifies `INFO`, `WARNING`, `ERROR`, `CRITICAL`, and `DEBUG`, applying beautiful typography.
|
|
43
|
-
* **Universal Parser**: Reads typical bracket logs (`[INFO]`) **and** parses modern NDJSON / JSON logs out of the box (e.g., Kubernetes, Docker).
|
|
43
|
+
* **Universal Parser**: Reads typical bracket logs (`[INFO]`) **and** parses modern NDJSON / JSON logs out of the box (e.g., Kubernetes, Docker, OpenTelemetry).
|
|
44
44
|
* **Auto-Highlighting**: Magically highlights `IPs`, `URLs`, `Dates/Timestamps`, `UUIDs`, and `E-Mails` with dynamic colors.
|
|
45
45
|
* **Custom Keyword Highlighting**: Highlight specific keywords in log messages with `--highlight` and customize colors with `--highlight-color`.
|
|
46
46
|
* **Live Dashboard**: Watch logs stream in real-time alongside a live statistics panel keeping track of Error vs Info counts (`--dashboard`).
|
|
@@ -109,6 +109,10 @@ logscope app.log --no-color
|
|
|
109
109
|
logscope archive/app.log.gz
|
|
110
110
|
```
|
|
111
111
|
|
|
112
|
+
JSON logs can use common fields such as `level`, `severity`, `severity_text`, `message`, `msg`,
|
|
113
|
+
`body`, or Docker's `log`. LogScope also extracts observability context from fields such as
|
|
114
|
+
`service.name`, `resource.attributes.service.name`, `trace_id`, and `span_id`.
|
|
115
|
+
|
|
112
116
|
### Piping from other commands (Stdin support)
|
|
113
117
|
LogScope acts as a brilliant text reformatter for other tools!
|
|
114
118
|
|
|
@@ -175,3 +179,4 @@ pytest tests/
|
|
|
175
179
|
MIT License.
|
|
176
180
|
|
|
177
181
|
Made by [vinnytherobot](https://github.com/vinnytherobot)
|
|
182
|
+
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
|
|
22
22
|
* **Fast & Lightweight**: Tail files natively or stream huge data directly via pipes (`cat server.log | logscope`).
|
|
23
23
|
* **Colored & Structured Logs**: Automatically identifies `INFO`, `WARNING`, `ERROR`, `CRITICAL`, and `DEBUG`, applying beautiful typography.
|
|
24
|
-
* **Universal Parser**: Reads typical bracket logs (`[INFO]`) **and** parses modern NDJSON / JSON logs out of the box (e.g., Kubernetes, Docker).
|
|
24
|
+
* **Universal Parser**: Reads typical bracket logs (`[INFO]`) **and** parses modern NDJSON / JSON logs out of the box (e.g., Kubernetes, Docker, OpenTelemetry).
|
|
25
25
|
* **Auto-Highlighting**: Magically highlights `IPs`, `URLs`, `Dates/Timestamps`, `UUIDs`, and `E-Mails` with dynamic colors.
|
|
26
26
|
* **Custom Keyword Highlighting**: Highlight specific keywords in log messages with `--highlight` and customize colors with `--highlight-color`.
|
|
27
27
|
* **Live Dashboard**: Watch logs stream in real-time alongside a live statistics panel keeping track of Error vs Info counts (`--dashboard`).
|
|
@@ -90,6 +90,10 @@ logscope app.log --no-color
|
|
|
90
90
|
logscope archive/app.log.gz
|
|
91
91
|
```
|
|
92
92
|
|
|
93
|
+
JSON logs can use common fields such as `level`, `severity`, `severity_text`, `message`, `msg`,
|
|
94
|
+
`body`, or Docker's `log`. LogScope also extracts observability context from fields such as
|
|
95
|
+
`service.name`, `resource.attributes.service.name`, `trace_id`, and `span_id`.
|
|
96
|
+
|
|
93
97
|
### Piping from other commands (Stdin support)
|
|
94
98
|
LogScope acts as a brilliant text reformatter for other tools!
|
|
95
99
|
|
|
@@ -155,4 +159,4 @@ pytest tests/
|
|
|
155
159
|
## License
|
|
156
160
|
MIT License.
|
|
157
161
|
|
|
158
|
-
Made by [vinnytherobot](https://github.com/vinnytherobot)
|
|
162
|
+
Made by [vinnytherobot](https://github.com/vinnytherobot)
|
|
@@ -51,6 +51,10 @@ _NORMALIZE_LEVEL_MAP = {
|
|
|
51
51
|
"EMERGENCY": "FATAL",
|
|
52
52
|
"ERR": "ERROR",
|
|
53
53
|
}
|
|
54
|
+
_JSON_LEVEL_KEYS = ("level", "severity", "log.level", "severity_text", "severityText")
|
|
55
|
+
_JSON_MESSAGE_KEYS = ("message", "msg", "text", "body", "log")
|
|
56
|
+
_JSON_TIMESTAMP_KEYS = ("timestamp", "time", "@timestamp")
|
|
57
|
+
_MISSING = object()
|
|
54
58
|
|
|
55
59
|
|
|
56
60
|
def _normalize_level(level: str) -> str:
|
|
@@ -58,6 +62,23 @@ def _normalize_level(level: str) -> str:
|
|
|
58
62
|
return _NORMALIZE_LEVEL_MAP.get(level.upper(), level.upper())
|
|
59
63
|
|
|
60
64
|
|
|
65
|
+
def _first_json_value(data: dict, keys: Tuple[str, ...]):
|
|
66
|
+
"""Return the first present JSON value from a list of common log field names."""
|
|
67
|
+
for key in keys:
|
|
68
|
+
if key in data:
|
|
69
|
+
return data[key]
|
|
70
|
+
return _MISSING
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def _stringify_json_message(value, raw_line: str) -> str:
|
|
74
|
+
"""Convert JSON message-like values to stable display text."""
|
|
75
|
+
if value is _MISSING:
|
|
76
|
+
return raw_line
|
|
77
|
+
if isinstance(value, (dict, list)):
|
|
78
|
+
return json.dumps(value, sort_keys=True)
|
|
79
|
+
return str(value).rstrip("\r\n")
|
|
80
|
+
|
|
81
|
+
|
|
61
82
|
def _extract_json_observability(data: dict) -> Tuple[Optional[str], Optional[str], Optional[str]]:
|
|
62
83
|
"""Pull service / trace / span from common JSON log shapes (K8s, OTel, Docker)."""
|
|
63
84
|
k8s = data.get("kubernetes")
|
|
@@ -66,10 +87,18 @@ def _extract_json_observability(data: dict) -> Tuple[Optional[str], Optional[str
|
|
|
66
87
|
if not pod_name and isinstance(k8s_d.get("pod"), dict):
|
|
67
88
|
pod_name = k8s_d["pod"].get("name")
|
|
68
89
|
|
|
90
|
+
resource = data.get("resource")
|
|
91
|
+
resource_d: dict = resource if isinstance(resource, dict) else {}
|
|
92
|
+
resource_attrs = resource_d.get("attributes")
|
|
93
|
+
resource_attrs_d: dict = resource_attrs if isinstance(resource_attrs, dict) else {}
|
|
94
|
+
|
|
69
95
|
service = (
|
|
70
96
|
data.get("service")
|
|
71
97
|
or data.get("service.name")
|
|
72
98
|
or data.get("service_name")
|
|
99
|
+
or data.get("resource.attributes.service.name")
|
|
100
|
+
or resource_attrs_d.get("service.name")
|
|
101
|
+
or resource_attrs_d.get("service_name")
|
|
73
102
|
or pod_name
|
|
74
103
|
or k8s_d.get("container_name")
|
|
75
104
|
or data.get("container")
|
|
@@ -102,15 +131,20 @@ def parse_line(line: str) -> LogEntry:
|
|
|
102
131
|
if line.startswith('{') and line.endswith('}'):
|
|
103
132
|
try:
|
|
104
133
|
data = json.loads(line)
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
134
|
+
level_value = _first_json_value(data, _JSON_LEVEL_KEYS)
|
|
135
|
+
message = _stringify_json_message(_first_json_value(data, _JSON_MESSAGE_KEYS), line)
|
|
136
|
+
if level_value is _MISSING:
|
|
137
|
+
inner_entry = parse_line(message) if message != line else None
|
|
138
|
+
level = inner_entry.level if inner_entry and inner_entry.level != "UNKNOWN" else "UNKNOWN"
|
|
139
|
+
if inner_entry and inner_entry.level != "UNKNOWN":
|
|
140
|
+
message = inner_entry.message
|
|
141
|
+
else:
|
|
142
|
+
level = _normalize_level(str(level_value))
|
|
109
143
|
|
|
110
144
|
# Find timestamp
|
|
111
|
-
timestamp_str =
|
|
145
|
+
timestamp_str = _first_json_value(data, _JSON_TIMESTAMP_KEYS)
|
|
112
146
|
timestamp = None
|
|
113
|
-
if timestamp_str:
|
|
147
|
+
if timestamp_str is not _MISSING and timestamp_str:
|
|
114
148
|
try:
|
|
115
149
|
# Basic ISO parsing
|
|
116
150
|
timestamp = datetime.fromisoformat(str(timestamp_str).replace('Z', '+00:00'))
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|