الملفات
ghaymah-exam--Momen-Lotfy--…/task5-mithal-dashboard/collector.py
2026-07-28 20:41:37 +03:00

151 أسطر
5.3 KiB
Python

"""
collector.py
يجمع مقاييس مراقبة لموقع mithal.space كل دقيقة ويخزنها في CSV و JSON:
- Latency: زمن استجابة HTTP
- Uptime: هل الموقع متاح (status code)
- SSL: حالة الشهادة وتاريخ انتهائها
- DNS: وقت تحليل DNS
- Search Response: زمن الرد على عملية بحث (query) داخل الموقع
الاستخدام:
python collector.py # يعمل باستمرار كل 60 ثانية
python collector.py --once # فحص واحد فقط (مفيد للاختبار / cron)
"""
import argparse
import csv
import json
import socket
import ssl
import time
from datetime import datetime, timezone
from pathlib import Path
from urllib.parse import urlparse
import requests
TARGET_URL = "https://mithal.space"
# عدّل هذا لمسار بحث فعلي في الموقع لو مختلف
SEARCH_URL = "https://mithal.space/search?q=test"
CSV_FILE = Path("metrics.csv")
JSON_FILE = Path("latest.json")
HISTORY_JSON = Path("history.json")
CHECK_INTERVAL = 60 # ثانية
HISTORY_MAX = 1440 # الاحتفاظ بآخر 24 ساعة (فحص كل دقيقة)
def check_dns(hostname: str) -> float:
"""يقيس وقت تحليل DNS بالمللي ثانية."""
start = time.perf_counter()
try:
socket.gethostbyname(hostname)
return round((time.perf_counter() - start) * 1000, 2)
except socket.gaierror:
return -1.0
def check_http(url: str) -> dict:
"""يقيس زمن الاستجابة وكود الحالة."""
try:
start = time.perf_counter()
resp = requests.get(url, timeout=10)
elapsed_ms = round((time.perf_counter() - start) * 1000, 2)
return {"status_code": resp.status_code, "latency_ms": elapsed_ms, "up": resp.status_code < 400}
except requests.RequestException as e:
return {"status_code": None, "latency_ms": -1, "up": False, "error": str(e)}
def check_ssl(hostname: str, port: int = 443) -> dict:
"""يفحص حالة شهادة SSL وعدد الأيام المتبقية على انتهائها."""
try:
ctx = ssl.create_default_context()
with socket.create_connection((hostname, port), timeout=10) as sock:
with ctx.wrap_socket(sock, server_hostname=hostname) as ssock:
cert = ssock.getpeercert()
expiry = datetime.strptime(cert["notAfter"], "%b %d %H:%M:%S %Y %Z")
expiry = expiry.replace(tzinfo=timezone.utc)
days_left = (expiry - datetime.now(timezone.utc)).days
return {"valid": True, "expires_on": expiry.isoformat(), "days_left": days_left}
except Exception as e:
return {"valid": False, "error": str(e)}
def check_search(url: str) -> dict:
"""يرسل طلب بحث ويقيس زمن الرد."""
try:
start = time.perf_counter()
resp = requests.get(url, timeout=10)
elapsed_ms = round((time.perf_counter() - start) * 1000, 2)
return {"status_code": resp.status_code, "latency_ms": elapsed_ms}
except requests.RequestException as e:
return {"status_code": None, "latency_ms": -1, "error": str(e)}
def run_check() -> dict:
hostname = urlparse(TARGET_URL).hostname
timestamp = datetime.now(timezone.utc).isoformat()
http_result = check_http(TARGET_URL)
dns_ms = check_dns(hostname)
ssl_result = check_ssl(hostname)
search_result = check_search(SEARCH_URL)
return {
"timestamp": timestamp,
"up": http_result.get("up", False),
"status_code": http_result.get("status_code"),
"latency_ms": http_result.get("latency_ms"),
"dns_ms": dns_ms,
"ssl_valid": ssl_result.get("valid"),
"ssl_days_left": ssl_result.get("days_left"),
"search_latency_ms": search_result.get("latency_ms"),
}
def append_csv(row: dict):
is_new = not CSV_FILE.exists()
with open(CSV_FILE, "a", newline="", encoding="utf-8") as f:
writer = csv.DictWriter(f, fieldnames=list(row.keys()))
if is_new:
writer.writeheader()
writer.writerow(row)
def update_json(row: dict):
# أحدث قراءة (يقرأها الـ dashboard مباشرة)
JSON_FILE.write_text(json.dumps(row, ensure_ascii=False, indent=2), encoding="utf-8")
# سجل تاريخي محدود (لآخر 24 ساعة) يستخدمه الـ dashboard للرسم البياني والجدول
history = []
if HISTORY_JSON.exists():
try:
history = json.loads(HISTORY_JSON.read_text(encoding="utf-8"))
except json.JSONDecodeError:
history = []
history.append(row)
history = history[-HISTORY_MAX:]
HISTORY_JSON.write_text(json.dumps(history, ensure_ascii=False, indent=2), encoding="utf-8")
def main():
parser = argparse.ArgumentParser()
parser.add_argument("--once", action="store_true", help="تشغيل فحص واحد فقط ثم الخروج")
args = parser.parse_args()
while True:
row = run_check()
append_csv(row)
update_json(row)
print(f"[{row['timestamp']}] up={row['up']} latency={row['latency_ms']}ms "
f"dns={row['dns_ms']}ms ssl_days_left={row['ssl_days_left']} "
f"search={row['search_latency_ms']}ms")
if args.once:
break
time.sleep(CHECK_INTERVAL)
if __name__ == "__main__":
main()