Automated Service Health Monitor
This tool runs periodically to check the health of a list of websites. If a site is down, it logs the error and sends an email alert.
Modules Used:
- schedule: To run the check periodically.
- requests: To check the HTTP status.
- smtplib: To send email alerts.
- python-dotenv: To manage credentials securely.
- logging: To keep a history of checks.
Setup
Create a .env file:
EMAIL_USER=your_email@gmail.com
EMAIL_PASS=your_app_password
ALERT_RECEIVER=admin@example.com
The Code
Save this as monitor.py.
import schedule
import time
import requests
import smtplib
import logging
import os
from dotenv import load_dotenv
from email.message import EmailMessage
# Load config
load_dotenv()
# Configure Logging
logging.basicConfig(
filename='monitor.log',
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s'
)
SITES_TO_CHECK = [
"https://www.google.com",
"https://www.github.com",
"https://httpbin.org/status/500" # Intentionally broken link for testing
]
def send_alert(site, status):
"""Sends an email alert."""
msg = EmailMessage()
msg.set_content(f"ALERT: {site} is down! Status Code: {status}")
msg['Subject'] = f"Downtime Alert: {site}"
msg['From'] = os.getenv("EMAIL_USER")
msg['To'] = os.getenv("ALERT_RECEIVER")
try:
with smtplib.SMTP_SSL('smtp.gmail.com', 465) as server:
server.login(os.getenv("EMAIL_USER"), os.getenv("EMAIL_PASS"))
server.send_message(msg)
logging.info(f"Alert sent for {site}")
except Exception as e:
logging.error(f"Failed to send alert: {e}")
def check_sites():
print("Running health check...")
for site in SITES_TO_CHECK:
try:
r = requests.get(site, timeout=5)
if r.status_code != 200:
logging.warning(f"{site} returned {r.status_code}")
send_alert(site, r.status_code)
else:
logging.info(f"{site} is UP")
except Exception as e:
logging.error(f"{site} is UNREACHABLE: {e}")
send_alert(site, "Unreachable")
# Run every 10 minutes
schedule.every(10).minutes.do(check_sites)
if __name__ == "__main__":
# Run once immediately on startup
check_sites()
while True:
schedule.run_pending()
time.sleep(1)