Skip to content

Commit 076a66c

Browse files
committed
fixes for UI
Signed-off-by: Shoumi <shoumimukherjee@gmail.com>
1 parent 602d8f3 commit 076a66c

File tree

6 files changed

+258
-93
lines changed

6 files changed

+258
-93
lines changed

mcpgateway/config.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -775,6 +775,7 @@ def _parse_allowed_origins(cls, v: Any) -> Set[str]:
775775

776776
# Metrics Aggregation Configuration
777777
metrics_aggregation_enabled: bool = Field(default=True, description="Enable automatic log aggregation into performance metrics")
778+
metrics_aggregation_backfill_hours: int = Field(default=6, ge=0, le=168, description="Hours of structured logs to backfill into performance metrics on startup")
778779
metrics_aggregation_window_minutes: int = Field(default=5, description="Time window for metrics aggregation (minutes)")
779780

780781
# Log Search Configuration

mcpgateway/main.py

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727

2828
# Standard
2929
import asyncio
30-
from contextlib import asynccontextmanager
30+
from contextlib import asynccontextmanager, suppress
3131
from datetime import datetime
3232
import json
3333
import os as _os # local alias to avoid collisions
@@ -114,6 +114,7 @@
114114
from mcpgateway.services.import_service import ImportError as ImportServiceError
115115
from mcpgateway.services.import_service import ImportService, ImportValidationError
116116
from mcpgateway.services.logging_service import LoggingService
117+
from mcpgateway.services.log_aggregator import get_log_aggregator
117118
from mcpgateway.services.metrics import setup_metrics
118119
from mcpgateway.services.prompt_service import PromptError, PromptNameConflictError, PromptNotFoundError, PromptService
119120
from mcpgateway.services.resource_service import ResourceError, ResourceNotFoundError, ResourceService, ResourceURIConflictError
@@ -392,6 +393,10 @@ async def lifespan(_app: FastAPI) -> AsyncIterator[None]:
392393
Exception: Any unhandled error that occurs during service
393394
initialisation or shutdown is re-raised to the caller.
394395
"""
396+
aggregation_stop_event: Optional[asyncio.Event] = None
397+
aggregation_loop_task: Optional[asyncio.Task] = None
398+
aggregation_backfill_task: Optional[asyncio.Task] = None
399+
395400
# Initialize logging service FIRST to ensure all logging goes to dual output
396401
await logging_service.initialize()
397402
logger.info("Starting MCP Gateway services")
@@ -447,6 +452,46 @@ async def lifespan(_app: FastAPI) -> AsyncIterator[None]:
447452
# Reconfigure uvicorn loggers after startup to capture access logs in dual output
448453
logging_service.configure_uvicorn_after_startup()
449454

455+
if settings.metrics_aggregation_enabled:
456+
aggregation_stop_event = asyncio.Event()
457+
log_aggregator = get_log_aggregator()
458+
459+
async def run_log_backfill() -> None:
460+
hours = getattr(settings, "metrics_aggregation_backfill_hours", 0)
461+
if hours <= 0:
462+
return
463+
try:
464+
await asyncio.to_thread(log_aggregator.backfill, hours)
465+
logger.info("Log aggregation backfill completed for last %s hour(s)", hours)
466+
except Exception as backfill_error: # pragma: no cover - defensive logging
467+
logger.warning("Log aggregation backfill failed: %s", backfill_error)
468+
469+
async def run_log_aggregation_loop() -> None:
470+
interval_seconds = max(1, int(settings.metrics_aggregation_window_minutes)) * 60
471+
logger.info(
472+
"Starting log aggregation loop (window=%s min)",
473+
log_aggregator.aggregation_window_minutes,
474+
)
475+
try:
476+
while not aggregation_stop_event.is_set():
477+
try:
478+
await asyncio.to_thread(log_aggregator.aggregate_all_components)
479+
except Exception as agg_error: # pragma: no cover - defensive logging
480+
logger.warning("Log aggregation loop iteration failed: %s", agg_error)
481+
482+
try:
483+
await asyncio.wait_for(aggregation_stop_event.wait(), timeout=interval_seconds)
484+
except asyncio.TimeoutError:
485+
continue
486+
except asyncio.CancelledError:
487+
logger.debug("Log aggregation loop cancelled")
488+
raise
489+
finally:
490+
logger.info("Log aggregation loop stopped")
491+
492+
aggregation_backfill_task = asyncio.create_task(run_log_backfill())
493+
aggregation_loop_task = asyncio.create_task(run_log_aggregation_loop())
494+
450495
yield
451496
except Exception as e:
452497
logger.error(f"Error during startup: {str(e)}")
@@ -460,6 +505,14 @@ async def lifespan(_app: FastAPI) -> AsyncIterator[None]:
460505
raise SystemExit(1)
461506
raise
462507
finally:
508+
if aggregation_stop_event is not None:
509+
aggregation_stop_event.set()
510+
for task in (aggregation_backfill_task, aggregation_loop_task):
511+
if task:
512+
task.cancel()
513+
with suppress(asyncio.CancelledError):
514+
await task
515+
463516
# Shutdown plugin manager
464517
if plugin_manager:
465518
try:

mcpgateway/routers/log_search.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
from sqlalchemy.sql import func as sa_func
2323

2424
# First-Party
25+
from mcpgateway.config import settings
2526
from mcpgateway.db import (
2627
AuditTrail,
2728
PerformanceMetric,
@@ -30,6 +31,7 @@
3031
get_db,
3132
)
3233
from mcpgateway.middleware.rbac import require_permission, get_current_user_with_permissions
34+
from mcpgateway.services.log_aggregator import get_log_aggregator
3335

3436
logger = logging.getLogger(__name__)
3537

@@ -578,6 +580,14 @@ async def get_performance_metrics(
578580
stmt = stmt.order_by(desc(PerformanceMetric.window_start))
579581

580582
metrics = db.execute(stmt).scalars().all()
583+
584+
if not metrics and settings.metrics_aggregation_enabled:
585+
try:
586+
aggregator = get_log_aggregator()
587+
aggregator.backfill(hours=hours, db=db)
588+
metrics = db.execute(stmt).scalars().all()
589+
except Exception as agg_error: # pragma: no cover - defensive logging
590+
logger.warning("On-demand metrics aggregation failed: %s", agg_error)
581591

582592
return [
583593
PerformanceMetricResponse(

0 commit comments

Comments
 (0)