logler 1.1.3__cp311-cp311-win_amd64.whl → 1.2.0__cp311-cp311-win_amd64.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.
- logler/cli.py +18 -85
- logler/helpers.py +0 -85
- logler/investigate.py +238 -473
- logler/llm_cli.py +642 -126
- {logler-1.1.3.dist-info → logler-1.2.0.dist-info}/METADATA +48 -44
- {logler-1.1.3.dist-info → logler-1.2.0.dist-info}/RECORD +10 -10
- logler_rs/logler_rs.cp311-win_amd64.pyd +0 -0
- {logler-1.1.3.dist-info → logler-1.2.0.dist-info}/WHEEL +0 -0
- {logler-1.1.3.dist-info → logler-1.2.0.dist-info}/entry_points.txt +0 -0
- {logler-1.1.3.dist-info → logler-1.2.0.dist-info}/licenses/LICENSE +0 -0
logler/cli.py
CHANGED
|
@@ -135,9 +135,9 @@ def stats(files: tuple, output_json: bool):
|
|
|
135
135
|
|
|
136
136
|
@main.command()
|
|
137
137
|
@click.argument("files", nargs=-1, required=True, type=click.Path(exists=True))
|
|
138
|
-
@click.option("--auto-insights", is_flag=True, help="
|
|
138
|
+
@click.option("--auto-insights", is_flag=True, hidden=True, help="[DEPRECATED] Removed")
|
|
139
139
|
@click.option("--errors", is_flag=True, help="Show only errors with analysis")
|
|
140
|
-
@click.option("--patterns", is_flag=True, help="
|
|
140
|
+
@click.option("--patterns", is_flag=True, hidden=True, help="[DEPRECATED] Removed")
|
|
141
141
|
@click.option("--thread", type=str, help="Follow specific thread ID")
|
|
142
142
|
@click.option("--correlation", type=str, help="Follow specific correlation ID")
|
|
143
143
|
@click.option("--trace", type=str, help="Follow specific trace ID")
|
|
@@ -191,12 +191,10 @@ def investigate(
|
|
|
191
191
|
min_occurrences: int,
|
|
192
192
|
):
|
|
193
193
|
"""
|
|
194
|
-
Investigate log files with
|
|
194
|
+
Investigate log files with analysis tools.
|
|
195
195
|
|
|
196
196
|
Examples:
|
|
197
|
-
logler investigate app.log --auto-insights # Auto-detect issues
|
|
198
197
|
logler investigate app.log --errors # Analyze errors
|
|
199
|
-
logler investigate app.log --patterns # Find repeated patterns
|
|
200
198
|
logler investigate app.log --thread worker-1 # Follow specific thread
|
|
201
199
|
logler investigate app.log --correlation req-123 # Follow request
|
|
202
200
|
logler investigate app.log --trace trace-abc123 # Follow distributed trace
|
|
@@ -207,9 +205,7 @@ def investigate(
|
|
|
207
205
|
logler investigate app.log --output summary # Token-efficient output
|
|
208
206
|
"""
|
|
209
207
|
from .investigate import (
|
|
210
|
-
analyze_with_insights,
|
|
211
208
|
search,
|
|
212
|
-
find_patterns,
|
|
213
209
|
follow_thread,
|
|
214
210
|
follow_thread_hierarchy,
|
|
215
211
|
get_hierarchy_summary,
|
|
@@ -218,7 +214,6 @@ def investigate(
|
|
|
218
214
|
)
|
|
219
215
|
from rich.console import Console
|
|
220
216
|
from rich.table import Table
|
|
221
|
-
from rich.panel import Panel
|
|
222
217
|
|
|
223
218
|
console = Console()
|
|
224
219
|
file_list = list(files)
|
|
@@ -230,90 +225,28 @@ def investigate(
|
|
|
230
225
|
console.print("[red]❌ Provide only one of --thread, --correlation, or --trace.[/red]")
|
|
231
226
|
sys.exit(2)
|
|
232
227
|
|
|
233
|
-
# Auto-insights mode
|
|
228
|
+
# Deprecated: Auto-insights mode
|
|
234
229
|
if auto_insights:
|
|
235
|
-
console.print("[bold cyan]🎯 Running automatic insights analysis...[/bold cyan]\n")
|
|
236
|
-
result = analyze_with_insights(files=file_list, auto_investigate=True)
|
|
237
|
-
|
|
238
|
-
if output_json:
|
|
239
|
-
console.print_json(data=result)
|
|
240
|
-
return
|
|
241
|
-
|
|
242
|
-
# Display overview
|
|
243
|
-
overview = result["overview"]
|
|
244
230
|
console.print(
|
|
245
|
-
|
|
246
|
-
f"[bold]Total Logs:[/bold] {overview['total_logs']}\n"
|
|
247
|
-
f"[bold]Error Count:[/bold] {overview['error_count']}\n"
|
|
248
|
-
f"[bold]Error Rate:[/bold] {overview['error_rate']:.1%}\n"
|
|
249
|
-
f"[bold]Log Levels:[/bold] {overview['log_levels']}",
|
|
250
|
-
title="📊 Overview",
|
|
251
|
-
border_style="cyan",
|
|
252
|
-
)
|
|
231
|
+
"[yellow]WARNING: --auto-insights is deprecated and has been removed.[/yellow]"
|
|
253
232
|
)
|
|
233
|
+
console.print(
|
|
234
|
+
"[dim]Use 'logler llm search --level ERROR' for error analysis instead.[/dim]"
|
|
235
|
+
)
|
|
236
|
+
sys.exit(0)
|
|
254
237
|
|
|
255
|
-
|
|
256
|
-
if result["insights"]:
|
|
257
|
-
console.print("\n[bold cyan]💡 Automatic Insights[/bold cyan]\n")
|
|
258
|
-
for i, insight in enumerate(result["insights"], 1):
|
|
259
|
-
severity_color = {"high": "red", "medium": "yellow", "low": "green"}.get(
|
|
260
|
-
insight["severity"], "white"
|
|
261
|
-
)
|
|
262
|
-
|
|
263
|
-
severity_icon = {"high": "🔴", "medium": "🟡", "low": "🟢"}.get(
|
|
264
|
-
insight["severity"], "⚪"
|
|
265
|
-
)
|
|
266
|
-
|
|
267
|
-
console.print(
|
|
268
|
-
f"{severity_icon} [bold {severity_color}]Insight #{i}:[/bold {severity_color}] {insight['type']}"
|
|
269
|
-
)
|
|
270
|
-
console.print(
|
|
271
|
-
f" [dim]Severity:[/dim] [{severity_color}]{insight['severity'].upper()}[/{severity_color}]"
|
|
272
|
-
)
|
|
273
|
-
console.print(f" [dim]Description:[/dim] {insight['description']}")
|
|
274
|
-
console.print(f" [dim]Suggestion:[/dim] {insight['suggestion']}\n")
|
|
275
|
-
|
|
276
|
-
# Display suggestions
|
|
277
|
-
if result["suggestions"]:
|
|
278
|
-
console.print("[bold cyan]📝 Suggestions[/bold cyan]\n")
|
|
279
|
-
for i, suggestion in enumerate(result["suggestions"], 1):
|
|
280
|
-
console.print(f" {i}. {suggestion}")
|
|
281
|
-
|
|
282
|
-
# Display next steps
|
|
283
|
-
if result["next_steps"]:
|
|
284
|
-
console.print("\n[bold cyan]🚀 Next Steps[/bold cyan]\n")
|
|
285
|
-
for i, step in enumerate(result["next_steps"], 1):
|
|
286
|
-
console.print(f" {i}. {step}")
|
|
287
|
-
|
|
288
|
-
# Pattern detection mode
|
|
238
|
+
# Deprecated: Pattern detection mode
|
|
289
239
|
elif patterns:
|
|
290
240
|
console.print(
|
|
291
|
-
|
|
241
|
+
"[yellow]WARNING: --patterns is deprecated and has been removed.[/yellow]"
|
|
292
242
|
)
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
if pattern_list:
|
|
301
|
-
table = Table(title=f"Found {len(pattern_list)} Patterns")
|
|
302
|
-
table.add_column("Pattern", style="cyan", no_wrap=False)
|
|
303
|
-
table.add_column("Count", justify="right", style="green")
|
|
304
|
-
table.add_column("First Seen", style="yellow")
|
|
305
|
-
table.add_column("Last Seen", style="yellow")
|
|
306
|
-
|
|
307
|
-
for pattern in pattern_list[:20]: # Show top 20
|
|
308
|
-
pattern_text = pattern.get("pattern", "")[:80]
|
|
309
|
-
count = pattern.get("occurrences", 0)
|
|
310
|
-
first = pattern.get("first_seen", "N/A")
|
|
311
|
-
last = pattern.get("last_seen", "N/A")
|
|
312
|
-
table.add_row(pattern_text, str(count), first, last)
|
|
313
|
-
|
|
314
|
-
console.print(table)
|
|
315
|
-
else:
|
|
316
|
-
console.print("[yellow]No repeated patterns found.[/yellow]")
|
|
243
|
+
console.print(
|
|
244
|
+
"[dim]Pattern detection requires specialized tools like Drain3 or LogMine.[/dim]"
|
|
245
|
+
)
|
|
246
|
+
console.print(
|
|
247
|
+
"[dim]Use 'logler llm search' with SQL grouping for similar results.[/dim]"
|
|
248
|
+
)
|
|
249
|
+
sys.exit(0)
|
|
317
250
|
|
|
318
251
|
# Thread/correlation following mode
|
|
319
252
|
elif thread or correlation or trace:
|
logler/helpers.py
CHANGED
|
@@ -46,21 +46,6 @@ def quick_summary(files: List[str]) -> Dict[str, Any]:
|
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
|
|
49
|
-
def find_top_errors(files: List[str], limit: int = 10) -> List[Dict[str, Any]]:
|
|
50
|
-
"""
|
|
51
|
-
Find the most common error patterns.
|
|
52
|
-
|
|
53
|
-
Returns a list of error patterns sorted by frequency.
|
|
54
|
-
|
|
55
|
-
Example:
|
|
56
|
-
errors = find_top_errors(["app.log"], limit=5)
|
|
57
|
-
for err in errors:
|
|
58
|
-
print(f"{err['occurrences']}x: {err['pattern']}")
|
|
59
|
-
"""
|
|
60
|
-
patterns = investigate.find_patterns(files, min_occurrences=2)
|
|
61
|
-
return sorted(patterns["patterns"], key=lambda x: x["occurrences"], reverse=True)[:limit]
|
|
62
|
-
|
|
63
|
-
|
|
64
49
|
def search_errors(
|
|
65
50
|
files: List[str], query: Optional[str] = None, limit: int = 100
|
|
66
51
|
) -> List[Dict[str, Any]]:
|
|
@@ -111,38 +96,6 @@ def trace_request(files: List[str], correlation_id: str) -> Dict[str, Any]:
|
|
|
111
96
|
}
|
|
112
97
|
|
|
113
98
|
|
|
114
|
-
def detect_spikes(files: List[str], window_minutes: int = 5) -> List[Dict[str, Any]]:
|
|
115
|
-
"""
|
|
116
|
-
Detect error rate spikes.
|
|
117
|
-
|
|
118
|
-
Note: This requires the SQL feature to be enabled.
|
|
119
|
-
|
|
120
|
-
Returns list of time windows with abnormally high error rates.
|
|
121
|
-
|
|
122
|
-
Example:
|
|
123
|
-
spikes = detect_spikes(["app.log"], window_minutes=5)
|
|
124
|
-
for spike in spikes:
|
|
125
|
-
print(f"Spike at {spike['time']}: {spike['errors']} errors")
|
|
126
|
-
"""
|
|
127
|
-
# This would require SQL queries to implement properly
|
|
128
|
-
# For now, use pattern detection as a simpler alternative
|
|
129
|
-
patterns = investigate.find_patterns(files, min_occurrences=3)
|
|
130
|
-
|
|
131
|
-
spikes = []
|
|
132
|
-
for pattern in patterns["patterns"]:
|
|
133
|
-
if pattern["occurrences"] >= 5: # Threshold for "spike"
|
|
134
|
-
spikes.append(
|
|
135
|
-
{
|
|
136
|
-
"pattern": pattern["pattern"],
|
|
137
|
-
"occurrences": pattern["occurrences"],
|
|
138
|
-
"first_seen": pattern["first_seen"],
|
|
139
|
-
"last_seen": pattern["last_seen"],
|
|
140
|
-
}
|
|
141
|
-
)
|
|
142
|
-
|
|
143
|
-
return spikes
|
|
144
|
-
|
|
145
|
-
|
|
146
99
|
def get_error_context(file: str, line_number: int, lines: int = 10) -> Dict[str, Any]:
|
|
147
100
|
"""
|
|
148
101
|
Get context around an error line.
|
|
@@ -190,39 +143,6 @@ def analyze_thread_health(files: List[str]) -> Dict[str, Dict[str, int]]:
|
|
|
190
143
|
}
|
|
191
144
|
|
|
192
145
|
|
|
193
|
-
def find_cascading_failures(files: List[str]) -> List[Dict[str, Any]]:
|
|
194
|
-
"""
|
|
195
|
-
Find patterns that suggest cascading failures.
|
|
196
|
-
|
|
197
|
-
Looks for:
|
|
198
|
-
- Multiple errors in quick succession
|
|
199
|
-
- Errors across multiple threads/services
|
|
200
|
-
- Increasing error rates over time
|
|
201
|
-
|
|
202
|
-
Example:
|
|
203
|
-
cascades = find_cascading_failures(["app.log"])
|
|
204
|
-
for cascade in cascades:
|
|
205
|
-
print(f"Cascade: {cascade['pattern']} across {len(cascade['threads'])} threads")
|
|
206
|
-
"""
|
|
207
|
-
patterns = investigate.find_patterns(files, min_occurrences=3)
|
|
208
|
-
|
|
209
|
-
cascades = []
|
|
210
|
-
for pattern in patterns["patterns"]:
|
|
211
|
-
# Cascading failures typically affect multiple threads
|
|
212
|
-
if len(pattern["affected_threads"]) >= 3:
|
|
213
|
-
cascades.append(
|
|
214
|
-
{
|
|
215
|
-
"pattern": pattern["pattern"],
|
|
216
|
-
"occurrences": pattern["occurrences"],
|
|
217
|
-
"threads": pattern["affected_threads"],
|
|
218
|
-
"first_seen": pattern["first_seen"],
|
|
219
|
-
"last_seen": pattern["last_seen"],
|
|
220
|
-
}
|
|
221
|
-
)
|
|
222
|
-
|
|
223
|
-
return cascades
|
|
224
|
-
|
|
225
|
-
|
|
226
146
|
def get_timeline_summary(files: List[str], correlation_id: str) -> str:
|
|
227
147
|
"""
|
|
228
148
|
Get a human-readable timeline summary for a request.
|
|
@@ -275,8 +195,3 @@ def trace(files: List[str], correlation_id: str) -> Dict[str, Any]:
|
|
|
275
195
|
def summary(files: List[str]) -> Dict[str, Any]:
|
|
276
196
|
"""Shorthand for quick_summary()"""
|
|
277
197
|
return quick_summary(files)
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
def patterns(files: List[str], limit: int = 10) -> List[Dict[str, Any]]:
|
|
281
|
-
"""Shorthand for find_top_errors()"""
|
|
282
|
-
return find_top_errors(files, limit)
|