Tuathe commited on
Commit
0193791
·
verified ·
1 Parent(s): e3506b5

Upload 4 files

Browse files
Files changed (4) hide show
  1. notify_slack.py +27 -0
  2. requirements.txt +5 -3
  3. streamlit_app.py +116 -0
  4. upload_to_s3.py +27 -0
notify_slack.py ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import requests
2
+ import os
3
+ from dotenv import load_dotenv
4
+
5
+ load_dotenv()
6
+ SLACK_WEBHOOK_URL = os.getenv("SLACK_WEBHOOK_URL")
7
+
8
+ def send_slack_alert(log_message, s3_link=None):
9
+ if not SLACK_WEBHOOK_URL:
10
+ print(" Slack webhook URL not found in .env")
11
+ return
12
+
13
+ text = f"* Incident Detected!*\n```{log_message}```"
14
+ if s3_link:
15
+ text += f"\n [Runbook]({s3_link})"
16
+
17
+ payload = {
18
+ "text": text,
19
+ "mrkdwn": True
20
+ }
21
+
22
+ response = requests.post(SLACK_WEBHOOK_URL, json=payload)
23
+
24
+ if response.status_code == 200:
25
+ print(" Slack alert sent.")
26
+ else:
27
+ print(f" Slack alert failed: {response.status_code} - {response.text}")
requirements.txt CHANGED
@@ -1,3 +1,5 @@
1
- altair
2
- pandas
3
- streamlit
 
 
 
