MorphGuard / src /utils /telemetry.py
juanquy's picture
Disable PostgreSQL database connections on Hugging Face Spaces
56af25f
Raw
History Blame Contribute Delete
7.76 kB
import datetime
try:
import psycopg2
HAS_POSTGRES = True
except ImportError:
psycopg2 = None
HAS_POSTGRES = False
import hashlib
import uuid
import os
import json
import psutil
import time
import threading
# Try to get database settings from config
try:
import config
DB_PARAMS = {
"dbname": getattr(config, 'DB_NAME', 'morphguard'),
"user": getattr(config, 'DB_USER', 'morphguard'),
"password": getattr(config, 'DB_PASSWORD', None) or 'morphguard',
"host": getattr(config, 'DB_HOST', 'localhost'),
"port": getattr(config, 'DB_PORT', 5432)
}
except (ImportError, AttributeError):
# Default settings from environment or fallback
DB_PARAMS = {
"dbname": os.environ.get('MORPHGUARD_DB_NAME', 'morphguard'),
"user": os.environ.get('MORPHGUARD_DB_USER', 'morphguard'),
"password": os.environ.get('MORPHGUARD_DB_PASS', 'morphguard'),
"host": os.environ.get('MORPHGUARD_DB_HOST', 'localhost'),
"port": int(os.environ.get('MORPHGUARD_DB_PORT', 5432))
}
def get_db_connection():
if os.environ.get('HF_SPACE', '0') == '1':
raise RuntimeError("PostgreSQL database is disabled on HuggingFace Spaces.")
if not HAS_POSTGRES:
raise RuntimeError("PostgreSQL / psycopg2 is not installed or available.")
return psycopg2.connect(**DB_PARAMS)
def log_detection_metrics(model_name, confidence_score, is_morphed, processing_time_ms,
image_path=None, image_hash=None, face_count=1, request_id=None,
forensic_ticks=0, reasoning_path=None, detection_tier='Tier1_Fast'):
"""Log detection metrics to TimescaleDB"""
if not request_id:
request_id = str(uuid.uuid4())
# Generate image hash if needed
if image_path and not image_hash:
try:
# Try importing project utility
from utils.image_hasher import get_image_hash
image_hash = get_image_hash(image_path, algorithm='phash')
except (ImportError, Exception):
# Fallback
try:
if os.path.exists(image_path):
with open(image_path, "rb") as f:
image_hash = hashlib.sha256(f.read()).hexdigest()
except Exception:
pass
if not image_hash:
image_hash = hashlib.sha256(str(uuid.uuid4()).encode()).hexdigest()
try:
conn = get_db_connection()
cursor = conn.cursor()
cursor.execute(
"""
INSERT INTO detection_metrics (
time, model_name, confidence_score, is_morphed,
processing_time_ms, image_hash, face_count, request_id,
forensic_ticks, reasoning_path, detection_tier
)
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
""",
(
datetime.datetime.now(),
model_name,
confidence_score,
is_morphed,
processing_time_ms,
image_hash,
face_count,
request_id,
forensic_ticks,
reasoning_path,
detection_tier
)
)
conn.commit()
cursor.close()
conn.close()
return True
except Exception as e:
print(f"Error logging detection metrics: {e}")
return False
def log_model_interaction(source_model, dest_model, value=1, details=None):
"""Log a model interaction event"""
try:
conn = get_db_connection()
cur = conn.cursor()
cur.execute(
"INSERT INTO model_interactions (time, source_model, dest_model, value, details) VALUES (%s, %s, %s, %s, %s)",
(datetime.datetime.now(), source_model, dest_model, value, json.dumps(details) if details else None)
)
conn.commit()
cur.close()
conn.close()
except Exception as e:
print(f"Error logging model_interaction {source_model}->{dest_model}: {e}")
def log_demorph_metrics(model_name, processing_time_ms, method, success=True, original_image_path=None,
result_image_path=None, original_image_hash=None, result_image_hash=None, request_id=None):
"""Log demorph metrics to TimescaleDB"""
if not request_id:
request_id = str(uuid.uuid4())
# Hash logic skipped for brevity, implementing basic fallback if needed or reuse existing flow...
# (Simplified for this file extraction, keeping key logic)
now = datetime.datetime.now().isoformat()
if not original_image_hash:
original_image_hash = hashlib.sha256((now + "original").encode()).hexdigest()
if not result_image_hash:
result_image_hash = hashlib.sha256((now + "result").encode()).hexdigest()
try:
conn = get_db_connection()
cursor = conn.cursor()
cursor.execute(
"""
INSERT INTO demorph_metrics
(time, model_name, processing_time_ms, original_image_hash, result_image_hash, method, success, request_id)
VALUES (%s, %s, %s, %s, %s, %s, %s, %s)
""",
(
datetime.datetime.now(),
model_name,
processing_time_ms,
original_image_hash,
result_image_hash,
method,
success,
request_id
)
)
conn.commit()
cursor.close()
conn.close()
return True
except Exception as e:
print(f"Error logging demorph metrics: {e}")
return False
def collect_system_metrics():
"""Collect system metrics (CPU/RAM/GPU)"""
# (Implementation matches app.py logic)
# ... logic here ...
# Wrapper function to be called by global startup
pass
def start_metrics_collection_thread():
"""Start the background metrics collection thread"""
def metrics_loop():
while True:
try:
# Re-implement collect_system_metrics logic inline or call it
conn = get_db_connection()
cursor = conn.cursor()
cpu = psutil.cpu_percent()
mem = psutil.virtual_memory().percent
cursor.execute(
"INSERT INTO device_metrics (time, cpu_percent, memory_percent) VALUES (%s, %s, %s)",
(datetime.datetime.now(), cpu, mem)
)
# GPU attempt
try:
import pynvml
pynvml.nvmlInit()
if pynvml.nvmlDeviceGetCount() > 0:
h = pynvml.nvmlDeviceGetHandleByIndex(0)
mem_info = pynvml.nvmlDeviceGetMemoryInfo(h)
util = pynvml.nvmlDeviceGetUtilizationRates(h)
temp = pynvml.nvmlDeviceGetTemperature(h, pynvml.NVML_TEMPERATURE_GPU)
cursor.execute(
"INSERT INTO gpu_metrics (time, memory_used_mb, utilization, temperature_c) VALUES (%s, %s, %s, %s)",
(datetime.datetime.now(), mem_info.used / 1024**2, util.gpu, temp)
)
pynvml.nvmlShutdown()
except Exception:
pass
conn.commit()
cursor.close()
conn.close()
except Exception as e:
print(f"Metrics collection error: {e}")
time.sleep(30)
thread = threading.Thread(target=metrics_loop, daemon=True)
thread.start()