@@ -23,7 +23,7 @@ async def index_repo(repo_id: str):
2323from typing import Optional , Any , Dict
2424from functools import wraps
2525from contextlib import contextmanager
26- from datetime import datetime
26+ from datetime import datetime , timezone
2727from collections import deque
2828
2929# Environment
@@ -43,15 +43,15 @@ class StructuredLogger:
4343 logger.error("Failed to index", repo_id="xyz", error=str(e))
4444 """
4545
46- def __init__ (self , name : str = "codeintel" ):
46+ def __init__ (self , name : str = "codeintel" ) -> None :
4747 self .name = name
4848 self .level = getattr (logging , LOG_LEVEL .upper (), logging .INFO )
4949 self ._context : Dict [str , Any ] = {}
5050
5151 def _format_message (self , level : str , message : str , ** kwargs ) -> str :
5252 """Format log message based on environment"""
5353 data = {
54- "timestamp" : datetime .utcnow ( ).isoformat (),
54+ "timestamp" : datetime .now ( timezone . utc ).isoformat (),
5555 "level" : level ,
5656 "service" : self .name ,
5757 "message" : message ,
@@ -73,7 +73,7 @@ def _format_message(self, level: str, message: str, **kwargs) -> str:
7373 parts .append (extras )
7474 return " " .join (parts )
7575
76- def _log (self , level : str , level_num : int , message : str , ** kwargs ):
76+ def _log (self , level : str , level_num : int , message : str , ** kwargs ) -> None :
7777 """Internal log method"""
7878 if level_num < self .level :
7979 return
@@ -84,27 +84,27 @@ def _log(self, level: str, level_num: int, message: str, **kwargs):
8484 output = sys .stderr if level_num >= logging .ERROR else sys .stdout
8585 print (formatted , file = output )
8686
87- def set_context (self , ** kwargs ):
87+ def set_context (self , ** kwargs ) -> None :
8888 """Set persistent context for all subsequent logs"""
8989 self ._context .update (kwargs )
9090
91- def clear_context (self ):
91+ def clear_context (self ) -> None :
9292 """Clear all context"""
9393 self ._context = {}
9494
95- def debug (self , message : str , ** kwargs ):
95+ def debug (self , message : str , ** kwargs ) -> None :
9696 self ._log ("DEBUG" , logging .DEBUG , message , ** kwargs )
9797
98- def info (self , message : str , ** kwargs ):
98+ def info (self , message : str , ** kwargs ) -> None :
9999 self ._log ("INFO" , logging .INFO , message , ** kwargs )
100100
101- def warning (self , message : str , ** kwargs ):
101+ def warning (self , message : str , ** kwargs ) -> None :
102102 self ._log ("WARNING" , logging .WARNING , message , ** kwargs )
103103
104- def error (self , message : str , ** kwargs ):
104+ def error (self , message : str , ** kwargs ) -> None :
105105 self ._log ("ERROR" , logging .ERROR , message , ** kwargs )
106106
107- def critical (self , message : str , ** kwargs ):
107+ def critical (self , message : str , ** kwargs ) -> None :
108108 self ._log ("CRITICAL" , logging .CRITICAL , message , ** kwargs )
109109
110110
@@ -114,7 +114,7 @@ def critical(self, message: str, **kwargs):
114114
115115# SENTRY INTEGRATION HELPERS
116116
117- def set_operation_context (operation : str , ** kwargs ):
117+ def set_operation_context (operation : str , ** kwargs ) -> None :
118118 """
119119 Set Sentry context for current operation.
120120
@@ -135,7 +135,7 @@ def set_operation_context(operation: str, **kwargs):
135135 pass
136136
137137
138- def add_breadcrumb (message : str , category : str = "custom" , level : str = "info" , ** data ):
138+ def add_breadcrumb (message : str , category : str = "custom" , level : str = "info" , ** data ) -> None :
139139 """
140140 Add breadcrumb for Sentry error context.
141141
@@ -153,7 +153,7 @@ def add_breadcrumb(message: str, category: str = "custom", level: str = "info",
153153 pass
154154
155155
156- def capture_exception (error : Exception , ** context ):
156+ def capture_exception (error : Exception , ** context ) -> None :
157157 """
158158 Capture exception to Sentry with additional context.
159159
@@ -162,7 +162,7 @@ def capture_exception(error: Exception, **context):
162162 """
163163 try :
164164 import sentry_sdk
165- with sentry_sdk .push_scope () as scope :
165+ with sentry_sdk .new_scope () as scope :
166166 for key , value in context .items ():
167167 scope .set_extra (key , value )
168168 sentry_sdk .capture_exception (error )
@@ -171,11 +171,11 @@ def capture_exception(error: Exception, **context):
171171 logger .error (f"Exception (no Sentry): { type (error ).__name__ } : { error } " , ** context )
172172
173173
174- def capture_message (message : str , level : str = "info" , ** context ):
174+ def capture_message (message : str , level : str = "info" , ** context ) -> None :
175175 """Capture a message (not exception) to Sentry"""
176176 try :
177177 import sentry_sdk
178- with sentry_sdk .push_scope () as scope :
178+ with sentry_sdk .new_scope () as scope :
179179 for key , value in context .items ():
180180 scope .set_extra (key , value )
181181 sentry_sdk .capture_message (message , level = level )
@@ -321,8 +321,6 @@ def __init__(self) -> None:
321321 self ._indexing_times : deque = deque (maxlen = 100 )
322322 self ._search_times : deque = deque (maxlen = 100 )
323323 self ._total_searches : int = 0
324- self ._cache_hits : int = 0
325- self ._cache_misses : int = 0
326324
327325 def increment (self , name : str , value : int = 1 , ** tags ) -> None :
328326 """Increment a counter"""
@@ -331,10 +329,8 @@ def increment(self, name: str, value: int = 1, **tags) -> None:
331329 def timing (self , name : str , value_ms : float ) -> None :
332330 """Record a timing measurement"""
333331 if name not in self ._timings :
334- self ._timings [name ] = []
332+ self ._timings [name ] = deque ( maxlen = 1000 )
335333 self ._timings [name ].append (value_ms )
336- if len (self ._timings [name ]) > 1000 :
337- self ._timings [name ] = self ._timings [name ][- 1000 :]
338334
339335 def gauge (self , name : str , value : float ) -> None :
340336 """Record a point-in-time value"""
@@ -347,47 +343,46 @@ def record_indexing(self, repo_id: str, duration: float, function_count: int) ->
347343 "duration" : duration ,
348344 "function_count" : function_count ,
349345 "speed" : function_count / duration if duration > 0 else 0 ,
350- "timestamp" : datetime .now ().isoformat (),
346+ "timestamp" : datetime .now (timezone . utc ).isoformat (),
351347 })
352348
353349 def record_search (self , duration : float , cached : bool ) -> None :
354350 """Record search performance for dashboard metrics."""
355351 self ._search_times .append ({
356352 "duration" : duration ,
357353 "cached" : cached ,
358- "timestamp" : datetime .now ().isoformat (),
354+ "timestamp" : datetime .now (timezone . utc ).isoformat (),
359355 })
360356 self ._total_searches += 1
361- if cached :
362- self ._cache_hits += 1
363- else :
364- self ._cache_misses += 1
357+ # cache hit/miss counting handled by cache.py via metrics.increment()
358+ # to avoid double counting now that we're a single Metrics instance
365359
366360 def get_metrics (self ) -> Dict :
367361 """Dashboard-friendly performance summary (used by /health and /metrics)."""
368362 indexing_speeds = [m ["speed" ] for m in self ._indexing_times ]
369363 search_durations = [m ["duration" ] for m in self ._search_times ]
370- cache_hit_rate = (
371- (self ._cache_hits / self ._total_searches * 100 )
372- if self ._total_searches > 0 else 0
364+ avg_indexing_speed = (
365+ sum (indexing_speeds ) / len (indexing_speeds ) if indexing_speeds else 0
373366 )
367+ # Cache hits/misses come from _counters (set by cache.py)
368+ cache_hits = self ._counters .get ("cache_hits" , 0 )
369+ cache_misses = self ._counters .get ("cache_misses" , 0 )
370+ cache_total = cache_hits + cache_misses
371+ cache_hit_rate = (cache_hits / cache_total * 100 ) if cache_total > 0 else 0
374372
375373 return {
376374 "indexing" : {
377375 "total_operations" : len (self ._indexing_times ),
378- "avg_speed_functions_per_sec" : (
379- sum (indexing_speeds ) / len (indexing_speeds )
380- if indexing_speeds else 0
381- ),
376+ "avg_speed_functions_per_sec" : avg_indexing_speed ,
382377 "max_speed" : max (indexing_speeds ) if indexing_speeds else 0 ,
383378 "min_speed" : min (indexing_speeds ) if indexing_speeds else 0 ,
384379 "recent_operations" : list (self ._indexing_times )[- 10 :],
385380 },
386381 "search" : {
387382 "total_searches" : self ._total_searches ,
388383 "cache_hit_rate" : f"{ cache_hit_rate :.1f} %" ,
389- "cache_hits" : self . _cache_hits ,
390- "cache_misses" : self . _cache_misses ,
384+ "cache_hits" : cache_hits ,
385+ "cache_misses" : cache_misses ,
391386 "avg_duration_ms" : (
392387 sum (search_durations ) / len (search_durations ) * 1000
393388 if search_durations else 0
@@ -398,10 +393,7 @@ def get_metrics(self) -> Dict:
398393 "health" : "healthy" ,
399394 "cache_working" : cache_hit_rate > 0 ,
400395 "indexing_performance" : (
401- "good" if (
402- sum (indexing_speeds ) / len (indexing_speeds )
403- if indexing_speeds else 0
404- ) > 10 else "needs_improvement"
396+ "good" if avg_indexing_speed > 10 else "needs_improvement"
405397 ),
406398 },
407399 }
@@ -431,8 +423,6 @@ def reset(self) -> None:
431423 self ._indexing_times .clear ()
432424 self ._search_times .clear ()
433425 self ._total_searches = 0
434- self ._cache_hits = 0
435- self ._cache_misses = 0
436426
437427
438428# SENTRY INITIALIZATION (moved from services/sentry.py)
@@ -496,7 +486,6 @@ def _filter_events(event: Dict[str, Any], hint: Optional[Dict[str, Any]]) -> Opt
496486 if any (path in exception_value for path in bot_paths ):
497487 return None
498488
499- if exception_values :
500489 exception_type = exception_values [0 ].get ("type" , "" )
501490 if exception_type in ("RequestValidationError" , "ValidationError" ):
502491 return None
@@ -517,7 +506,7 @@ def capture_http_exception(request: Any, exc: Exception, status_code: int) -> No
517506 """Capture HTTP exception with request context for Sentry."""
518507 try :
519508 import sentry_sdk
520- with sentry_sdk .push_scope () as scope :
509+ with sentry_sdk .new_scope () as scope :
521510 scope .set_extra ("status_code" , status_code )
522511 scope .set_extra ("path" , str (request .url .path ))
523512 scope .set_extra ("method" , request .method )
0 commit comments