Files
log-analyzer-backend/backend/services/analyzer.py
T
2026-05-07 10:14:57 +02:00

106 lines
4.1 KiB
Python

from typing import List, Dict, Any
from sqlalchemy import func, select
from sqlalchemy.ext.asyncio import AsyncSession
from models import LogEntry
class StatsAnalyzer:
@staticmethod
async def overview(session: AsyncSession) -> Dict[str, Any]:
total = (await session.execute(select(func.count()).select_from(LogEntry))).scalar() or 0
fw = (await session.execute(
select(func.count()).select_from(LogEntry).where(LogEntry.log_type == "firewall")
)).scalar() or 0
px = (await session.execute(
select(func.count()).select_from(LogEntry).where(LogEntry.log_type == "proxy")
)).scalar() or 0
return {
"total_entries": total,
"firewall_entries": fw,
"proxy_entries": px,
}
@staticmethod
async def top_sources(session: AsyncSession, limit: int = 20) -> List[Dict[str, Any]]:
stmt = (
select(LogEntry.source_ip, func.count().label("cnt"))
.where(LogEntry.source_ip.isnot(None))
.group_by(LogEntry.source_ip)
.order_by(func.count().desc())
.limit(limit)
)
rows = await session.execute(stmt)
return [{"source_ip": r[0], "count": r[1]} for r in rows]
@staticmethod
async def top_destinations(session: AsyncSession, limit: int = 20) -> List[Dict[str, Any]]:
stmt = (
select(LogEntry.destination_ip, func.count().label("cnt"))
.where(LogEntry.destination_ip.isnot(None))
.group_by(LogEntry.destination_ip)
.order_by(func.count().desc())
.limit(limit)
)
rows = await session.execute(stmt)
return [{"destination_ip": r[0], "count": r[1]} for r in rows]
@staticmethod
async def top_ports(session: AsyncSession, limit: int = 20) -> List[Dict[str, Any]]:
stmt = (
select(LogEntry.destination_port, func.count().label("cnt"))
.where(LogEntry.destination_port.isnot(None))
.group_by(LogEntry.destination_port)
.order_by(func.count().desc())
.limit(limit)
)
rows = await session.execute(stmt)
return [{"destination_port": r[0], "count": r[1]} for r in rows]
@staticmethod
async def top_urls(session: AsyncSession, limit: int = 20) -> List[Dict[str, Any]]:
stmt = (
select(LogEntry.url, func.count().label("cnt"))
.where(LogEntry.url.isnot(None))
.group_by(LogEntry.url)
.order_by(func.count().desc())
.limit(limit)
)
rows = await session.execute(stmt)
return [{"url": r[0], "count": r[1]} for r in rows]
@staticmethod
async def action_distribution(session: AsyncSession) -> List[Dict[str, Any]]:
stmt = (
select(LogEntry.action, func.count().label("cnt"))
.where(LogEntry.action.isnot(None))
.group_by(LogEntry.action)
.order_by(func.count().desc())
)
rows = await session.execute(stmt)
return [{"action": r[0], "count": r[1]} for r in rows]
@staticmethod
async def timeline(session: AsyncSession, granularity: str = "hour") -> List[Dict[str, Any]]:
if granularity == "hour":
fmt = "%Y-%m-%d %H:00"
else:
fmt = "%Y-%m-%d"
# SQLite strftime
stmt = (
select(func.strftime(fmt, LogEntry.timestamp).label("bucket"), func.count().label("cnt"))
.where(LogEntry.timestamp.isnot(None))
.group_by("bucket")
.order_by("bucket")
)
rows = await session.execute(stmt)
return [{"time_bucket": r[0], "count": r[1]} for r in rows]
@staticmethod
async def unique_counts(session: AsyncSession) -> Dict[str, int]:
src = (await session.execute(
select(func.count(func.distinct(LogEntry.source_ip)))
)).scalar() or 0
dst = (await session.execute(
select(func.count(func.distinct(LogEntry.destination_ip)))
)).scalar() or 0
return {"unique_sources": src, "unique_destinations": dst}