LogLens / app.py
meet12341234's picture
Update app.py
fb00a86 verified
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()