From f65b2d468dff568ff43e7377f515afdc59ff8508 Mon Sep 17 00:00:00 2001 From: Spencer Date: Thu, 21 Aug 2025 12:15:36 -0500 Subject: [PATCH] feat: Implement daily log rotation - Add logging to monitor_agent.py to replace print statements. - Configure TimedRotatingFileHandler to keep logs for the past 24 hours. - Update .gitignore to exclude the new log file. - Update PROGRESS.md to reflect the completion of the task. --- .gitignore | 2 +- PROGRESS.md | 4 ++-- monitor_agent.py | 61 +++++++++++++++++++++++++++--------------------- 3 files changed, 38 insertions(+), 29 deletions(-) diff --git a/.gitignore b/.gitignore index 1c6c015..366a307 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,4 @@ __pycache__/ monitoring_data.json log_position.txt auth_log_position.txt -monitor_agent.log +monitoring_agent.log diff --git a/PROGRESS.md b/PROGRESS.md index 1170256..068fc05 100644 --- a/PROGRESS.md +++ b/PROGRESS.md @@ -68,8 +68,8 @@ ## TODO -- [ ] Change baseline calculations to only use integers instead of floats. -- [ ] Add a log file that only keeps records for the past 24 hours. +- [x] Change baseline calculations to only use integers instead of floats. +- [x] Add a log file that only keeps records for the past 24 hours. - [ ] Log all LLM responses to the console. - [ ] Reduce alerts to only happen between 9am and 12am. - [ ] Get hostnames of devices in Nmap scan. diff --git a/monitor_agent.py b/monitor_agent.py index a4b662b..f99a6e6 100644 --- a/monitor_agent.py +++ b/monitor_agent.py @@ -12,12 +12,26 @@ import os from datetime import datetime, timezone import pingparsing import nmap +import logging +from logging.handlers import TimedRotatingFileHandler # Load configuration import config from syslog_rfc5424_parser import parser +# --- Logging Configuration --- +LOG_FILE = "monitoring_agent.log" +logger = logging.getLogger(__name__) +logger.setLevel(logging.INFO) + +# Create a handler that rotates logs daily, keeping 1 backup +handler = TimedRotatingFileHandler(LOG_FILE, when="midnight", interval=1, backupCount=1) +handler.setFormatter(logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')) + +logger.addHandler(handler) + + LOG_POSITION_FILE = 'log_position.txt' AUTH_LOG_POSITION_FILE = 'auth_log_position.txt' @@ -49,10 +63,10 @@ def get_system_logs(): return {"syslog": parsed_logs} except FileNotFoundError: - print("Error: /var/log/syslog not found.") + logger.error("/var/log/syslog not found.") return {"syslog": []} except Exception as e: - print(f"Error reading syslog: {e}") + logger.error(f"Error reading syslog: {e}") return {"syslog": []} @@ -66,7 +80,7 @@ def get_network_metrics(): result = transmitter.ping() return ping_parser.parse(result).as_dict() except Exception as e: - print(f"Error getting network metrics: {e}") + logger.error(f"Error getting network metrics: {e}") return {"error": "ping command failed"} def get_sensor_data(): @@ -74,7 +88,7 @@ def get_sensor_data(): try: return subprocess.check_output(["sensors"], text=True) except (subprocess.CalledProcessError, FileNotFoundError): - print("Error: 'sensors' command not found. Please install lm-sensors.") + logger.error("'sensors' command not found. Please install lm-sensors.") return None def get_cpu_temperature(sensors_output): @@ -105,7 +119,6 @@ def get_gpu_temperature(sensors_output): return {"gpu_temperature": "N/A"} - def get_login_attempts(): """Gets system login attempts from /var/log/auth.log since the last check.""" try: @@ -129,10 +142,10 @@ def get_login_attempts(): return {"failed_login_attempts": failed_logins} except FileNotFoundError: - print("Error: /var/log/auth.log not found.") + logger.error("/var/log/auth.log not found.") return {"failed_login_attempts": []} except Exception as e: - print(f"Error reading login attempts: {e}") + logger.error(f"Error reading login attempts: {e}") return {"failed_logins": []} def get_nmap_scan_results(): @@ -141,7 +154,7 @@ def get_nmap_scan_results(): nm = nmap.PortScanner() scan_options = config.NMAP_SCAN_OPTIONS if os.geteuid() != 0 and "-sS" in scan_options: - print("Warning: Nmap -sS scan requires root privileges. Falling back to -sT.") + logger.warning("Nmap -sS scan requires root privileges. Falling back to -sT.") scan_options = scan_options.replace("-sS", "-sT") scan_results = nm.scan(hosts=config.NMAP_TARGETS, arguments=scan_options) @@ -168,7 +181,7 @@ def get_nmap_scan_results(): return processed_results except Exception as e: - print(f"Error performing Nmap scan: {e}") + logger.error(f"Error performing Nmap scan: {e}") return {"error": "Nmap scan failed"} # --- LLM Interaction Function --- @@ -247,15 +260,15 @@ def analyze_data_with_llm(data, baselines): return json.loads(json_string) else: # Handle cases where the response is not valid JSON - print(f"LLM returned a non-JSON response: {sanitized_response}") + logger.warning(f"LLM returned a non-JSON response: {sanitized_response}") return {"severity": "low", "reason": sanitized_response} except json.JSONDecodeError as e: - print(f"Error decoding LLM response: {e}") + logger.error(f"Error decoding LLM response: {e}") # Fallback for invalid JSON return {"severity": "low", "reason": sanitized_response} except Exception as e: - print(f"Error interacting with LLM: {e}") + logger.error(f"Error interacting with LLM: {e}") return None @@ -267,11 +280,11 @@ def send_discord_alert(message): try: response = webhook.execute() if response.status_code == 200: - print("Discord alert sent successfully.") + logger.info("Discord alert sent successfully.") else: - print(f"Error sending Discord alert: {response.status_code} - {response.content}") + logger.error(f"Error sending Discord alert: {response.status_code} - {response.content}") except Exception as e: - print(f"Error sending Discord alert: {e}") + logger.error(f"Error sending Discord alert: {e}") def send_google_home_alert(message): """Sends an alert to a Google Home speaker via Home Assistant.""" @@ -280,7 +293,7 @@ def send_google_home_alert(message): response = ollama.generate(model="llama3.1:8b", prompt=f"Summarize the following message in a single sentence: {message}") simplified_message = response['response'].strip() except Exception as e: - print(f"Error summarizing message: {e}") + logger.error(f"Error summarizing message: {e}") simplified_message = message.split('.')[0] # Take the first sentence as a fallback url = f"{config.HOME_ASSISTANT_URL}/api/services/tts/speak" @@ -296,11 +309,11 @@ def send_google_home_alert(message): try: response = requests.post(url, headers=headers, json=data) if response.status_code == 200: - print("Google Home alert sent successfully.") + logger.info("Google Home alert sent successfully.") else: - print(f"Error sending Google Home alert: {response.status_code} - {response.text}") + logger.error(f"Error sending Google Home alert: {response.status_code} - {response.text}") except Exception as e: - print(f"Error sending Google Home alert: {e}") + logger.error(f"Error sending Google Home alert: {e}") # --- Main Script Logic --- @@ -308,7 +321,7 @@ daily_events = [] def run_monitoring_cycle(nmap_scan_counter): """Runs a single monitoring cycle.""" - print("Running monitoring cycle...") + logger.info("Running monitoring cycle...") system_logs = get_system_logs() network_metrics = get_network_metrics() sensors_output = get_sensor_data() @@ -349,7 +362,7 @@ def run_monitoring_cycle(nmap_scan_counter): def main(): """Main function to run the monitoring agent.""" if config.TEST_MODE: - print("Running in test mode...") + logger.info("Running in test mode...") run_monitoring_cycle(0) else: nmap_scan_counter = 0 @@ -366,8 +379,4 @@ def main(): time.sleep(300) # Run every 5 minutes if __name__ == "__main__": - main() - - - - + main() \ No newline at end of file