feat(stage16): implement observability — structured logging, metrics, tracing
- JSONFormatter, ContextVars, set_research_run_id() with short logger names - self-built Counter/Histogram/Gauge system (no external deps) - Prometheus text export at /metrics - Request logging middleware with X-Request-ID - Metrics instrumentation: search_queries, sources_fetched, claims, contradictions - Histograms: research_duration, llm_request_duration - Gauge: active_research_runs - 13 + 21 = 34 tests
This commit is contained in:
@@ -18,6 +18,16 @@ from uuid import UUID, uuid4
|
||||
from nsct.agents.planner import MockResearchPlanner, ResearchPlanner
|
||||
from nsct.config import AppSettings
|
||||
from nsct.crawler.manager import CrawlerManager
|
||||
from nsct.logging_config import set_research_run_id, get_research_run_id, get_logger
|
||||
from nsct.metrics import (
|
||||
metrics,
|
||||
C_SEARCH_QUERIES_TOTAL,
|
||||
C_SOURCES_FETCHED_TOTAL,
|
||||
C_CLAIMS_EXTRACTED_TOTAL,
|
||||
C_CONTRADICTIONS_DETECTED_TOTAL,
|
||||
H_RESEARCH_DURATION,
|
||||
H_LLM_REQUEST_DURATION,
|
||||
)
|
||||
from nsct.models.claim import Claim
|
||||
from nsct.orchestration.budget import BudgetExhaustedError, BudgetTracker, HardBudgetConfig
|
||||
from nsct.orchestration.models import ResearchRun
|
||||
@@ -312,6 +322,9 @@ class ResearchOrchestrator:
|
||||
if self._run is None:
|
||||
await self.start()
|
||||
|
||||
# Record research start timestamp for duration histogram
|
||||
_start = time.monotonic()
|
||||
|
||||
# Pipeline-Schritte nacheinander
|
||||
steps = [
|
||||
"planning",
|
||||
@@ -383,7 +396,11 @@ class ResearchOrchestrator:
|
||||
"budget_usage": self._budget_tracker.get_usage(),
|
||||
}
|
||||
|
||||
logger.info("Research pipeline completed: %d claims, state=%s", len(self._claims), report["state"])
|
||||
elapsed = time.monotonic() - _start
|
||||
metrics.observe(H_RESEARCH_DURATION, elapsed)
|
||||
|
||||
logger.info("Research pipeline completed: %d claims, state=%s, %.1fs",
|
||||
len(self._claims), report["state"], elapsed)
|
||||
return report
|
||||
|
||||
async def run_step(self, step_name: str) -> dict[str, Any]:
|
||||
@@ -428,6 +445,7 @@ class ResearchOrchestrator:
|
||||
"""PLANNING: Erstelle Recherchestrategie mit dem Planner."""
|
||||
self._transition_to("planning")
|
||||
|
||||
llm_duration_start = time.monotonic()
|
||||
try:
|
||||
planner = self._get_planner()
|
||||
if isinstance(planner, MockResearchPlanner):
|
||||
@@ -436,6 +454,9 @@ class ResearchOrchestrator:
|
||||
else:
|
||||
self._budget_tracker.increment_llm_requests(1)
|
||||
plan = await planner.plan(self._query, language="de")
|
||||
# Track LLM request duration
|
||||
elapsed = time.monotonic() - llm_duration_start
|
||||
metrics.observe(H_LLM_REQUEST_DURATION, elapsed)
|
||||
|
||||
self._plan = plan
|
||||
|
||||
@@ -514,6 +535,7 @@ class ResearchOrchestrator:
|
||||
self._search_results.append(result_dict)
|
||||
|
||||
logger.info("Search complete: %d unique URLs collected", len(self._search_results))
|
||||
metrics.increment(C_SEARCH_QUERIES_TOTAL, len(plan_queries))
|
||||
return {"success": True, "data": {"urls": self._search_results}, "url_count": len(self._search_results)}
|
||||
|
||||
except Exception as exc:
|
||||
@@ -561,6 +583,7 @@ class ResearchOrchestrator:
|
||||
self._budget_tracker.increment_download_bytes(content_len)
|
||||
|
||||
successful = [s for s in self._sources if not s.get("error")]
|
||||
metrics.increment(C_SOURCES_FETCHED_TOTAL, len(self._sources))
|
||||
logger.info("Fetching complete: %d/%d sources successfully fetched", len(successful), len(self._sources))
|
||||
return {
|
||||
"success": True,
|
||||
@@ -614,6 +637,7 @@ class ResearchOrchestrator:
|
||||
)
|
||||
|
||||
logger.info("Extraction complete: %d claims", len(self._claims))
|
||||
metrics.increment(C_CLAIMS_EXTRACTED_TOTAL, len(self._claims))
|
||||
return {"success": True, "data": {"claims": [c.model_dump() for c in self._claims]}, "claim_count": len(self._claims)}
|
||||
|
||||
except Exception as exc:
|
||||
|
||||
Reference in New Issue
Block a user