File size: 3,213 Bytes
fe204a0
 
2163b34
fe204a0
2163b34
 
 
 
 
 
 
 
 
 
fe204a0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2163b34
 
fe204a0
 
 
 
2163b34
fe204a0
2163b34
fe204a0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
"""debug‑toolkit — Parse and analyze error messages and stack traces.
==============================================================="""

import re
from pathlib import Path

try:
    from toolstore.toolset import tool
except ImportError:
    def tool(fn):
        return fn


@tool
def analyze_error(*, error_text: str) -> dict:
    """Parse an error message or stack trace into structured parts.

    Handles Python tracebacks, generic error messages, and multi-line logs.

    Args:
        error_text: The full error/stack trace text.

    Returns:
        dict with: error_type, message, file, line, traceback_frames
    """
    result = {"error_type": "", "message": "", "file": "", "line": None, "frames": []}

    # Python traceback
    tb_match = re.search(r'Traceback \(most recent call last\):', error_text)
    if tb_match:
        frames = re.findall(r'File "(.+?)", line (\d+), in (\w+)', error_text)
        for fpath, lineno, func in frames:
            result["frames"].append({"file": fpath, "line": int(lineno), "function": func})
        err_match = re.search(r'(\w+(?:Error|Exception|Warning)): (.+)', error_text)
        if err_match:
            result["error_type"] = err_match.group(1)
            result["message"] = err_match.group(2)
        if result["frames"]:
            result["file"] = result["frames"][-1]["file"]
            result["line"] = result["frames"][-1]["line"]
        return result

    # Generic error
    err_match = re.search(r'(\w+(?:Error|Exception|Warning)):?\s*(.+)', error_text)
    if err_match:
        result["error_type"] = err_match.group(1)
        result["message"] = err_match.group(2).strip()
    elif error_text.strip():
        result["message"] = error_text.strip().split("\n")[0]

    return result


@tool
def extract_log_patterns(*, log_text: str, pattern: str = "") -> dict:
    """Extract common log patterns: timestamps, IPs, error levels, URLs.

    Args:
        log_text: The log content to scan.
        pattern:  One of "timestamps", "ips", "errors", "urls", "all".
                  Defaults to "all".

    Returns:
        dict with found patterns grouped by type.
    """
    patterns = {}
    if pattern in ("all", "timestamps"):
        ts = re.findall(r'\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:?\d{2})?', log_text)
        if ts:
            patterns["timestamps"] = ts
    if pattern in ("all", "ips"):
        ips = re.findall(r'\b(?:\d{1,3}\.){3}\d{1,3}\b', log_text)
        if ips:
            # Filter valid IPs only
            valid = [ip for ip in ips if all(0 <= int(o) <= 255 for o in ip.split("."))]
            if valid:
                patterns["ips"] = valid[:50]
    if pattern in ("all", "errors"):
        errs = re.findall(r'(ERROR|WARN|WARNING|CRITICAL|FATAL|DEBUG|INFO|TRACE)', log_text)
        if errs:
            from collections import Counter
            patterns["log_levels"] = dict(Counter(errs))
    if pattern in ("all", "urls"):
        urls = re.findall(r'https?://[^\s<>"\'\)]+', log_text)
        if urls:
            patterns["urls"] = urls[:50]

    patterns["total_chars"] = len(log_text)
    patterns["total_lines"] = log_text.count("\n") + 1
    return patterns