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.
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -3,4 +3,4 @@ __pycache__/
|
|||||||
monitoring_data.json
|
monitoring_data.json
|
||||||
log_position.txt
|
log_position.txt
|
||||||
auth_log_position.txt
|
auth_log_position.txt
|
||||||
monitor_agent.log
|
monitoring_agent.log
|
||||||
|
|||||||
@@ -68,8 +68,8 @@
|
|||||||
|
|
||||||
## TODO
|
## TODO
|
||||||
|
|
||||||
- [ ] Change baseline calculations to only use integers instead of floats.
|
- [x] Change baseline calculations to only use integers instead of floats.
|
||||||
- [ ] Add a log file that only keeps records for the past 24 hours.
|
- [x] Add a log file that only keeps records for the past 24 hours.
|
||||||
- [ ] Log all LLM responses to the console.
|
- [ ] Log all LLM responses to the console.
|
||||||
- [ ] Reduce alerts to only happen between 9am and 12am.
|
- [ ] Reduce alerts to only happen between 9am and 12am.
|
||||||
- [ ] Get hostnames of devices in Nmap scan.
|
- [ ] Get hostnames of devices in Nmap scan.
|
||||||
|
|||||||
@@ -12,12 +12,26 @@ import os
|
|||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
import pingparsing
|
import pingparsing
|
||||||
import nmap
|
import nmap
|
||||||
|
import logging
|
||||||
|
from logging.handlers import TimedRotatingFileHandler
|
||||||
|
|
||||||
# Load configuration
|
# Load configuration
|
||||||
import config
|
import config
|
||||||
|
|
||||||
from syslog_rfc5424_parser import parser
|
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'
|
LOG_POSITION_FILE = 'log_position.txt'
|
||||||
AUTH_LOG_POSITION_FILE = 'auth_log_position.txt'
|
AUTH_LOG_POSITION_FILE = 'auth_log_position.txt'
|
||||||
|
|
||||||
@@ -49,10 +63,10 @@ def get_system_logs():
|
|||||||
|
|
||||||
return {"syslog": parsed_logs}
|
return {"syslog": parsed_logs}
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
print("Error: /var/log/syslog not found.")
|
logger.error("/var/log/syslog not found.")
|
||||||
return {"syslog": []}
|
return {"syslog": []}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Error reading syslog: {e}")
|
logger.error(f"Error reading syslog: {e}")
|
||||||
return {"syslog": []}
|
return {"syslog": []}
|
||||||
|
|
||||||
|
|
||||||
@@ -66,7 +80,7 @@ def get_network_metrics():
|
|||||||
result = transmitter.ping()
|
result = transmitter.ping()
|
||||||
return ping_parser.parse(result).as_dict()
|
return ping_parser.parse(result).as_dict()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Error getting network metrics: {e}")
|
logger.error(f"Error getting network metrics: {e}")
|
||||||
return {"error": "ping command failed"}
|
return {"error": "ping command failed"}
|
||||||
|
|
||||||
def get_sensor_data():
|
def get_sensor_data():
|
||||||
@@ -74,7 +88,7 @@ def get_sensor_data():
|
|||||||
try:
|
try:
|
||||||
return subprocess.check_output(["sensors"], text=True)
|
return subprocess.check_output(["sensors"], text=True)
|
||||||
except (subprocess.CalledProcessError, FileNotFoundError):
|
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
|
return None
|
||||||
|
|
||||||
def get_cpu_temperature(sensors_output):
|
def get_cpu_temperature(sensors_output):
|
||||||
@@ -105,7 +119,6 @@ def get_gpu_temperature(sensors_output):
|
|||||||
return {"gpu_temperature": "N/A"}
|
return {"gpu_temperature": "N/A"}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def get_login_attempts():
|
def get_login_attempts():
|
||||||
"""Gets system login attempts from /var/log/auth.log since the last check."""
|
"""Gets system login attempts from /var/log/auth.log since the last check."""
|
||||||
try:
|
try:
|
||||||
@@ -129,10 +142,10 @@ def get_login_attempts():
|
|||||||
|
|
||||||
return {"failed_login_attempts": failed_logins}
|
return {"failed_login_attempts": failed_logins}
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
print("Error: /var/log/auth.log not found.")
|
logger.error("/var/log/auth.log not found.")
|
||||||
return {"failed_login_attempts": []}
|
return {"failed_login_attempts": []}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Error reading login attempts: {e}")
|
logger.error(f"Error reading login attempts: {e}")
|
||||||
return {"failed_logins": []}
|
return {"failed_logins": []}
|
||||||
|
|
||||||
def get_nmap_scan_results():
|
def get_nmap_scan_results():
|
||||||
@@ -141,7 +154,7 @@ def get_nmap_scan_results():
|
|||||||
nm = nmap.PortScanner()
|
nm = nmap.PortScanner()
|
||||||
scan_options = config.NMAP_SCAN_OPTIONS
|
scan_options = config.NMAP_SCAN_OPTIONS
|
||||||
if os.geteuid() != 0 and "-sS" in 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_options = scan_options.replace("-sS", "-sT")
|
||||||
|
|
||||||
scan_results = nm.scan(hosts=config.NMAP_TARGETS, arguments=scan_options)
|
scan_results = nm.scan(hosts=config.NMAP_TARGETS, arguments=scan_options)
|
||||||
@@ -168,7 +181,7 @@ def get_nmap_scan_results():
|
|||||||
|
|
||||||
return processed_results
|
return processed_results
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Error performing Nmap scan: {e}")
|
logger.error(f"Error performing Nmap scan: {e}")
|
||||||
return {"error": "Nmap scan failed"}
|
return {"error": "Nmap scan failed"}
|
||||||
|
|
||||||
# --- LLM Interaction Function ---
|
# --- LLM Interaction Function ---
|
||||||
@@ -247,15 +260,15 @@ def analyze_data_with_llm(data, baselines):
|
|||||||
return json.loads(json_string)
|
return json.loads(json_string)
|
||||||
else:
|
else:
|
||||||
# Handle cases where the response is not valid JSON
|
# 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}
|
return {"severity": "low", "reason": sanitized_response}
|
||||||
except json.JSONDecodeError as e:
|
except json.JSONDecodeError as e:
|
||||||
print(f"Error decoding LLM response: {e}")
|
logger.error(f"Error decoding LLM response: {e}")
|
||||||
# Fallback for invalid JSON
|
# Fallback for invalid JSON
|
||||||
return {"severity": "low", "reason": sanitized_response}
|
return {"severity": "low", "reason": sanitized_response}
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Error interacting with LLM: {e}")
|
logger.error(f"Error interacting with LLM: {e}")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
@@ -267,11 +280,11 @@ def send_discord_alert(message):
|
|||||||
try:
|
try:
|
||||||
response = webhook.execute()
|
response = webhook.execute()
|
||||||
if response.status_code == 200:
|
if response.status_code == 200:
|
||||||
print("Discord alert sent successfully.")
|
logger.info("Discord alert sent successfully.")
|
||||||
else:
|
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:
|
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):
|
def send_google_home_alert(message):
|
||||||
"""Sends an alert to a Google Home speaker via Home Assistant."""
|
"""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}")
|
response = ollama.generate(model="llama3.1:8b", prompt=f"Summarize the following message in a single sentence: {message}")
|
||||||
simplified_message = response['response'].strip()
|
simplified_message = response['response'].strip()
|
||||||
except Exception as e:
|
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
|
simplified_message = message.split('.')[0] # Take the first sentence as a fallback
|
||||||
|
|
||||||
url = f"{config.HOME_ASSISTANT_URL}/api/services/tts/speak"
|
url = f"{config.HOME_ASSISTANT_URL}/api/services/tts/speak"
|
||||||
@@ -296,11 +309,11 @@ def send_google_home_alert(message):
|
|||||||
try:
|
try:
|
||||||
response = requests.post(url, headers=headers, json=data)
|
response = requests.post(url, headers=headers, json=data)
|
||||||
if response.status_code == 200:
|
if response.status_code == 200:
|
||||||
print("Google Home alert sent successfully.")
|
logger.info("Google Home alert sent successfully.")
|
||||||
else:
|
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:
|
except Exception as e:
|
||||||
print(f"Error sending Google Home alert: {e}")
|
logger.error(f"Error sending Google Home alert: {e}")
|
||||||
|
|
||||||
# --- Main Script Logic ---
|
# --- Main Script Logic ---
|
||||||
|
|
||||||
@@ -308,7 +321,7 @@ daily_events = []
|
|||||||
|
|
||||||
def run_monitoring_cycle(nmap_scan_counter):
|
def run_monitoring_cycle(nmap_scan_counter):
|
||||||
"""Runs a single monitoring cycle."""
|
"""Runs a single monitoring cycle."""
|
||||||
print("Running monitoring cycle...")
|
logger.info("Running monitoring cycle...")
|
||||||
system_logs = get_system_logs()
|
system_logs = get_system_logs()
|
||||||
network_metrics = get_network_metrics()
|
network_metrics = get_network_metrics()
|
||||||
sensors_output = get_sensor_data()
|
sensors_output = get_sensor_data()
|
||||||
@@ -349,7 +362,7 @@ def run_monitoring_cycle(nmap_scan_counter):
|
|||||||
def main():
|
def main():
|
||||||
"""Main function to run the monitoring agent."""
|
"""Main function to run the monitoring agent."""
|
||||||
if config.TEST_MODE:
|
if config.TEST_MODE:
|
||||||
print("Running in test mode...")
|
logger.info("Running in test mode...")
|
||||||
run_monitoring_cycle(0)
|
run_monitoring_cycle(0)
|
||||||
else:
|
else:
|
||||||
nmap_scan_counter = 0
|
nmap_scan_counter = 0
|
||||||
@@ -366,8 +379,4 @@ def main():
|
|||||||
time.sleep(300) # Run every 5 minutes
|
time.sleep(300) # Run every 5 minutes
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user