22
33from __future__ import annotations
44
5+ import logging
56from collections import Counter
67from datetime import datetime , timezone
78from typing import Any
1112from .event_utils import retention_tier as _retention_tier
1213from .helpers import event_value
1314
15+ logger = logging .getLogger (__name__ )
16+
1417# Time-decay constants (in days)
1518RECENT_SESSION_DAYS = 7
1619STALE_SESSION_DAYS = 30
1720RECENT_BOOST = 0.2
1821STALE_PENALTY = 0.3
1922
2023
24+ def _ensure_aware (ts : datetime ) -> datetime :
25+ """Ensure a datetime is timezone-aware (assume UTC if naive)."""
26+ if ts .tzinfo is None :
27+ return ts .replace (tzinfo = timezone .utc )
28+ return ts
29+
30+
31+ def _parse_timestamp (ts : datetime | str ) -> datetime :
32+ """Parse a timestamp value into a timezone-aware datetime."""
33+ if isinstance (ts , datetime ):
34+ return _ensure_aware (ts )
35+ return _ensure_aware (datetime .fromisoformat (ts .replace ("Z" , "+00:00" )))
36+
37+
2138def compute_session_replay_value (
2239 session : dict [str , Any ],
2340 events : list [TraceEvent ],
@@ -35,19 +52,11 @@ def compute_session_replay_value(
3552 """
3653 now = datetime .now (timezone .utc )
3754 started_at = session .get ("started_at" )
38- if isinstance (started_at , str ):
39- started_at = datetime .fromisoformat (started_at .replace ("Z" , "+00:00" ))
40- elif isinstance (started_at , datetime ):
41- pass # already a datetime
42- elif started_at is None :
43- started_at = now
55+ if isinstance (started_at , (str , datetime )):
56+ started_at = _parse_timestamp (started_at )
4457 else :
4558 started_at = now
4659
47- # Ensure timezone-aware for arithmetic with UTC now
48- if started_at .tzinfo is None :
49- started_at = started_at .replace (tzinfo = timezone .utc )
50-
5160 # Calculate session age in days
5261 session_age = (now - started_at ).total_seconds () / 86400
5362
@@ -69,18 +78,10 @@ def compute_session_replay_value(
6978 failure_events = [e for e in events if e .event_type in failure_event_types ]
7079 failure_recency_boost = 0.0
7180 if failure_events :
72- # Get the most recent failure timestamp
73- def _ensure_aware (ts : datetime ) -> datetime :
74- if ts .tzinfo is None :
75- return ts .replace (tzinfo = timezone .utc )
76- return ts
77-
78- def _parse_ts (e ):
79- if isinstance (e .timestamp , datetime ):
80- return _ensure_aware (e .timestamp )
81- return datetime .fromisoformat (e .timestamp .replace ("Z" , "+00:00" ))
82-
83- most_recent_failure_ts = max ((_parse_ts (e ) for e in failure_events if e .timestamp ), default = started_at )
81+ most_recent_failure_ts = max (
82+ (_parse_timestamp (e .timestamp ) for e in failure_events if e .timestamp ),
83+ default = started_at ,
84+ )
8485 days_since_failure = (now - most_recent_failure_ts ).total_seconds () / 86400
8586 if days_since_failure <= RECENT_SESSION_DAYS :
8687 failure_recency_boost = 0.15
@@ -144,14 +145,14 @@ def compute_event_ranking(
144145 retry_score = compute_retry_churn_score (all_events )
145146 bonus += retry_score * 0.05
146147 except Exception :
147- pass # Skip if computation fails
148+ logger . debug ( "Retry churn computation failed, skipping bonus" )
148149
149150 # Latency spike bonus (cap at +0.05)
150151 try :
151152 latency_score = compute_latency_spike_score (all_events )
152153 bonus += latency_score * 0.05
153154 except Exception :
154- pass # Skip if computation fails
155+ logger . debug ( "Latency spike computation failed, skipping bonus" )
155156
156157 replay_value = min (replay_value + bonus , 1.0 )
157158
0 commit comments