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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: logscope-cli
3
- Version: 0.4.6
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
- # Find level key
106
- level = _normalize_level(data.get('level', data.get('severity', data.get('log.level', 'UNKNOWN'))))
107
- # Find message key
108
- message = str(data.get('message', data.get('msg', data.get('text', line))))
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 = data.get('timestamp', data.get('time', data.get('@timestamp')))
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'))
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "logscope-cli"
3
- version = "0.4.6"
3
+ version = "0.4.7"
4
4
  description = "LogScope — Beautiful log viewer for the terminal"
5
5
  authors = ["vinnytherobot"]
6
6
  readme = "README.md"
File without changes