1
+ streamlit
2
+ torch
3
+ transformers
4
+ boto3
5
+ python-dotenv
streamlit_app.py ADDED
@@ -0,0 +1,116 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import os
3
+ from transformers import AutoTokenizer, AutoModelForSeq2SeqLM
4
+ import torch
5
+ from upload_to_s3 import upload_file_to_s3
6
+ from notify_slack import send_slack_alert
7
+
8
+ # Load fine-tuned model
9
+ model_path = "model/codementor-flan"
10
+ tokenizer = AutoTokenizer.from_pretrained(model_path)
11
+ model = AutoModelForSeq2SeqLM.from_pretrained(model_path)
12
+
13
+ # Fallback rule-based classifier
14
+ def fallback_label(log):
15
+ log_lower = log.lower()
16
+ if "login" in log_lower and "failed" in log_lower:
17
+ return "SECURITY"
18
+ elif "error" in log_lower or "failed" in log_lower:
19
+ return "ERROR"
20
+ elif "timeout" in log_lower or "not responding" in log_lower:
21
+ return "CRITICAL"
22
+ elif "cpu" in log_lower or "memory" in log_lower:
23
+ return "WARNING"
24
+ else:
25
+ return "INFO"
26
+
27
+ # Hybrid classifier using Flan-T5 with fallback
28
+ def classify_log(log):
29
+ prompt = f"""Classify this log message as one of: INFO, WARNING, ERROR, CRITICAL, SECURITY.
30
+
31
+ Log: {log}
32
+
33
+ Label:"""
34
+ inputs = tokenizer(prompt, return_tensors="pt", truncation=True)
35
+ with torch.no_grad():
36
+ outputs = model.generate(**inputs, max_new_tokens=3)
37
+ prediction = tokenizer.decode(outputs[0], skip_special_tokens=True).strip().upper()
38
+
39
+ valid_labels = {"INFO", "WARNING", "ERROR", "CRITICAL", "SECURITY"}
40
+ if prediction in valid_labels:
41
+ return prediction, "Model"
42
+ else:
43
+ return fallback_label(log), "Fallback"
44
+
45
+ # Runbook generator in markdown format
46
+ def generate_runbook(log_text, label):
47
+ prompt = f"""
48
+ You are an expert SRE. Create a step-by-step runbook in markdown format for the following {label} log.
49
+
50
+ Log message: "{log_text}"
51
+
52
+ Include:
53
+ 1. Summary
54
+ 2. Possible causes
55
+ 3. Troubleshooting steps
56
+ 4. Mitigation actions
57
+ 5. Responsible team or escalation
58
+
59
+ Only output valid markdown text.
60
+ """
61
+ inputs = tokenizer(prompt, return_tensors="pt", truncation=True)
62
+ with torch.no_grad():
63
+ outputs = model.generate(**inputs, max_new_tokens=300)
64
+ runbook_text = tokenizer.decode(outputs[0], skip_special_tokens=True).strip()
65
+
66
+ safe_filename = log_text.lower().replace(" ", "_").replace(":", "").replace('"', '').replace(".", "")
67
+ runbook_path = f"runbooks/runbook_{safe_filename}.md"
68
+ os.makedirs("runbooks", exist_ok=True)
69
+ with open(runbook_path, "w", encoding="utf-8") as f:
70
+ f.write(runbook_text)
71
+
72
+ return runbook_path, runbook_text
73
+
74
+ # Streamlit UI
75
+ st.set_page_config(page_title="WatchTowerAI", layout="centered")
76
+ st.title("WatchTowerAI - Log Classification and Runbook Generator")
77
+
78
+ # Input: text or file
79
+ log_input = st.text_input("Enter a log message")
80
+ uploaded_file = st.file_uploader("Or upload a .log file", type=["txt", "log"])
81
+
82
+ # Trigger button
83
+ if st.button("Classify and Generate Runbook"):
84
+ logs = []
85
+
86
+ if log_input:
87
+ logs.append(log_input.strip())
88
+
89
+ if uploaded_file:
90
+ content = uploaded_file.read().decode("utf-8")
91
+ logs.extend([line.strip() for line in content.splitlines() if line.strip()])
92
+
93
+ if not logs:
94
+ st.warning("Please enter a log message or upload a file.")
95
+ else:
96
+ for log in logs:
97
+ with st.spinner(f"Processing: {log}"):
98
+ label, source = classify_log(log)
99
+ st.markdown(f"**Classification:** `{label}` ({source})")
100
+ st.markdown(f"**Log:** {log}")
101
+
102
+ if label in {"CRITICAL", "SECURITY"}:
103
+ runbook_path, runbook_md = generate_runbook(log, label)
104
+
105
+ s3_path = runbook_path.replace("\\", "/")
106
+ success = upload_file_to_s3(runbook_path, "watchtowerai-artifacts", s3_path)
107
+
108
+ if success:
109
+ s3_url = f"https://watchtowerai-artifacts.s3.ap-south-1.amazonaws.com/{s3_path}"
110
+ send_slack_alert(log, s3_url)
111
+ st.success("Slack alert sent with S3 link.")
112
+ st.markdown(f"[View Runbook on S3]({s3_url})")
113
+
114
+ st.download_button("Download Runbook", runbook_md, file_name=os.path.basename(runbook_path))
115
+ else:
116
+ st.info("No runbook generated for this log.")
upload_to_s3.py ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import boto3
2
+ from botocore.exceptions import BotoCoreError, ClientError
3
+ import os
4
+ from dotenv import load_dotenv
5
+
6
+ # Load AWS credentials from .env
7
+ load_dotenv()
8
+
9
+ aws_access_key = os.getenv("AWS_ACCESS_KEY_ID")
10
+ aws_secret_key = os.getenv("AWS_SECRET_ACCESS_KEY")
11
+ region = os.getenv("AWS_REGION", "ap-south-1")
12
+
13
+ s3_client = boto3.client(
14
+ "s3",
15
+ aws_access_key_id=aws_access_key,
16
+ aws_secret_access_key=aws_secret_key,
17
+ region_name=region
18
+ )
19
+
20
+ def upload_file_to_s3(local_path, bucket, s3_key):
21
+ try:
22
+ s3_client.upload_file(local_path, bucket, s3_key)
23
+ print(f" Uploaded: {local_path} → s3://{bucket}/{s3_key}")
24
+ return True
25
+ except (BotoCoreError, ClientError) as e:
26
+ print(f" Upload failed: {e}")
27
+ return False