import gradio as gr import json import torch from transformers import pipeline, AutoTokenizer, AutoModelForCausalLM, TFAutoModelForSeq2SeqLM # --- Model Loading --- # Summarization model (BART) def load_summarizer(): model_name = "VidhuMathur/bart-log-summarization" model = TFAutoModelForSeq2SeqLM.from_pretrained(model_name) tokenizer = AutoTokenizer.from_pretrained(model_name) summarizer = pipeline( "summarization", model=model, tokenizer=tokenizer, device=0 if torch.cuda.is_available() else -1, ) return summarizer # Causal LM for analysis (Qwen) def load_qwen(): model_name = "Qwen/Qwen3-0.6B" tokenizer = AutoTokenizer.from_pretrained(model_name) model = AutoModelForCausalLM.from_pretrained( model_name, torch_dtype=torch.float16 if torch.cuda.is_available() else torch.float32, ) device = "cuda" if torch.cuda.is_available() else "cpu" model = model.to(device) if tokenizer.pad_token is None: tokenizer.pad_token = tokenizer.eos_token return model, tokenizer # --- Core Pipeline Functions --- def extract_json_simple(text): start = text.find('{') if start == -1: return None brace_count = 0 end = start for i, char in enumerate(text[start:], start): if char == '{': brace_count += 1 elif char == '}': brace_count -= 1 if brace_count == 0: end = i + 1 break if brace_count == 0: return text[start:end] return None def ensure_required_keys(analysis, summary): required_keys = { "root_cause": f"Issue identified from log analysis: {summary[:100]}...", "debugging_steps": [ "Check system logs for error patterns", "Verify service status and configuration", "Test connectivity and permissions" ], "debug_commands": [ "systemctl status service-name", "journalctl -u service-name -n 50", "netstat -tlnp | grep port" ], "useful_links": [ "https://docs.system-docs.com/troubleshooting", "https://stackoverflow.com/questions/tagged/debugging" ] } for key, default_value in required_keys.items(): if key not in analysis or not analysis[key]: analysis[key] = default_value elif isinstance(analysis[key], list) and len(analysis[key]) == 0: analysis[key] = default_value return analysis def create_fallback_analysis(summary): summary_lower = summary.lower() if any(word in summary_lower for word in ['database', 'connection', 'sql']): return { "root_cause": "Database connection issue detected in the logs", "debugging_steps": [ "Check if database service is running", "Verify database connection parameters", "Test network connectivity to database server", "Check database user permissions" ], "debug_commands": [ "sudo systemctl status postgresql", "netstat -an | grep 5432", "psql -U username -h host -d database", "ping database-host" ], "useful_links": [ "https://www.postgresql.org/docs/current/runtime.html", "https://dev.mysql.com/doc/refman/8.0/en/troubleshooting.html" ] } elif any(word in summary_lower for word in ['memory', 'heap', 'oom']): return { "root_cause": "Memory exhaustion or memory leak detected", "debugging_steps": [ "Monitor current memory usage", "Check for memory leaks in application", "Review JVM heap settings if Java application", "Analyze memory dump if available" ], "debug_commands": [ "free -h", "top -o %MEM", "jstat -gc PID", "ps aux --sort=-%mem | head" ], "useful_links": [ "https://docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/memleaks.html", "https://linux.die.net/man/1/free" ] } elif any(word in summary_lower for word in ['disk', 'space', 'full']): return { "root_cause": "Disk space exhaustion causing system issues", "debugging_steps": [ "Check disk usage across all filesystems", "Identify largest files and directories", "Clean up temporary files and logs", "Check for deleted files held by processes" ], "debug_commands": [ "df -h", "du -sh /* | sort -hr", "find /var/log -type f -size +100M", "lsof +L1" ], "useful_links": [ "https://linux.die.net/man/1/df", "https://www.cyberciti.biz/faq/linux-check-disk-space-command/" ] } else: return { "root_cause": f"System issue detected: {summary[:100]}...", "debugging_steps": [ "Review complete error logs", "Check system resource usage", "Verify service configurations", "Test system connectivity" ], "debug_commands": [ "systemctl --failed", "journalctl -p err -n 50", "htop", "netstat -tlnp" ], "useful_links": [ "https://linux.die.net/man/1/systemctl", "https://www.freedesktop.org/software/systemd/man/journalctl.html" ] } def log_processing_pipeline(raw_log, summarizer, model, tokenizer): results = { 'raw_log': raw_log, 'summary': None, 'analysis': None, 'success': False, 'errors': [] } # Step 1: Summarization try: summary_result = summarizer(raw_log, max_length=350, min_length=40, do_sample=False) summary_text = summary_result[0]['summary_text'] results['summary'] = summary_text except Exception as e: results['errors'].append(f"Summarization failed: {e}") return results # Step 2: Analysis success = False attempts = 0 max_attempts = 2 while not success and attempts < max_attempts: attempts += 1 prompt = f"""Analyze this log summary and respond with ONLY a JSON object:\n\nLog: {summary_text}\n\nRequired JSON format:\n{{\n \"root_cause\": \"explain the main problem\",\n \"debugging_steps\": [\"step 1\", \"step 2\", \"step 3\"],\n \"debug_commands\": [\"command1\", \"command2\", \"command3\"],\n \"useful_links\": [\"link1\", \"link2\"]\n}}\n\nJSON:""" try: inputs = tokenizer(prompt, return_tensors="pt", max_length=800, truncation=True) device = next(model.parameters()).device inputs = {k: v.to(device) for k, v in inputs.items()} with torch.no_grad(): outputs = model.generate( **inputs, max_new_tokens=300, temperature=0.2, do_sample=True, pad_token_id=tokenizer.eos_token_id, eos_token_id=tokenizer.eos_token_id, repetition_penalty=1.1 ) response = tokenizer.decode(outputs[0][inputs['input_ids'].shape[1]:], skip_special_tokens=True) json_str = extract_json_simple(response) if json_str: try: parsed = json.loads(json_str) fixed_analysis = ensure_required_keys(parsed, summary_text) results['analysis'] = fixed_analysis results['success'] = True success = True except json.JSONDecodeError: if attempts == max_attempts: results['errors'].append(f"JSON parsing failed after {attempts} attempts") else: if attempts == max_attempts: results['errors'].append("No valid JSON found in response") except Exception as e: if attempts == max_attempts: results['errors'].append(f"Generation failed: {e}") if not results['success']: results['analysis'] = create_fallback_analysis(summary_text) results['success'] = True results['errors'].append("Used fallback analysis due to model issues") return results # --- Gradio Interface --- def process_log_file(file_obj, summarizer, model, tokenizer): if file_obj is None: return ("No file uploaded", "", "", "", "") try: encodings = ['utf-8', 'latin-1', 'cp1252', 'iso-8859-1'] log_content = None for encoding in encodings: try: with open(file_obj.name, 'r', encoding=encoding) as f: log_content = f.read() break except UnicodeDecodeError: continue if log_content is None: return ("Encoding error", "", "", "", "") if not log_content.strip(): return ("Empty file", "", "", "", "") if len(log_content) > 100000: log_content = log_content[:100000] + "\n... (file truncated)" results = log_processing_pipeline(log_content, summarizer, model, tokenizer) if results['success']: analysis = results['analysis'] return ( "Analysis complete", results['summary'], analysis.get('root_cause', ''), '\n'.join(analysis.get('debugging_steps', [])), '\n'.join(analysis.get('debug_commands', [])), '\n'.join(analysis.get('useful_links', [])), json.dumps(results, indent=2) ) else: return ("Analysis failed", "", "", "", "") except Exception as e: return (f"Processing error: {str(e)}", "", "", "", "") def main(): summarizer = load_summarizer() model, tokenizer = load_qwen() with gr.Blocks(title="Minimal LogLens") as app: gr.Markdown("# Minimal LogLens Log Analyzer") file_input = gr.File(label="Upload Log File", file_types=[".txt", ".log", ".out", ".err"], type="filepath") analyze_btn = gr.Button("Analyze Log") status = gr.Textbox(label="Status", interactive=False) summary = gr.Textbox(label="Summary", lines=3, interactive=False) root_cause = gr.Textbox(label="Root Cause", lines=2, interactive=False) debug_steps = gr.Textbox(label="Debugging Steps", lines=4, interactive=False) debug_commands = gr.Textbox(label="Debug Commands", lines=4, interactive=False) useful_links = gr.Textbox(label="Useful Links", lines=2, interactive=False) json_output = gr.Code(label="Full JSON Output", language="json", interactive=False) analyze_btn.click( fn=lambda f: process_log_file(f, summarizer, model, tokenizer), inputs=file_input, outputs=[status, summary, root_cause, debug_steps, debug_commands, useful_links, json_output] ) app.launch() if __name__ == "__main__": main()