feat: add timestamp tracking to flows with first_seen_at/last_seen_at/observed_at fields across all collectors
Add FIREWALL_LOG_TS_RE regex to parse timestamps from firewall log lines, implement firewall_log_seen_at to extract and convert log timestamps to UTC ISO format, add first_seen_at/last_seen_at/observed_at fields to flows in collect_packet_flows (AF_PACKET collector) with timestamp updates on flow aggregation, add timestamp fields to collect_flows (conntrack collector) and parse_firewall_log_line (
This commit is contained in:
@@ -656,6 +656,20 @@ def traffic_flow_key(node_id: str, raw_flow: dict[str, object]) -> tuple[object,
|
||||
)
|
||||
|
||||
|
||||
def flow_observed_at(raw_flow: dict[str, object], payload_collected_at: datetime | None) -> datetime:
|
||||
for key in ("last_seen_at", "observed_at", "first_seen_at"):
|
||||
value = raw_flow.get(key)
|
||||
if not value:
|
||||
continue
|
||||
try:
|
||||
parsed = datetime.fromisoformat(str(value).replace("Z", "+00:00"))
|
||||
except ValueError:
|
||||
continue
|
||||
return parsed.replace(tzinfo=None) if parsed.tzinfo else parsed
|
||||
fallback = payload_collected_at or datetime.utcnow()
|
||||
return fallback.replace(tzinfo=None) if fallback.tzinfo else fallback
|
||||
|
||||
|
||||
def compact_agent_payload(payload: AgentHeartbeat) -> dict:
|
||||
value = payload.model_dump(mode="json")
|
||||
flows = value.get("flows")
|
||||
@@ -1581,8 +1595,7 @@ def agent_heartbeat(payload: AgentHeartbeat, authorization: str | None = Header(
|
||||
destination_ip = str(raw_flow.get("destination_ip") or "")
|
||||
if not source_ip or not destination_ip:
|
||||
continue
|
||||
observed_at = payload.collected_at or datetime.utcnow()
|
||||
observed_at = observed_at.replace(tzinfo=None) if observed_at.tzinfo else observed_at
|
||||
observed_at = flow_observed_at(raw_flow, payload.collected_at)
|
||||
key = traffic_flow_key(node.id, raw_flow)
|
||||
existing = existing_flows.get(key)
|
||||
if existing:
|
||||
|
||||
Reference in New Issue
Block a user