SecureLens / utils /audit_log.py
Your Name
Initial commit: SecureLens privacy-preserving medical AI
9e00302
Raw
History Blame Contribute Delete
4 kB
"""
utils/audit_log.py
SecureLens — Audit Trail System
Logs all encrypted inferences with timestamps.
"""
import os, json, hashlib, logging
from datetime import datetime
LOGS_DIR = os.path.join(
os.path.dirname(__file__), "..", "logs")
os.makedirs(LOGS_DIR, exist_ok=True)
LOG_FILE = os.path.join(LOGS_DIR, "audit_trail.jsonl")
APP_LOG = os.path.join(LOGS_DIR, "app.log")
# Configure Python logger
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s [%(levelname)s] %(message)s",
handlers=[
logging.FileHandler(APP_LOG),
logging.StreamHandler(),
]
)
logger = logging.getLogger("securelens")
class AuditLogger:
"""
Logs every encrypted inference request.
Stores: timestamp, image hash, prediction,
confidence, encryption params, latency.
Never stores: raw image data, patient identifiers.
"""
def __init__(self, log_file=LOG_FILE):
self.log_file = log_file
os.makedirs(os.path.dirname(log_file), exist_ok=True)
def log_inference(
self,
image_bytes: bytes,
prediction: str,
confidence: float,
latency_ms: float,
encryption_params: dict,
endpoint: str = "/api/predict",
):
"""
Logs one encrypted inference event.
Image is hashed — never stored raw.
"""
image_hash = hashlib.sha256(image_bytes).hexdigest()[:16]
entry = {
"timestamp" : datetime.utcnow().isoformat() + "Z",
"endpoint" : endpoint,
"image_hash" : image_hash,
"prediction" : prediction,
"confidence_pct" : round(confidence, 2),
"latency_ms" : round(latency_ms, 1),
"encryption_scheme": encryption_params.get(
"scheme", "CKKS"),
"security_bits" : encryption_params.get(
"security_bits", 128),
"ciphertext_kb" : encryption_params.get(
"ciphertext_size_kb", 0),
"data_exposed" : "none",
}
with open(self.log_file, "a", encoding="utf-8") as f:
f.write(json.dumps(entry) + "\n")
logger.info(
f"Inference logged | hash={image_hash} "
f"pred={prediction} conf={confidence:.1f}% "
f"latency={latency_ms:.1f}ms")
return entry
def get_recent_logs(self, n=20):
"""Returns last n log entries."""
if not os.path.exists(self.log_file):
return []
with open(self.log_file, encoding="utf-8") as f:
lines = f.readlines()
entries = []
for line in lines[-n:]:
try:
entries.append(json.loads(line.strip()))
except Exception:
continue
return list(reversed(entries))
def get_stats(self):
"""Returns summary statistics of all logged inferences."""
if not os.path.exists(self.log_file):
return {"total": 0}
entries = []
with open(self.log_file, encoding="utf-8") as f:
for line in f:
try:
entries.append(json.loads(line.strip()))
except Exception:
continue
if not entries:
return {"total": 0}
preds = [e["prediction"] for e in entries]
latencies = [e["latency_ms"] for e in entries]
return {
"total" : len(entries),
"normal_count" : preds.count("Normal"),
"pneumonia_count": preds.count("Pneumonia"),
"avg_latency_ms" : round(
sum(latencies)/len(latencies), 1),
"max_latency_ms" : round(max(latencies), 1),
"min_latency_ms" : round(min(latencies), 1),
"first_log" : entries[-1]["timestamp"],
"last_log" : entries[0]["timestamp"],
}
# Global instance
audit_logger = AuditLogger()