first commit

This commit is contained in:
cpu
2026-07-11 12:14:59 +00:00
commit f492147b53
8 changed files with 272 additions and 0 deletions

166
daily_summary.py Normal file
View File

@@ -0,0 +1,166 @@
import os
import subprocess
import requests
import smtplib
from email.message import EmailMessage
import time
import schedule
# --- CONFIGURATION (Loaded from environment variables) ---
OLLAMA_API = os.getenv("OLLAMA_API_URL", "http://localhost:11434/api/generate")
MODEL = os.getenv("OLLAMA_MODEL", "llama3.2:1b")
AI_SYSTEM_PROMPT = os.getenv("AI_SYSTEM_PROMPT", "You are a Linux Sysadmin. Summarize these logs briefly.")
MY_EMAIL = os.getenv("REPORT_TO_EMAIL")
FROM_EMAIL = os.getenv("REPORT_FROM_EMAIL")
SMTP_SERVER = os.getenv("SMTP_SERVER", "127.0.0.1")
SMTP_PORT = int(os.getenv("SMTP_PORT", 25))
SMTP_USER = os.getenv("SMTP_USER", "")
SMTP_PASS = os.getenv("SMTP_PASS", "")
JOURNAL_UNIT = os.getenv("JOURNAL_UNIT", "mailserver")
MAX_ERRORS = int(os.getenv("MAX_ERRORS_TO_ANALYZE", 50))
# Parse comma-separated keywords into a list
keyword_string = os.getenv("ERROR_KEYWORDS", "error,failed,warning,fatal,timeout")
KEYWORDS = [k.strip().lower() for k in keyword_string.split(',')]
# --- EXECUTION TOGGLES ---
DEBUG = os.getenv("DEBUG", "false").lower() in ("true", "1", "yes")
RUN_NOW = os.getenv("RUN_NOW", "false").lower() in ("true", "1", "yes")
REPORT_TIME = os.getenv("REPORT_TIME", "08:00")
def debug_print(message):
"""Prints only if DEBUG=true in .env"""
if DEBUG:
print(f"[DEBUG] {message}")
def get_yesterdays_logs():
print(f"Fetching logs for '{JOURNAL_UNIT}' from yesterday...")
debug_print(f"Running command: journalctl -u {JOURNAL_UNIT} --since yesterday --until today")
result = subprocess.run(
['journalctl', '-u', JOURNAL_UNIT, '--since', 'yesterday', '--until', 'today'],
capture_output=True, text=True
)
lines = result.stdout.splitlines()
debug_print(f"Fetched {len(lines)} total lines from journalctl.")
return lines
def process_logs(logs):
total_lines = len(logs)
errors = []
debug_print(f"Filtering logs using keywords: {KEYWORDS}")
for line in logs:
line_lower = line.lower()
if any(keyword in line_lower for keyword in KEYWORDS):
errors.append(line)
debug_print(f"Found {len(errors)} matching lines before truncation.")
# Truncate to maximum allowed errors to save CPU time
errors = errors[-MAX_ERRORS:]
debug_print(f"Truncated to {len(errors)} lines for AI processing (MAX_ERRORS={MAX_ERRORS}).")
return total_lines, errors
def get_ai_summary(errors):
if not errors:
debug_print("Skipping AI API call because error list is empty.")
return "No errors or warnings were found in the logs yesterday. Everything looks healthy!"
print(f"Asking AI to summarize {len(errors)} errors... (This will take a few minutes)")
error_text = "\n".join(errors)
# Combine the system prompt from .env with the logs
prompt = f"{AI_SYSTEM_PROMPT}\n\nLOGS:\n{error_text}"
payload = {
"model": MODEL,
"prompt": prompt,
"stream": False
}
debug_print(f"Sending prompt to Ollama at {OLLAMA_API} using model '{MODEL}'")
debug_print(f"System Prompt used: {AI_SYSTEM_PROMPT}")
debug_print(f"Total payload length: {len(prompt)} characters.")
try:
response = requests.post(OLLAMA_API, json=payload)
debug_print(f"AI response status code: {response.status_code}")
return response.json().get('response', 'Error: AI returned empty response.')
except Exception as e:
debug_print(f"AI Connection Exception: {str(e)}")
return f"Failed to connect to local AI: {str(e)}"
def send_email_report(total_traffic, ai_summary):
if not MY_EMAIL or not FROM_EMAIL:
print("Skipping email: REPORT_TO_EMAIL or REPORT_FROM_EMAIL not set.")
return
print(f"Sending email report to {MY_EMAIL}...")
debug_print(f"SMTP Server: {SMTP_SERVER}:{SMTP_PORT}")
debug_print(f"SMTP Auth User: {'<configured>' if SMTP_USER else '<none>'}")
msg = EmailMessage()
msg['Subject'] = f"Daily AI Summary: {JOURNAL_UNIT}"
msg['From'] = FROM_EMAIL
msg['To'] = MY_EMAIL
body = (
f"DAILY SERVER REPORT: {JOURNAL_UNIT}\n"
f"----------------------------------------\n"
f"Total Log Lines Processed: {total_traffic}\n\n"
f"AI Analysis of Errors/Warnings:\n"
f"{ai_summary}\n"
)
msg.set_content(body)
try:
server = smtplib.SMTP(SMTP_SERVER, SMTP_PORT)
if SMTP_USER:
debug_print("Attempting STARTTLS and Login...")
server.starttls()
server.login(SMTP_USER, SMTP_PASS)
debug_print("Transmitting email payload...")
server.send_message(msg)
server.quit()
print("Email sent successfully!")
except Exception as e:
print(f"Failed to send email: {e}")
debug_print(f"Email Exception: {str(e)}")
def job():
print(f"\n--- Starting log analysis for {JOURNAL_UNIT} ---")
logs = get_yesterdays_logs()
total_traffic, errors = process_logs(logs)
summary = get_ai_summary(errors)
send_email_report(total_traffic, summary)
print("--- Analysis complete. ---")
if __name__ == "__main__":
print("Log Analyzer Container starting up...")
if DEBUG:
print("[DEBUG] Debug mode is ENABLED.")
# 1. Register the permanent daily schedule first
schedule.every().day.at(REPORT_TIME).do(job)
print(f"Job permanently scheduled to run daily at {REPORT_TIME}.")
# 2. Handle the immediate run if requested
if RUN_NOW:
print("RUN_NOW is enabled. Executing immediate startup run...")
job()
print("--- Immediate run finished. Returning to standard schedule. ---")
else:
print("RUN_NOW is disabled. Skipping immediate run.")
# 3. Enter the permanent background loop
print(f"Entering background scheduler loop. Waiting for {REPORT_TIME}...")
while True:
schedule.run_pending()
time.sleep(60)